Compare commits

...

2 Commits

41 changed files with 4767 additions and 6292 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -0,0 +1,340 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="AI预警处理结果"
:table-data="[]"
:table-columns="[]"
:table-height="0"
:total="0"
:current-page="1"
:page-size="10"
:z-index="2200"
@close="handleClose"
>
<!-- 标题栏下方自定义插槽 -->
<template #header>
<!-- 内容区域 -->
<div class="content-wrapper">
<!-- AI处理前预警 -->
<div class="panel">
<div class="center">
<div class="panel-title" style="color: #18EFF7">AI处理前预警</div>
</div>
<div class="panel-content">
<div class="info-container">
<div class="info-item">
<span class="info-label">发布单位&#xff1a;</span>
<span class="info-value">{{ beforeData.publishOrg }}</span>
</div>
<div class="info-item">
<span class="info-label">发布内容&#xff1a;</span>
<span class="info-value">{{ beforeData.publishContent }}</span>
</div>
<div class="info-item">
<span class="info-label">生效时间&#xff1a;</span>
<span class="info-value">{{ beforeData.effectiveTime }}</span>
</div>
<div class="info-item">
<span class="info-label">失效时间&#xff1a;</span>
<span class="info-value">{{ beforeData.expireTime }}</span>
</div>
</div>
</div>
</div>
<!-- 中间AI标识 -->
<div class="ai-center">
<img
class="ai-icon-img"
src="../../../assets/RiskWarning_img/AI1@2x.png"
alt="AI"
/>
<div class="ai-arrow">
<el-icon color="#4FECFF"><DArrowRight /></el-icon>
</div>
</div>
<!-- AI处理后预警 -->
<div class="panel after-panel panel1">
<div class="center">
<div class="panel-title" style="color: #18EFF7">AI处理后预警</div>
</div>
<div class="panel-content">
<!-- 标签页 -->
<div class="tab-header">
<div
v-for="tab in tabs"
:key="tab.key"
class="tab-item"
:class="{ active: activeTab === tab.key }"
@click="activeTab = tab.key"
>
{{ tab.label }}
</div>
</div>
<div class="info-container-tab">
<!-- 标签内容 -->
<div class="tab-content">
<div class="info-item">
<div class="info-label">预警等级;</div>
<div class="info-value">{{ afterData.warningLevel }}</div>
</div>
<div class="info-item">
<div class="info-label">预警信息;</div>
<div class="info-value">{{ afterData.warningInfo }}</div>
</div>
<div class="info-item">
<div class="info-label">预警详情;</div>
<div class="info-value">{{ afterData.warningDetail }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
</base-dialog>
</template>
<script setup>
import { ref, watch } from "vue";
import { Close, DArrowRight } from "@element-plus/icons-vue";
import baseDialog from "../component/baseDialog.vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
aiData: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(["update:visible", "close"]);
//
const tabs = ref([
{ key: "frontline", label: "一线人员" },
{ key: "district", label: "区县人员" },
{ key: "handler", label: "处室人员" },
{ key: "leader", label: "中心领导" },
]);
const activeTab = ref("frontline");
// AI
const beforeData = ref({
publishOrg: "潼南区预警中心",
publishContent:
'潼南区气象台2025年11月13日0时40分发布"暴雨蓝色预警信号"预计23日0:40-6:40龙形、宝龙、上和、大佛、桂林、玉溪、米心、花岩、双江、古溪、群力、柏梓、崇龛、梓潼、太安等15个乡镇街道强降水趋于减弱未来6小时累计雨量将达50100毫米最大小时雨强将达2040毫米局地伴有雷电、阵性大风请各地注意防范。',
effectiveTime: "2025-11-13 00:31:30.0",
expireTime: "2025-11-13 00:31:30.0",
});
// AI
const afterData = ref({
warningLevel: "重庆市潼南气象台发布大雾黄色预警[III级/较重]",
warningInfo:
'潼南区区气象台发布暴雨红色预警按照相关要求启动I级防御响应并请及时关注地质、水文等风险提示信息落实主动封闭管控/"关停撒转"措施',
warningDetail:
"请立即按照2小时一次频率对你管养的重点路段/重点部位进行巡查,重点巡查较高及以上风险路段、涉灾隐患点、地质条件复杂路段、临河临崖路段/两区三厂、大型设施设备、取弃土(渣)场、砂石料场、涉水桥梁、富水隧道、围堰、支架脚手架、高切坡、滑坡处置等部位,重点关注涉水桥梁基础及墩台、不良地质隧道、隧道洞口边仰坡及侧切结构、高陡边坡支挡防护以及防排水设施,发现异常情况,立即向上报告,采取紧急排危、告警阻拦、吹哨撒转等措施,并及时报送工作开展情况。",
});
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
// base-dialog
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal && props.aiData) {
Object.assign(beforeData.value, props.aiData.before);
Object.assign(afterData.value, props.aiData.after);
}
},
);
</script>
<style lang="scss" scoped>
//
.content-wrapper {
display: flex;
gap: 20px;
align-items: stretch;
}
//
.panel {
flex: 1;
position: relative;
.panel-title {
font-size: 16px;
font-weight: 600;
color: #40a9ff;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
padding: 12px 0;
width: 50%;
background-image: url("../../../assets/RiskWarning_img/矩形@2x.png");
background-size: 100% 100%;
background-position: center;
}
.panel-content {
background-image: url("../../../assets/RiskWarning_img/AI背景@2x.png");
background-size: 100% 100%;
background-position: center;
height: 85%;
width: 100%;
padding: 20px;
.info-container {
overflow-y: auto;
height: 100%;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
.info-container-tab {
overflow-y: auto;
height: 80%;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
.info-item {
margin-bottom: 12px;
display: flex;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
white-space: nowrap;
font-size: 13px;
color: rgba(255, 255, 255, 0.6);
margin-right: 8px;
}
.info-value {
font-size: 13px;
color: rgba(255, 255, 255, 0.9);
line-height: 1.6;
&.content-text {
display: block;
margin-top: 6px;
text-align: justify;
}
}
}
}
// AI
.ai-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
padding: 0 10px;
.ai-icon-img {
width: 60px;
height: 60px;
}
.ai-icon {
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: 700;
color: #fff;
box-shadow: 0 4px 20px rgba(64, 169, 255, 0.4);
}
.ai-arrow {
font-size: 32px;
color: #40a9ff;
animation: pulse 1.5s infinite;
:deep(.el-icon) {
font-size: 32px;
}
}
}
@keyframes pulse {
0%,
100% {
opacity: 1;
transform: translateX(0);
}
50% {
opacity: 0.6;
transform: translateX(5px);
}
}
//
.tab-header {
display: flex;
justify-content: center;
gap: 8px;
margin-bottom: 16px;
padding-bottom: 12px;
.tab-item {
padding: 6px;
font-size: 12px;
color: #fff;
background-color: #1e4f70;
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.4);
}
&.active {
// background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
background: #18f2f9;
border-color: #40a9ff;
color: #075948;
}
}
}
.tab-content {
.info-item {
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
}
}
</style>

View File

@ -0,0 +1,355 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="抢通情况"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="tableHeight"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
>
<!-- 筛选区域 -->
<template #filter>
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">影响区域</span>
<el-select v-model="filterForm.region" placeholder="请选择" class="filter-select" clearable>
<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.type" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in typeOptions"
: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.controlMeasure" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in controlMeasureOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</template>
<!-- 起止桩号列插槽 -->
<template #stakeNo="{ row }">
<el-tooltip :content="row.stakeNo" placement="top" :show-after="500">
<span class="ellipsis-text">{{ row.stakeNo }}</span>
</el-tooltip>
</template>
<!-- 路况位置列插槽 -->
<template #location="{ row }">
<el-tooltip :content="row.location" placement="top" :show-after="500">
<span class="ellipsis-text">{{ row.location }}</span>
</el-tooltip>
</template>
<!-- 管控措施列插槽 -->
<template #controlMeasure="{ row }">
<span :class="['control-tag', getControlClass(row.controlMeasure)]">{{ row.controlMeasure }}</span>
</template>
<!-- 操作列插槽 -->
<template #operation="{ row }">
<span class="detail-link" @click="handleDetail(row)">详情</span>
</template>
</base-dialog>
</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";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "detail"]);
//
const filterForm = ref({
region: "",
type: "",
controlMeasure: "",
});
//
// index.js
//
// index.js
//
// index.js
//
const tableHeight = ref(300);
//
const tableColumns = ref([
{ prop: 'id', label: '序号', width: '50' },
{ prop: 'region', label: '影响区域', width: '80' },
{ prop: 'routeNo', label: '线路编号', width: '80' },
{ prop: 'stakeNo', label: '起止桩号', width: '100', slot: 'stakeNo' },
{ prop: 'location', label: '路况位置', width: '100', slot: 'location' },
{ prop: 'occurrenceTime', label: '发生时间', width: '140' },
{ prop: 'routeNo2', label: '线路编号', width: '80' },
{ prop: 'type', label: '类型', width: '80' },
{ prop: 'controlMeasure', label: '管控措施', width: '100', slot: 'controlMeasure' },
{ prop: 'operation', label: '操作', width: '80', slot: 'operation' },
]);
//
const tableData = ref([
{
id: 1,
region: "巫溪县",
routeNo: "G242",
stakeNo: "336.800-338.850",
location: "三星乡五斗村",
occurrenceTime: "2025-08-11 04:53:42",
routeNo2: "G242",
type: "边坡坍塌",
controlMeasure: "全幅封闭",
},
{
id: 2,
region: "万州区",
routeNo: "G242",
stakeNo: "338.800-338.850",
location: "三星乡五斗村",
occurrenceTime: "2025-08-11 04:53:42",
routeNo2: "G242",
type: "边坡坍塌",
controlMeasure: "正常通行",
},
{
id: 3,
region: "沙坪坝区",
routeNo: "G319",
stakeNo: "120.500-122.000",
location: "沙坪坝镇",
occurrenceTime: "2025-08-10 14:30:00",
routeNo2: "G319",
type: "路面塌陷",
controlMeasure: "半幅封闭",
},
{
id: 4,
region: "渝中区",
routeNo: "G212",
stakeNo: "88.200-89.100",
location: "渝中大道",
occurrenceTime: "2025-08-09 09:15:30",
routeNo2: "G212",
type: "桥梁损坏",
controlMeasure: "限制通行",
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 5;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const getControlClass = (measure) => {
const classMap = {
"全幅封闭": "control-close",
"半幅封闭": "control-half",
"正常通行": "control-normal",
"限制通行": "control-limit",
};
return classMap[measure] || "";
};
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleDetail = (item) => {
emit("detail", item);
};
//
const handleSizeChange = (val) => {
pageSize.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
currentPage.value = val;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
//
.filter-section {
margin-bottom: 16px;
.filter-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.filter-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.filter-select {
width: 120px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
}
//
.ellipsis-text {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
cursor: pointer;
}
//
.control-tag {
display: inline-block;
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
&.control-close {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
&.control-half {
background-color: rgba(255, 122, 0, 0.2);
color: #ff7a00;
border: 1px solid rgba(255, 122, 0, 0.4);
}
&.control-normal {
background-color: rgba(82, 196, 26, 0.2);
color: #52c41a;
border: 1px solid rgba(82, 196, 26, 0.4);
}
&.control-limit {
background-color: rgba(250, 219, 20, 0.2);
color: #fadb14;
border: 1px solid rgba(250, 219, 20, 0.4);
}
}
//
.detail-link {
color: #40a9ff;
cursor: pointer;
font-size: 13px;
transition: color 0.3s;
&:hover {
color: #69c0ff;
text-decoration: underline;
}
}
</style>

View File

@ -1,6 +1,15 @@
<template> <template>
<div v-if="visible" class="confirm-dialog-overlay" @click="handleOverlayClick"> <div
v-if="visible"
class="confirm-dialog-overlay"
@click="handleOverlayClick"
>
<div class="confirm-dialog" @click.stop> <div class="confirm-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="dialog-header">
<div class="header-title">{{ title }}</div> <div class="header-title">{{ title }}</div>
@ -16,7 +25,7 @@
<!-- 按钮区域 --> <!-- 按钮区域 -->
<div class="dialog-footer"> <div class="dialog-footer">
<el-button class="btn-cancel" @click="handleCancel"> <el-button type="primary" class="btn-confirm" @click="handleCancel">
{{ cancelText }} {{ cancelText }}
</el-button> </el-button>
<el-button type="primary" class="btn-confirm" @click="handleConfirm"> <el-button type="primary" class="btn-confirm" @click="handleConfirm">
@ -45,11 +54,11 @@ const props = defineProps({
}, },
confirmText: { confirmText: {
type: String, type: String,
default: "确定", default: "已拨打",
}, },
cancelText: { cancelText: {
type: String, type: String,
default: "取消", default: "未拨打",
}, },
}); });
@ -89,18 +98,59 @@ const handleOverlayClick = () => {
.confirm-dialog { .confirm-dialog {
width: 80vw; width: 80vw;
max-width: 400px; max-width: 300px;
background: linear-gradient(135deg, rgba(20, 50, 90, 0.95) 0%, rgba(10, 30, 60, 0.98) 100%); 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); border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 8px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
overflow: hidden; overflow: hidden;
animation: dialogSlideIn 0.3s ease-out; animation: dialogSlideIn 0.3s ease-out;
position: relative;
@media (max-width: 768px) { @media (max-width: 768px) {
width: 90vw; width: 90vw;
max-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 { @keyframes dialogSlideIn {
@ -120,7 +170,11 @@ const handleOverlayClick = () => {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 12px 16px; padding: 12px 16px;
background: linear-gradient(90deg, rgba(64, 169, 255, 0.15) 0%, rgba(64, 169, 255, 0.05) 100%); 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); border-bottom: 1px solid rgba(64, 169, 255, 0.2);
.header-title { .header-title {

View File

@ -0,0 +1,302 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="管控情况"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="tableHeight"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:z-index="2200"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
>
<!-- 筛选区域 -->
<template #filter>
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">影响区域</span>
<el-select v-model="filterForm.region" placeholder="请选择" class="filter-select" clearable>
<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.riskLevel" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in riskLevelOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</template>
<!-- 驻地名称列插槽 -->
<template #stationName="{ row }">
<el-tooltip :content="row.stationName" placement="top" :show-after="500">
<span class="ellipsis-text">{{ row.stationName }}</span>
</el-tooltip>
</template>
<!-- 所属项目列插槽 -->
<template #project="{ row }">
<el-tooltip :content="row.project" placement="top" :show-after="500">
<span class="ellipsis-text">{{ row.project }}</span>
</el-tooltip>
</template>
<!-- 驻地风险等级列插槽 -->
<template #riskLevel="{ row }">
<span :class="['risk-tag', getRiskClass(row.riskLevel)]">{{ row.riskLevel }}</span>
</template>
</base-dialog>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import { regionOptions, riskLevelOptions } from "../component/index.js";
import BaseDialog from "../component/baseDialog.vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close"]);
//
const filterForm = ref({
region: "",
riskLevel: "",
});
//
// index.js
//
// index.js
//
const tableHeight = ref(300);
//
const tableColumns = ref([
{ prop: 'id', label: '序号', width: '60' },
{ prop: 'region', label: '影响区域', width: '100' },
{ prop: 'stationName', label: '驻地名称', width: '200', slot: 'stationName' },
{ prop: 'project', label: '所属项目', width: '200', slot: 'project' },
{ prop: 'peopleCount', label: '驻地人数', width: '80' },
{ prop: 'riskLevel', label: '驻地风险等级', width: '100', slot: 'riskLevel' },
]);
//
const tableData = ref([
{
id: 1,
region: "沙坪坝区",
stationName: "沙坪坝区S545茅山峡公路桥新建工程渝黔铁路扩能改造工程项目经理部",
project: "沙坪坝区S545茅山峡公路桥新建工程渝黔铁路扩能改造工程",
peopleCount: 21,
riskLevel: "Ⅳ级",
},
{
id: 2,
region: "万州区",
stationName: "万州区项目经理部",
project: "万州区公路改造项目",
peopleCount: 15,
riskLevel: "Ⅲ级",
},
{
id: 3,
region: "渝中区",
stationName: "渝中区桥梁维护项目部",
project: "渝中区桥梁维护工程",
peopleCount: 8,
riskLevel: "Ⅱ级",
},
{
id: 4,
region: "江北区",
stationName: "江北区道路施工项目部",
project: "江北区道路施工项目",
peopleCount: 32,
riskLevel: "Ⅰ级",
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 5;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const getRiskClass = (level) => {
const classMap = {
"Ⅰ级": "risk-level-1",
"Ⅱ级": "risk-level-2",
"Ⅲ级": "risk-level-3",
"Ⅳ级": "risk-level-4",
};
return classMap[level] || "";
};
//
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
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
//
.filter-section {
margin-bottom: 16px;
.filter-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.filter-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.filter-select {
width: 140px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
}
//
.ellipsis-text {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
cursor: pointer;
}
//
.risk-tag {
display: inline-block;
padding: 2px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
&.risk-level-1 {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
&.risk-level-2 {
background-color: rgba(255, 122, 0, 0.2);
color: #ff7a00;
border: 1px solid rgba(255, 122, 0, 0.4);
}
&.risk-level-3 {
background-color: rgba(250, 219, 20, 0.2);
color: #fadb14;
border: 1px solid rgba(250, 219, 20, 0.4);
}
&.risk-level-4 {
background-color: rgba(82, 196, 26, 0.2);
color: #52c41a;
border: 1px solid rgba(82, 196, 26, 0.4);
}
}
</style>

View File

@ -0,0 +1,234 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="调度区县情况"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="tableHeight"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:z-index="2300"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
>
<!-- 筛选区域 -->
<template #filter>
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">影响区域</span>
<el-select v-model="filterForm.region" placeholder="请选择" class="filter-select" clearable>
<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.type" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in typeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</template>
</base-dialog>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import { regionOptions, typeOptions } from "../component/index.js";
import BaseDialog from "../component/baseDialog.vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close"]);
//
const filterForm = ref({
region: "",
type: "",
});
//
// index.js
//
// index.js
//
const tableHeight = ref(300);
//
const tableColumns = ref([
{ prop: 'id', label: '序号', width: '50' },
{ prop: 'district', label: '区县/镇街', width: '100' },
{ prop: 'name', label: '姓名', width: '80' },
{ prop: 'phone', label: '电话', width: '120' },
{ prop: 'type', label: '类型', width: '100' },
{ prop: 'role', label: '角色', width: '140' },
{ prop: 'dispatchTime', label: '调度时间', width: '160' },
]);
//
const tableData = ref([
{
id: 1,
district: "柏梓镇",
name: "赵海浪",
phone: "18623520681",
type: "交通主管部门",
role: "一般人员(路长履职)",
dispatchTime: "2025-08-11 04:53:42",
},
{
id: 2,
district: "柏梓镇",
name: "赵海浪",
phone: "18623520681",
type: "公路机构",
role: "一般人员(路长履职)",
dispatchTime: "2025-08-11 04:53:42",
},
{
id: 3,
district: "万州区",
name: "王鑫",
phone: "18623520682",
type: "养护站",
role: "站长",
dispatchTime: "2025-08-10 14:30:00",
},
{
id: 4,
district: "沙坪坝区",
name: "李华",
phone: "18623520683",
type: "护路员",
role: "一般人员",
dispatchTime: "2025-08-09 09:15:30",
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 5;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
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
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
//
.filter-section {
margin-bottom: 16px;
.filter-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.filter-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.filter-select {
width: 140px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,233 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="调度区县情况"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="tableHeight"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:z-index="2200"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
>
<!-- 筛选区域 -->
<template #filter>
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">影响区域</span>
<el-select v-model="filterForm.region" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in regionOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</template>
<!-- 调度数列插槽 -->
<template #dispatchCount="{ row }">
<span class="dispatch-count" @click="handleDispatchClick(row)">{{ row.dispatchCount }}</span>
</template>
</base-dialog>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import { regionOptions } from "../component/index.js";
import BaseDialog from "../component/baseDialog.vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits({
"update:visible": (value) => typeof value === "boolean",
"close": () => true,
"dispatchClick": (item) => item !== undefined
});
//
const filterForm = ref({
region: "",
});
//
// index.js
//
const tableHeight = ref(300);
//
const tableColumns = ref([
{ prop: 'id', label: '序号', width: '60' },
{ prop: 'region', label: '影响区域', width: '120' },
{ prop: 'dispatchCount', label: '调度数', width: '100', slot: 'dispatchCount' },
{ prop: 'lastDispatchTime', label: '最近调度时间', width: '160' },
]);
//
const tableData = ref([
{
id: 1,
region: "重庆市",
dispatchCount: 1,
lastDispatchTime: "2025-08-11 04:53:42",
},
{
id: 2,
region: "万州区",
dispatchCount: 1,
lastDispatchTime: "2025-08-11 04:53:42",
},
{
id: 3,
region: "沙坪坝区",
dispatchCount: 3,
lastDispatchTime: "2025-08-10 16:20:15",
},
{
id: 4,
region: "渝中区",
dispatchCount: 2,
lastDispatchTime: "2025-08-09 11:45:30",
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 5;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleDispatchClick = (item) => {
emit("dispatchClick", item);
};
//
const handleSizeChange = (val) => {
pageSize.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
currentPage.value = val;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
//
filterForm.value.region = "";
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
//
.filter-section {
margin-bottom: 16px;
.filter-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.filter-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.filter-select {
width: 140px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
}
//
.dispatch-count {
color: #40a9ff;
cursor: pointer;
font-weight: 600;
transition: all 0.3s;
&:hover {
color: #69c0ff;
text-shadow: 0 0 8px rgba(105, 192, 255, 0.6);
}
}
</style>

View File

@ -1,14 +1,18 @@
<template> <template>
<div v-if="visible" class="event-dialog-overlay" @click="handleOverlayClick"> <base-dialog
<div class="event-dialog" @click.stop> v-model:visible="props.visible"
<!-- 标题栏 --> title="详情"
<div class="dialog-header"> :table-data="[]"
<div class="header-title">详情</div> :table-columns="[]"
<div class="close-btn" @click="handleClose"> :table-height="0"
<el-icon><Close /></el-icon> :total="0"
</div> :current-page="1"
</div> :page-size="10"
:z-index="2200"
@close="handleClose"
>
<!-- 标题栏下方自定义插槽 -->
<template #header>
<!-- 事件基本信息 --> <!-- 事件基本信息 -->
<div class="section"> <div class="section">
<div class="section-title"> <div class="section-title">
@ -116,15 +120,15 @@
</div> </div>
</div> </div>
</div> </div>
</div> </template>
</base-dialog>
<!-- 图片预览弹窗 --> <!-- 图片预览弹窗 -->
<div v-if="previewVisible" class="image-preview-overlay" @click="closePreview"> <div v-if="previewVisible" class="image-preview-overlay" @click="closePreview">
<div class="image-preview-container" @click.stop> <div class="image-preview-container" @click.stop>
<img :src="previewImageUrl" alt="预览" /> <img :src="previewImageUrl" alt="预览" />
<div class="close-preview-btn" @click="closePreview"> <div class="close-preview-btn" @click="closePreview">
<el-icon><Close /></el-icon> <el-icon><Close /></el-icon>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -133,6 +137,7 @@
<script setup> <script setup>
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { Close } from "@element-plus/icons-vue"; import { Close } from "@element-plus/icons-vue";
import baseDialog from "../component/baseDialog.vue";
const props = defineProps({ const props = defineProps({
visible: { visible: {
@ -211,10 +216,7 @@ const handleClose = () => {
emit("close"); emit("close");
}; };
// // base-dialog
const handleOverlayClick = () => {
handleClose();
};
// visible // visible
watch( watch(
@ -229,68 +231,6 @@ watch(
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.event-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: 2100;
}
.event-dialog {
width: 80vw;
max-width: 700px;
max-height: 85vh;
overflow-y: auto;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 24px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
// //
.section { .section {
margin-bottom: 24px; margin-bottom: 24px;
@ -496,7 +436,7 @@ watch(
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 1100; z-index: 2200;
} }
.image-preview-container { .image-preview-container {
@ -532,7 +472,7 @@ watch(
} }
// //
.event-dialog { :deep(.base-dialog) {
scrollbar-width: none; scrollbar-width: none;
-ms-overflow-style: none; -ms-overflow-style: none;

View File

@ -1,14 +1,19 @@
<template> <template>
<div v-if="visible" class="impact-detail-dialog-overlay" @click="handleOverlayClick"> <base-dialog
<div class="impact-detail-dialog" @click.stop> v-model:visible="props.visible"
<!-- 标题栏 --> title="影响点详情"
<div class="dialog-header"> :table-data="[]"
<div class="header-title">影响点详情</div> :table-columns="[]"
<div class="close-btn" @click="handleClose"> :table-height="0"
<el-icon><Close /></el-icon> :total="0"
</div> :current-page="1"
</div> :page-size="10"
:z-index="2500"
:max-width="650"
@close="handleClose"
>
<!-- 标题栏下方自定义插槽 -->
<template #header>
<!-- 隐患点基本信息 --> <!-- 隐患点基本信息 -->
<div class="section"> <div class="section">
<div class="section-title"> <div class="section-title">
@ -109,15 +114,15 @@
</div> </div>
</div> </div>
</div> </div>
</div> </template>
</base-dialog>
<!-- 图片预览弹窗 --> <!-- 图片预览弹窗 -->
<div v-if="previewVisible" class="image-preview-overlay" @click="closePreview"> <div v-if="previewVisible" class="image-preview-overlay" @click="closePreview">
<div class="image-preview-container" @click.stop> <div class="image-preview-container" @click.stop>
<img :src="previewImageUrl" alt="预览" /> <img :src="previewImageUrl" alt="预览" />
<div class="close-preview-btn" @click="closePreview"> <div class="close-preview-btn" @click="closePreview">
<el-icon><Close /></el-icon> <el-icon><Close /></el-icon>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -126,6 +131,7 @@
<script setup> <script setup>
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { Close } from "@element-plus/icons-vue"; import { Close } from "@element-plus/icons-vue";
import baseDialog from "../component/baseDialog.vue";
const props = defineProps({ const props = defineProps({
visible: { visible: {
@ -203,10 +209,7 @@ const handleClose = () => {
emit("close"); emit("close");
}; };
// // base-dialog
const handleOverlayClick = () => {
handleClose();
};
// visible // visible
watch( watch(
@ -220,67 +223,7 @@ watch(
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.impact-detail-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: 2500;
}
.impact-detail-dialog {
width: 80vw;
max-width: 650px;
max-height: 90vh;
overflow-y: auto;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 24px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
// //
.section { .section {
@ -556,13 +499,5 @@ watch(
} }
} }
//
.impact-detail-dialog {
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
</style> </style>

View File

@ -0,0 +1,428 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="影响点情况"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="320"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:z-index="2000"
:max-width="1200"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
>
<!-- 标题栏下方自定义插槽 -->
<template #header>
<!-- 统计卡片 -->
<div class="stats-cards">
<div class="stat-card">
<div class="stat-label">影响桥梁</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响边坡</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响隧道</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响项目</div>
<div class="stat-value">2933</div>
</div>
</div>
</template>
<!-- 筛选区域 -->
<template #filter>
<div class="filter-row">
<div class="filter-item">
<el-select v-model="filterForm.pointType" size="small" 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">
<el-select v-model="filterForm.pointLevel" size="small" 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">
<el-select v-model="filterForm.region" size="small" placeholder="影响区域" class="filter-select">
<el-option
v-for="option in regionOptionsWithAll"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div class="filter-item">
<el-button type="primary" class="search-btn" @click="handleSearch">
查询
</el-button>
</div>
</div>
</template>
<!-- 影响点等级列插槽 -->
<template #pointLevel="{ row }">
<span class="level-tag" :class="row.levelClass">{{ row.pointLevel }}</span>
</template>
<!-- 交通主管部门负责人列插槽 -->
<template #trafficDept="{ row }">
<div class="person-info">
<span class="person-name">{{ row.trafficDept.name }}</span>
<span class="person-phone">{{ row.trafficDept.phone }}</span>
</div>
</template>
<!-- 公路机构责任人列插槽 -->
<template #roadOrg="{ row }">
<div class="person-info">
<span class="person-name">{{ row.roadOrg.name }}</span>
<span class="person-phone">{{ row.roadOrg.phone }}</span>
</div>
</template>
<!-- 养护站负责人列插槽 -->
<template #maintenance="{ row }">
<div class="person-info">
<span class="person-name">{{ row.maintenance.name }}</span>
<span class="person-phone">{{ row.maintenance.phone }}</span>
</div>
</template>
<!-- 护路员列插槽 -->
<template #roadKeeper="{ row }">
<div class="person-info">
<span class="person-name">{{ row.roadKeeper.name }}</span>
<span class="person-phone">{{ row.roadKeeper.phone }}</span>
</div>
</template>
<!-- 操作列插槽 -->
<template #operation="{ row }">
<span class="detail-link" @click="handleDetail(row)">详情</span>
</template>
</base-dialog>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close, ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
import { pointTypeOptions, pointLevelOptions, regionOptionsWithAll } from "../component/index.js";
import baseDialog from "../component/baseDialog.vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "detail"]);
//
const filterForm = ref({
pointType: "",
pointLevel: "",
region: "",
});
//
const tableColumns = ref([
{ prop: "id", label: "序号", width: "50px" },
{ prop: "region", label: "影响区域", width: "80px" },
{ prop: "pointType", label: "影响点类型", width: "80px" },
{ prop: "pointLocation", label: "影响点位置", width: "180px" },
{ prop: "pointLevel", label: "影响点等级", width: "90px", slot: "pointLevel" },
{ prop: "trafficDept", label: "交通主管部门负责人", width: "130px", slot: "trafficDept" },
{ prop: "roadOrg", label: "公路机构责任人", width: "110px", slot: "roadOrg" },
{ prop: "maintenance", label: "养护站负责人", width: "110px", slot: "maintenance" },
{ prop: "roadKeeper", label: "护路员", width: "100px", slot: "roadKeeper" },
{ prop: "operation", label: "操作", width: "60px", slot: "operation" },
]);
//
const tableData = ref([
{
id: 1,
region: "重庆市",
pointType: "边坡",
pointLocation: "武汉-大理(K1452+951至K1462+209)",
pointLevel: "一般隐患",
levelClass: "level-normal",
trafficDept: { name: "罗宸", phone: "17623865172" },
roadOrg: { name: "李海平", phone: "13708320801" },
maintenance: { name: "苏祖兵", phone: "13594331090" },
roadKeeper: { name: "凌承礼", phone: "1592393704" },
},
{
id: 2,
region: "重庆市",
pointType: "边坡",
pointLocation: "武汉-大理(K1452+951至K1462+209)",
pointLevel: "一般隐患",
levelClass: "level-normal",
trafficDept: { name: "罗宸", phone: "17623865172" },
roadOrg: { name: "李海平", phone: "13708320801" },
maintenance: { name: "苏祖兵", phone: "13594331090" },
roadKeeper: { name: "凌承礼", phone: "1592393704" },
},
{
id: 3,
region: "重庆市",
pointType: "边坡",
pointLocation: "武汉-大理(K1452+951至K1462+209)",
pointLevel: "一般隐患",
levelClass: "level-normal",
trafficDept: { name: "罗宸", phone: "17623865172" },
roadOrg: { name: "李海平", phone: "13708320801" },
maintenance: { name: "苏祖兵", phone: "13594331090" },
roadKeeper: { name: "凌承礼", phone: "1592393704" },
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 4;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
// base-dialog
//
const handleSearch = () => {
console.log("查询条件:", filterForm.value);
currentPage.value = 1;
fetchData();
};
//
const handleDetail = (item) => {
emit("detail", item);
};
//
const handleSizeChange = (val) => {
pageSize.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
currentPage.value = val;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
// -
@function vw($px) {
@return calc($px / 1920 * 100vw);
}
//
.stats-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: vw(16);
.stat-card {
background: linear-gradient(135deg, rgba(30, 70, 120, 0.6) 0%, rgba(20, 50, 90, 0.8) 100%);
border: vw(2) solid rgba(64, 169, 255, 0.4);
text-align: center;
transition: all 0.3s;
cursor: pointer;
.stat-label {
font-size: vw(14);
color: rgba(255, 255, 255, 0.8);
font-weight: 500;
}
.stat-value {
font-size: vw(28);
font-weight: bold;
color: #40a9ff;
text-shadow: 0 0 10px rgba(64, 169, 255, 0.5);
}
}
}
//
.filter-section {
margin-bottom: vw(20);
.filter-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.filter-item {
.filter-select {
width: vw(150);
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: vw(4);
.el-input__inner {
color: #fff;
font-size: vw(13);
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
.search-btn {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
border: none;
border-radius: vw(4);
padding: 0 vw(24);
height: vw(32);
font-size: vw(13);
&:hover {
background: linear-gradient(135deg, #69c0ff 0%, #40a9ff 100%);
}
}
}
}
//
.level-tag {
display: inline-block;
padding: vw(2) vw(8);
border-radius: vw(4);
font-size: vw(11);
&.level-normal {
background-color: rgba(250, 219, 95, 0.2);
color: #fadb5f;
border: 1px solid rgba(250, 219, 95, 0.4);
}
&.level-serious {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
}
.person-info {
display: flex;
flex-direction: column;
gap: vw(2);
.person-name {
font-size: vw(12);
color: rgba(255, 255, 255, 0.9);
}
.person-phone {
font-size: vw(11);
color: rgba(255, 255, 255, 0.6);
}
}
.detail-link {
color: #40a9ff;
cursor: pointer;
font-size: vw(12);
&:hover {
color: #69c0ff;
text-decoration: underline;
}
}
//
:deep(.el-select-dropdown) {
background-color: rgba(20, 50, 90, 0.98);
border: 1px solid rgba(64, 169, 255, 0.3);
.el-select-dropdown__item {
color: rgba(255, 255, 255, 0.85);
&:hover {
background-color: rgba(64, 169, 255, 0.2);
}
&.selected {
background-color: rgba(64, 169, 255, 0.3);
color: #40a9ff;
}
}
}
</style>

View File

@ -1,14 +1,19 @@
<template> <template>
<div v-if="visible" class="response-dialog-overlay" @click="handleOverlayClick"> <base-dialog
<div class="response-dialog" @click.stop> v-model:visible="props.visible"
<!-- 标题栏 --> title="响应点详情"
<div class="dialog-header"> :table-data="[]"
<div class="header-title">响应点详情</div> :table-columns="[]"
<div class="close-btn" @click="handleClose"> :table-height="0"
<el-icon><Close /></el-icon> :total="0"
</div> :current-page="1"
</div> :page-size="10"
:z-index="1000"
:max-width="700"
@close="handleClose"
>
<!-- 标题栏下方自定义插槽 -->
<template #header>
<!-- 隐患点基本信息 --> <!-- 隐患点基本信息 -->
<div class="section"> <div class="section">
<div class="section-title"> <div class="section-title">
@ -114,15 +119,15 @@
</div> </div>
</div> </div>
</div> </div>
</div> </template>
</base-dialog>
<!-- 图片预览弹窗 --> <!-- 图片预览弹窗 -->
<div v-if="previewVisible" class="image-preview-overlay" @click="closePreview"> <div v-if="previewVisible" class="image-preview-overlay" @click="closePreview">
<div class="image-preview-container" @click.stop> <div class="image-preview-container" @click.stop>
<img :src="previewImageUrl" alt="预览" /> <img :src="previewImageUrl" alt="预览" />
<div class="close-preview-btn" @click="closePreview"> <div class="close-preview-btn" @click="closePreview">
<el-icon><Close /></el-icon> <el-icon><Close /></el-icon>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -131,6 +136,7 @@
<script setup> <script setup>
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { Close } from "@element-plus/icons-vue"; import { Close } from "@element-plus/icons-vue";
import baseDialog from "../component/baseDialog.vue";
const props = defineProps({ const props = defineProps({
visible: { visible: {
@ -240,10 +246,7 @@ const handleClose = () => {
emit("close"); emit("close");
}; };
// // base-dialog
const handleOverlayClick = () => {
handleClose();
};
// visible // visible
watch( watch(
@ -257,67 +260,7 @@ watch(
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.response-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: 1000;
}
.response-dialog {
width: 80vw;
max-width: 700px;
max-height: 90vh;
overflow-y: auto;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 24px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
// //
.section { .section {
@ -657,22 +600,5 @@ watch(
} }
} }
//
.response-dialog::-webkit-scrollbar {
width: 6px;
}
.response-dialog::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.response-dialog::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 3px;
}
.response-dialog::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
</style> </style>

View File

@ -1,14 +1,19 @@
<template> <template>
<div v-if="visible" class="response-info-dialog-overlay" @click="handleOverlayClick"> <base-dialog
<div class="response-info-dialog" @click.stop> v-model:visible="props.visible"
<!-- 标题栏 --> title="响应点详情"
<div class="dialog-header"> :table-data="[]"
<div class="header-title">响应点详情</div> :table-columns="[]"
<div class="close-btn" @click="handleClose"> :table-height="0"
<el-icon><Close /></el-icon> :total="0"
</div> :current-page="1"
</div> :page-size="10"
:z-index="1000"
:max-width="750"
@close="handleClose"
>
<!-- 标题栏下方自定义插槽 -->
<template #header>
<!-- 基本信息 --> <!-- 基本信息 -->
<div class="section"> <div class="section">
<div class="section-title"> <div class="section-title">
@ -137,15 +142,15 @@
</div> </div>
</div> </div>
</div> </div>
</div> </template>
</base-dialog>
<!-- 图片预览弹窗 --> <!-- 图片预览弹窗 -->
<div v-if="previewVisible" class="image-preview-overlay" @click="closePreview"> <div v-if="previewVisible" class="image-preview-overlay" @click="closePreview">
<div class="image-preview-container" @click.stop> <div class="image-preview-container" @click.stop>
<img :src="previewImageUrl" alt="预览" /> <img :src="previewImageUrl" alt="预览" />
<div class="close-preview-btn" @click="closePreview"> <div class="close-preview-btn" @click="closePreview">
<el-icon><Close /></el-icon> <el-icon><Close /></el-icon>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -154,6 +159,7 @@
<script setup> <script setup>
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { Close } from "@element-plus/icons-vue"; import { Close } from "@element-plus/icons-vue";
import baseDialog from "../component/baseDialog.vue";
const props = defineProps({ const props = defineProps({
visible: { visible: {
@ -274,10 +280,7 @@ const handleClose = () => {
emit("close"); emit("close");
}; };
// // base-dialog
const handleOverlayClick = () => {
handleClose();
};
// visible // visible
watch( watch(
@ -291,67 +294,7 @@ watch(
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.response-info-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: 1000;
}
.response-info-dialog {
width: 80vw;
max-width: 750px;
max-height: 90vh;
overflow-y: auto;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 24px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
// //
.section { .section {
@ -694,22 +637,5 @@ watch(
} }
} }
//
.response-info-dialog::-webkit-scrollbar {
width: 6px;
}
.response-info-dialog::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.response-info-dialog::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 3px;
}
.response-info-dialog::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
</style> </style>

View File

@ -0,0 +1,264 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="潼南三级路长明细"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="300"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:z-index="1000"
:max-width="700"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
>
<!-- 标题栏下方自定义插槽 -->
<template #header>
<!-- 统计数据卡片 -->
<div class="stats-cards">
<div class="stat-card">
<div class="card-icon">
<el-icon class="stat-icon"><User /></el-icon>
</div>
<div class="card-info">
<div class="card-label">路长总人数</div>
<div class="card-value">
<span class="value-num">{{ stats.total }}</span>
<span class="value-unit"></span>
</div>
</div>
</div>
<div class="stat-card">
<div class="card-icon">
<el-icon class="stat-icon"><OfficeBuilding /></el-icon>
</div>
<div class="card-info">
<div class="card-label">县级路长</div>
<div class="card-value">
<span class="value-num">{{ stats.county }}</span>
<span class="value-unit">公里</span>
</div>
</div>
</div>
<div class="stat-card">
<div class="card-icon">
<el-icon class="stat-icon"><MapLocation /></el-icon>
</div>
<div class="card-info">
<div class="card-label">村路长</div>
<div class="card-value">
<span class="value-num">{{ stats.village }}</span>
<span class="value-unit"></span>
</div>
</div>
</div>
</div>
</template>
<!-- 操作列插槽 -->
<template #operation="{ row }">
<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>
</template>
</base-dialog>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close, VideoCamera, Microphone, Phone, ArrowLeft, ArrowRight, User, OfficeBuilding, MapLocation } from "@element-plus/icons-vue";
import baseDialog from "../component/baseDialog.vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close"]);
//
const stats = ref({
total: 1127,
county: 216,
village: 1099,
});
//
const tableColumns = ref([
{ prop: "id", label: "序号", width: "60px" },
{ prop: "district", label: "区县/镇街", width: "120px" },
{ prop: "name", label: "姓名", width: "100px" },
{ prop: "phone", label: "电话", width: "120px" },
{ prop: "role", label: "角色", width: "200px" },
{ prop: "position", label: "职务", width: "100px" },
{ prop: "operation", label: "操作", width: "120px", slot: "operation" },
]);
//
const tableData = ref([
{ id: 1, district: "万州区柏梓镇", name: "赵海浪", phone: "1862352068", role: "一般人员(路长履职)", position: "其他" },
{ id: 2, district: "万州区柏梓镇", name: "赵海浪", phone: "1862352068", role: "一般人员(路长履职)", position: "其他" },
{ id: 3, district: "万州区柏梓镇", name: "赵海浪", phone: "1862352068", role: "一般人员(路长履职)", position: "其他" },
{ id: 4, district: "万州区柏梓镇", name: "赵海浪", phone: "1862352068", role: "一般人员(路长履职)", position: "其他" },
{ id: 5, district: "万州区李河镇", name: "王建国", phone: "1398324567", role: "一般人员(路长履职)", position: "其他" },
{ id: 6, district: "万州区李河镇", name: "王建国", phone: "1398324567", role: "一般人员(路长履职)", position: "其他" },
{ id: 7, district: "万州区李河镇", name: "王建国", phone: "1398324567", role: "一般人员(路长履职)", position: "其他" },
{ id: 8, district: "万州区李河镇", name: "王建国", phone: "1398324567", role: "一般人员(路长履职)", position: "其他" },
{ id: 9, district: "万州区分水镇", name: "刘志强", phone: "1387654321", role: "一般人员(路长履职)", position: "其他" },
{ id: 10, district: "万州区分水镇", name: "刘志强", phone: "1387654321", role: "一般人员(路长履职)", position: "其他" },
{ id: 11, district: "万州区分水镇", name: "刘志强", phone: "1387654321", role: "一般人员(路长履职)", position: "其他" },
{ id: 12, district: "万州区分水镇", name: "刘志强", phone: "1387654321", role: "一般人员(路长履职)", position: "其他" },
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
//
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>
//
.stats-cards {
display: flex;
gap: 12px;
margin-bottom: 12px;
.stat-card {
flex: 1;
display: flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, rgba(30, 80, 140, 0.6) 0%, rgba(20, 60, 110, 0.8) 100%);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 4px;
padding: 10px 12px;
.card-icon {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.15);
border-radius: 4px;
.stat-icon {
font-size: 18px;
color: #40a9ff;
}
}
.card-info {
.card-label {
font-size: 11px;
color: rgba(255, 255, 255, 0.7);
margin-bottom: 1px;
}
.card-value {
display: flex;
align-items: baseline;
gap: 2px;
.value-num {
font-size: 18px;
font-weight: 700;
color: #40a9ff;
text-shadow: 0 0 6px rgba(64, 169, 255, 0.5);
}
.value-unit {
font-size: 10px;
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
//
.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

@ -0,0 +1,448 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="响应情况"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="200"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:z-index="1000"
:max-width="1150"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
>
<!-- 标题栏下方自定义插槽 -->
<template #header>
<!-- 统计卡片 -->
<div class="stats-cards">
<div class="stat-card">
<div class="stat-label">影响桥梁</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响边坡</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响隧道</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响项目</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响路段</div>
<div class="stat-value">2432</div>
</div>
</div>
</template>
<!-- 筛选区域 -->
<template #filter>
<div class="filter-row">
<div class="filter-item">
<el-select
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">
<el-select
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">
<el-select
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>
</template>
<!-- 影响点等级列插槽 -->
<template #pointLevel="{ row }">
<span class="level-tag" :class="row.levelClass">{{ row.pointLevel }}</span>
</template>
<!-- 交通主管部门负责人列插槽 -->
<template #trafficDept="{ row }">
<div class="person-info">
<span class="person-name">{{ row.trafficDept.name }}</span>
<span class="person-phone">{{ row.trafficDept.phone }}</span>
</div>
</template>
<!-- 公路机构责任人列插槽 -->
<template #roadOrg="{ row }">
<div class="person-info">
<span class="person-name">{{ row.roadOrg.name }}</span>
<span class="person-phone">{{ row.roadOrg.phone }}</span>
</div>
</template>
<!-- 养护站负责人列插槽 -->
<template #maintenance="{ row }">
<div class="person-info">
<span class="person-name">{{ row.maintenance.name }}</span>
<span class="person-phone">{{ row.maintenance.phone }}</span>
</div>
</template>
<!-- 护路员列插槽 -->
<template #roadKeeper="{ row }">
<div class="person-info">
<span class="person-name">{{ row.roadKeeper.name }}</span>
<span class="person-phone">{{ row.roadKeeper.phone }}</span>
</div>
</template>
<!-- 回应状态列插槽 -->
<template #responseStatus="{ row }">
<span class="response-status" :class="row.responseClass">{{ row.responseStatus }}</span>
</template>
<!-- 最新催告时间列插槽 -->
<template #urgeTime="{ row }">
<div class="time-info">
<span class="time-date">{{ row.urgeTime.date }}</span>
<span class="time-clock">{{ row.urgeTime.time }}</span>
</div>
</template>
<!-- 操作列插槽 -->
<template #operation="{ row }">
<span class="detail-link" @click="handleDetail(row)">详情</span>
</template>
</base-dialog>
</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";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "detail"]);
//
const filterForm = ref({
pointType: "",
pointLevel: "",
isResponded: "",
});
//
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" },
]);
//
const tableData = ref([
{
id: 1,
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" },
},
{
id: 12,
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" },
},
]);
tableData.value.push(...tableData.value);
tableData.value.push(...tableData.value);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
// base-dialog
//
const handleDetail = (item) => {
emit("detail", item);
};
//
const handleSizeChange = (val) => {
pageSize.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
currentPage.value = val;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
},
);
</script>
<style lang="scss" scoped>
//
.filter-section {
margin-bottom: 20px;
.filter-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.filter-item {
.filter-select {
width: 150px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
}
//
.level-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
&.level-normal {
background-color: rgba(250, 219, 95, 0.2);
color: #fadb5f;
border: 1px solid rgba(250, 219, 95, 0.4);
}
&.level-serious {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
}
.person-info {
display: flex;
flex-direction: column;
gap: 2px;
.person-name {
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
}
.person-phone {
font-size: 11px;
color: rgba(255, 255, 255, 0.6);
}
}
.response-status {
font-size: 12px;
&.status-responded {
color: #52c41a;
}
&.status-unresponded {
color: #ff7a45;
}
}
.time-info {
display: flex;
flex-direction: column;
gap: 2px;
.time-date {
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
}
.time-clock {
font-size: 11px;
color: rgba(255, 255, 255, 0.6);
}
}
.detail-link {
color: #40a9ff;
cursor: pointer;
font-size: 12px;
&:hover {
color: #69c0ff;
text-decoration: underline;
}
}
//
:deep(.el-select-dropdown) {
background-color: rgba(20, 50, 90, 0.98);
border: 1px solid rgba(64, 169, 255, 0.3);
.el-select-dropdown__item {
color: rgba(255, 255, 255, 0.85);
&:hover {
background-color: rgba(64, 169, 255, 0.2);
}
&.selected {
background-color: rgba(64, 169, 255, 0.3);
color: #40a9ff;
}
}
}
//
.stats-cards {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 20px;
margin-bottom: 20px;
.stat-card {
background: linear-gradient(
135deg,
rgba(30, 70, 120, 0.6) 0%,
rgba(20, 50, 90, 0.8) 100%
);
border: 2px solid rgba(64, 169, 255, 0.4);
border-radius: 8px;
padding: 8px 20px;
text-align: center;
transition: all 0.3s;
cursor: pointer;
&:hover {
border-color: rgba(64, 169, 255, 0.8);
box-shadow: 0 0 20px rgba(64, 169, 255, 0.3);
transform: translateY(-2px);
}
.stat-label {
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 8px;
font-weight: 500;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #40a9ff;
text-shadow: 0 0 10px rgba(64, 169, 255, 0.5);
}
}
}
</style>

View File

@ -1,14 +1,19 @@
<template> <template>
<div v-if="visible" class="risk-dialog-overlay" @click="handleOverlayClick"> <base-dialog
<div class="risk-dialog" @click.stop> v-model:visible="props.visible"
<!-- 标题栏 --> title="影响点详情"
<div class="dialog-header"> :table-data="[]"
<div class="header-title">影响点详情</div> :table-columns="[]"
<div class="close-btn" @click="handleClose"> :table-height="0"
<el-icon><Close /></el-icon> :total="0"
</div> :current-page="1"
</div> :page-size="10"
:z-index="1000"
:max-width="800"
@close="handleClose"
>
<!-- 标题栏下方自定义插槽 -->
<template #header>
<!-- 基本信息 --> <!-- 基本信息 -->
<div class="section"> <div class="section">
<div class="section-title"> <div class="section-title">
@ -166,15 +171,15 @@
</div> </div>
</div> </div>
</div> </div>
</div> </template>
</base-dialog>
<!-- 图片预览弹窗 --> <!-- 图片预览弹窗 -->
<div v-if="previewVisible" class="image-preview-overlay" @click="closePreview"> <div v-if="previewVisible" class="image-preview-overlay" @click="closePreview">
<div class="image-preview-container" @click.stop> <div class="image-preview-container" @click.stop>
<img :src="previewImageUrl" alt="预览" /> <img :src="previewImageUrl" alt="预览" />
<div class="close-preview-btn" @click="closePreview"> <div class="close-preview-btn" @click="closePreview">
<el-icon><Close /></el-icon> <el-icon><Close /></el-icon>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -183,6 +188,7 @@
<script setup> <script setup>
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { Close } from "@element-plus/icons-vue"; import { Close } from "@element-plus/icons-vue";
import baseDialog from "../component/baseDialog.vue";
const props = defineProps({ const props = defineProps({
visible: { visible: {
@ -319,10 +325,7 @@ const handleClose = () => {
emit("close"); emit("close");
}; };
// // base-dialog
const handleOverlayClick = () => {
handleClose();
};
// visible // visible
watch( watch(
@ -336,67 +339,7 @@ watch(
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.risk-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: 1000;
}
.risk-dialog {
width: 80vw;
max-width: 800px;
max-height: 90vh;
overflow-y: auto;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 24px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
// //
.section { .section {
@ -776,22 +719,5 @@ watch(
} }
} }
//
.risk-dialog::-webkit-scrollbar {
width: 6px;
}
.risk-dialog::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.risk-dialog::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 3px;
}
.risk-dialog::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
</style> </style>

View File

@ -0,0 +1,614 @@
<template>
<div
v-if="visible"
class="warning-dialog-overlay"
@click="handleOverlayClick"
>
<div class="warning-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">响应情况</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 筛选区域 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<el-select
v-model="filterForm.warningLevel"
placeholder="预警等级"
class="filter-select"
>
<el-option
v-for="option in warningLevelOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div class="filter-item">
<el-select
v-model="filterForm.region"
placeholder="影响区域"
class="filter-select"
>
<el-option
v-for="option in regionOptionsWithAll"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div class="filter-item">
<el-select
v-model="filterForm.isEnded"
placeholder="是否结束"
class="filter-select"
>
<el-option
v-for="option in isEndedOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div class="filter-item">
<el-select
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" class="search-btn" @click="handleSearch">
<el-icon><Search /></el-icon>
查询
</el-button>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<el-table
:data="tableData"
:height="tableHeight"
style="width: 100%"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
size="small"
>
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="weatherSource" label="气象来源" width="80" />
<el-table-column prop="warningLevel" label="预警等级" width="100">
<template #default="{ row }">
<span class="warning-level" :class="row.levelClass">{{ row.warningLevel }}</span>
</template>
</el-table-column>
<el-table-column prop="region" label="影响区域" width="100" />
<el-table-column prop="warningTime" label="预警时间" width="160" />
<el-table-column prop="endTime" label="结束时间" width="160" />
<el-table-column prop="impactPoints" label="影响点数量" width="100" />
<el-table-column prop="called" label="已叫应" width="80">
<template #default="{ row }">
<span class="clickable-cell" @click="handleCalledClick(row)">{{ row.called }}</span>
</template>
</el-table-column>
<el-table-column prop="responded" label="已回应" width="80" />
<el-table-column prop="notResponded" label="未回应" width="80" />
<el-table-column prop="urged" label="已催告" width="80" />
</el-table>
</div>
<!-- 分页 -->
<div class="pagination">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
:total="total"
background
layout="prev, pager, next, jumper, ->, total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close, Search, ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
import {
warningLevelOptions,
regionOptionsWithAll,
isEndedOptions,
isRespondedOptions,
} from "../component/index.js";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "openImpactPoint"]);
//
const filterForm = ref({
warningLevel: "",
region: "",
isEnded: "",
isResponded: "",
});
//
const tableColumns = ref([
{ label: "序号", width: "60px" },
{ label: "气象来源", width: "80px" },
{ label: "预警等级", width: "100px" },
{ label: "影响区域", width: "100px" },
{ label: "预警时间", width: "160px" },
{ label: "结束时间", width: "160px" },
{ label: "影响点数量", width: "100px" },
{ label: "已叫应", width: "80px" },
{ label: "已回应", width: "80px" },
{ label: "未回应", width: "80px" },
{ label: "已催告", width: "80px" },
]);
//
const tableData = ref([
{
id: 1,
weatherSource: "气象局",
warningLevel: "红色预警",
levelClass: "level-red",
region: "万州区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactPoints: 4,
called: 2,
responded: 2,
notResponded: 2,
urged: 22,
},
{
id: 2,
weatherSource: "气象局",
warningLevel: "橙色预警",
levelClass: "level-orange",
region: "涪陵区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactPoints: 0,
called: 0,
responded: 0,
notResponded: 0,
urged: 18,
},
]);
tableData.value.push(...tableData.value);
tableData.value.push(...tableData.value);
tableData.value.push(...tableData.value);
//
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 handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
//
const handleCalledClick = () => {
emit("responseStatus");
};
//
const handleSearch = () => {
console.log("查询条件:", filterForm.value);
currentPage.value = 1;
fetchData();
};
//
const handleSizeChange = (val) => {
pageSize.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
currentPage.value = val;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
},
);
</script>
<style lang="scss" scoped>
.warning-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: 1000;
}
.warning-dialog {
width: 80vw;
max-width: 1000px;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(
90deg,
transparent 0%,
rgba(64, 169, 255, 0.2) 20%,
rgba(64, 169, 255, 0.2) 80%,
transparent 100%
);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.filter-section {
margin-bottom: 20px;
.filter-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.filter-item {
.filter-select {
width: 120px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
.search-btn {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
border: none;
border-radius: 4px;
padding: 0 20px;
height: 32px;
font-size: 13px;
display: flex;
align-items: center;
gap: 6px;
&:hover {
background: linear-gradient(135deg, #69c0ff 0%, #40a9ff 100%);
}
.el-icon {
font-size: 14px;
}
}
}
}
//
.table-section {
background-color: rgba(30, 70, 120, 0.3);
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
:deep(.el-table) {
background-color: transparent;
border: none;
.el-table__header-wrapper {
background-color: transparent;
}
.el-table__body-wrapper {
background-color: transparent;
}
.el-table__row {
background-color: transparent;
transition: background-color 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&:nth-child(even) {
background-color: rgba(30, 70, 120, 0.2);
}
}
.el-table__cell {
color: rgba(255, 255, 255, 0.85);
font-size: 13px;
text-align: center;
border: none;
padding: 12px 16px;
}
.el-table__header .el-table__cell {
background-color: rgba(64, 169, 255, 0.2);
color: #fff;
font-size: 14px;
font-weight: 500;
text-align: center;
border: none;
padding: 12px 16px;
}
}
.warning-level {
display: inline-block;
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
&.level-red {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
&.level-orange {
background-color: rgba(255, 122, 69, 0.2);
color: #ff7a45;
border: 1px solid rgba(255, 122, 69, 0.4);
}
&.level-yellow {
background-color: rgba(250, 219, 95, 0.2);
color: #fadb5f;
border: 1px solid rgba(250, 219, 95, 0.4);
}
&.level-blue {
background-color: rgba(64, 169, 255, 0.2);
color: #40a9ff;
border: 1px solid rgba(64, 169, 255, 0.4);
}
}
.clickable-cell {
cursor: pointer;
color: #40a9ff;
font-weight: 500;
transition: all 0.3s;
&:hover {
color: #69c0ff;
text-shadow: 0 0 8px rgba(105, 192, 255, 0.6);
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 16px;
:deep(.el-pagination) {
.el-pagination__total {
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
}
.el-pagination__sizes .el-input__inner {
color: #fff;
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
font-size: 13px;
}
.el-pagination__btn {
color: rgba(255, 255, 255, 0.8);
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 4px;
transition: all 0.3s;
&:hover:not(.is-disabled) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.4);
}
&.is-disabled {
opacity: 0.4;
}
}
.el-pagination__item {
color: rgba(255, 255, 255, 0.8);
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 4px;
transition: all 0.3s;
&:hover:not(.is-disabled) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.4);
}
&.is-active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
}
.el-pagination__jump .el-input__inner {
color: #fff;
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
font-size: 13px;
}
}
}
//
:deep(.el-table__body-wrapper::-webkit-scrollbar) {
display: none;
}
:deep(.el-table__body-wrapper) {
-ms-overflow-style: none;
scrollbar-width: none;
}
//
:deep(.el-select-dropdown) {
background-color: rgba(20, 50, 90, 0.98);
border: 1px solid rgba(64, 169, 255, 0.3);
.el-select-dropdown__item {
color: rgba(255, 255, 255, 0.85);
&:hover {
background-color: rgba(64, 169, 255, 0.2);
}
&.selected {
background-color: rgba(64, 169, 255, 0.3);
color: #40a9ff;
}
}
}
</style>

View File

@ -0,0 +1,577 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="预警情况"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="tableHeight"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
>
<!-- 筛选区域 -->
<template #filter>
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">预警等级</span>
<el-select
v-model="filterForm.warningLevel"
placeholder="请选择"
class="filter-select"
clearable
size="small"
>
<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.region"
placeholder="请选择"
class="filter-select"
clearable
size="small"
>
<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"
>
<el-option
v-for="item in isEndedOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</template>
<!-- 预警等级列插槽 -->
<template #warningLevel="{ row }">
<span
:class="[
'warning-level-tag',
getWarningClass(row.warningLevel),
]"
>{{ row.warningLevel }}</span
>
</template>
<!-- 影响点数量列插槽 -->
<template #impactCount="{ row }">
<span class="impact-count" @click="handleImpactClick(row)">{{
row.impactCount
}}</span>
</template>
</base-dialog>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import {
warningLevelOptions,
regionOptions,
isEndedOptions,
} from "../component/index.js";
import baseDialog from "../component/baseDialog.vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "impactClick"]);
//
const filterForm = ref({
warningLevel: "",
region: "",
isEnded: "",
dateRange: [],
});
//
// index.js
//
// index.js
//
// index.js
//
const tableHeight = ref(300);
//
const tableColumns = ref([
{ prop: 'index', label: '序号', width: '60' },
{ prop: 'warningLevel', label: '预警等级', width: '100', slot: 'warningLevel' },
{ prop: 'weatherType', label: '气象类型', width: '100' },
{ prop: 'region', label: '行政区域', width: '100' },
{ prop: 'warningTime', label: '预警时间', width: '160' },
{ prop: 'endTime', label: '结束时间', width: '160' },
{ prop: 'impactCount', label: '影响点数量', width: '100', slot: 'impactCount' },
]);
//
const tableData = ref([
{
id: 1,
index: 1,
warningLevel: "红色预警",
weatherType: "暴雨",
region: "重庆市",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactCount: 0,
},
{
id: 2,
index: 2,
warningLevel: "橙色预警",
weatherType: "暴雨",
region: "万州区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactCount: 1,
},
{
id: 3,
index: 3,
warningLevel: "黄色预警",
weatherType: "大风",
region: "沙坪坝区",
warningTime: "2025-08-10 16:20:15",
endTime: "2025-08-10 20:30:00",
impactCount: 3,
},
{
id: 4,
index: 4,
warningLevel: "蓝色预警",
weatherType: "雷电",
region: "渝中区",
warningTime: "2025-08-09 09:15:30",
endTime: "2025-08-09 12:00:00",
impactCount: 2,
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
//
const getWarningClass = (level) => {
const classMap = {
红色预警: "warning-red",
橙色预警: "warning-orange",
黄色预警: "warning-yellow",
蓝色预警: "warning-blue",
};
return classMap[level] || "";
};
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleImpactClick = (item) => {
emit("impactClick", item);
};
//
const handleSizeChange = (val) => {
pageSize.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
currentPage.value = val;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
},
);
</script>
<style lang="scss" scoped>
//
.filter-section {
margin-bottom: 16px;
padding: 0 24px;
.filter-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.filter-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.filter-select {
width: 120px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
width: 210px !important;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
.date-range-picker {
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
width: 210px !important;
.el-input__inner {
color: #fff;
font-size: 13px;
background: transparent;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
:deep(.el-range-input) {
background: transparent;
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
:deep(.el-range-separator) {
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
}
}
}
}
//
.table-section {
width: 100%;
margin-bottom: 20px;
padding: 0 24px;
:deep(.el-table) {
background-color: transparent;
border: none;
.el-table__header-wrapper {
background-color: transparent;
}
.el-table__body-wrapper {
background-color: transparent;
}
.el-table__row {
width: 100% !important;
background-color: transparent;
transition: background-color 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&:nth-child(even) {
background-color: rgba(30, 70, 120, 0.2);
}
}
.el-table__cell {
color: rgba(255, 255, 255, 0.85);
font-size: 13px;
text-align: center;
border: none;
padding: 12px 16px;
}
.el-table__header {
width: 100% !important;
}
.el-table__header .el-table__cell {
background-color: #1c4979;
color: #fff;
font-size: 14px;
font-weight: 500;
text-align: center;
border: none;
padding: 12px 16px;
}
}
//
.warning-level-tag {
display: inline-block;
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
&.warning-red {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
&.warning-orange {
background-color: rgba(255, 122, 0, 0.2);
color: #ff7a00;
border: 1px solid rgba(255, 122, 0, 0.4);
}
&.warning-yellow {
background-color: rgba(250, 219, 20, 0.2);
color: #fadb14;
border: 1px solid rgba(250, 219, 20, 0.4);
}
&.warning-blue {
background-color: rgba(64, 169, 255, 0.2);
color: #40a9ff;
border: 1px solid rgba(64, 169, 255, 0.4);
}
}
//
.impact-count {
color: #ff4d4f;
cursor: pointer;
font-weight: 600;
transition: all 0.3s;
&:hover {
color: #ff7875;
text-shadow: 0 0 8px rgba(255, 77, 79, 0.6);
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 16px;
padding: 12px;
:deep(.el-pagination) {
.el-pagination__total {
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
}
.el-pagination__sizes .el-input__inner {
color: #fff;
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
font-size: 13px;
}
.el-pagination__btn {
color: rgba(255, 255, 255, 0.8);
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 4px;
transition: all 0.3s;
&:hover:not(.is-disabled) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.4);
}
&.is-disabled {
opacity: 0.4;
}
}
.el-pagination__item {
color: rgba(255, 255, 255, 0.8);
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 4px;
transition: all 0.3s;
&:hover:not(.is-disabled) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.4);
}
&.is-active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
}
.el-pagination__jump .el-input__inner {
color: #fff;
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
font-size: 13px;
}
}
}
:deep(.el-table__body) {
width: 100% !important;
}
//
:deep(.el-table__body-wrapper::-webkit-scrollbar) {
display: none;
}
:deep(.el-table__body-wrapper) {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>
<style lang="scss">
//
.el-picker-panel {
background: rgba(20, 50, 90, 0.98) !important;
border: 1px solid rgba(64, 169, 255, 0.3) !important;
.el-picker-panel__content {
color: #fff;
.el-date-table th {
color: rgba(255, 255, 255, 0.6);
border-bottom-color: rgba(64, 169, 255, 0.3);
}
.el-date-table td {
color: rgba(255, 255, 255, 0.8);
&.selected .el-date-table-cell {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
}
&.today span {
color: #40a9ff;
font-weight: bold;
}
&.in-range,
&.start-date,
&.end-date {
background: rgba(64, 169, 255, 0.2);
}
}
}
.el-picker-panel__footer {
border-top: 1px solid rgba(64, 169, 255, 0.3);
.el-button {
background: rgba(30, 70, 120, 0.4);
border-color: rgba(64, 169, 255, 0.3);
color: #fff;
&:hover {
background: rgba(64, 169, 255, 0.3);
}
}
.el-button--primary {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
border: none;
&:hover {
background: linear-gradient(135deg, #69c0ff 0%, #40a9ff 100%);
}
}
}
}
.el-table--fit .el-table__inner-wrapper:before {
width: 0% !important;
}
</style>

View File

@ -79,6 +79,7 @@
" "
alt="" alt=""
/> />
<span class="ml_10">{{ row.level }}</span>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -465,7 +466,7 @@ onUnmounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: vw(12); font-size: vw(14);
color: #fff; color: #fff;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
@ -537,7 +538,7 @@ onUnmounted(() => {
} }
span { span {
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
&.tag-red { &.tag-red {
@ -606,7 +607,7 @@ onUnmounted(() => {
td.el-table__cell { td.el-table__cell {
background: transparent; background: transparent;
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.9);
padding: vw(5) vw(5); padding: vw(5) vw(5);
} }

View File

@ -41,19 +41,27 @@ const loadMapData = async () => {
try { try {
// //
// // URLMap=dev
const response = await fetch(GEOJSON_URL) const urlParams = new URLSearchParams(window.location.search);
if (!response.ok) { const isDev = urlParams.get('Map') === 'dev';
throw new Error(`HTTP error! status: ${response.status}`)
}
const geoJsonData = await response.json()
// let geoJsonData;
// const response = await axios.get('/aliyun-geo/bound/500000_full.json');
// const geoJsonData = response.data; if (isDev) {
// if (!geoJsonData) { //
// throw new Error(''); const response = await fetch(GEOJSON_URL)
// } if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
geoJsonData = await response.json()
} else {
//
const response = await axios.get('/aliyun-geo/bound/500000_full.json');
geoJsonData = response.data;
if (!geoJsonData) {
throw new Error('地图数据为空');
}
}
// // // //
@ -164,16 +172,16 @@ const initMap = (geoJsonData) => {
}); });
// - 使 // - 使
const tileLayer = new window.L.TileLayer( // const tileLayer = new window.L.TileLayer(
"https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png", // "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
{ // {
attribution: // attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>', // '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
subdomains: "abcd", // subdomains: "abcd",
maxZoom: 19, // maxZoom: 19,
}, // },
); // );
mapInstance.addLayer(tileLayer); // mapInstance.addLayer(tileLayer);
// GeoJSON // GeoJSON
geoJsonLayer = new window.L.GeoJSON(geoJsonData, { geoJsonLayer = new window.L.GeoJSON(geoJsonData, {

View File

@ -1,371 +0,0 @@
<template>
<div v-if="visible" class="ai-dialog-overlay" @click="handleOverlayClick">
<div class="ai-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">AI预警处理结果</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 内容区域 -->
<div class="content-wrapper">
<!-- AI处理前预警 -->
<div class="panel before-panel" style="background: linear-gradient(135deg, rgba(30, 80, 140, 0.6) 0%, rgba(20, 60, 110, 0.8) 100%);">
<div class="panel-title">AI处理前预警</div>
<div class="panel-content">
<div class="info-item">
<span class="info-label">发布单位&#xff1a;</span>
<span class="info-value">{{ beforeData.publishOrg }}</span>
</div>
<div class="info-item">
<span class="info-label">发布内容&#xff1a;</span>
<span class="info-value content-text">{{ beforeData.publishContent }}</span>
</div>
<div class="info-item">
<span class="info-label">生效时间&#xff1a;</span>
<span class="info-value">{{ beforeData.effectiveTime }}</span>
</div>
<div class="info-item">
<span class="info-label">失效时间&#xff1a;</span>
<span class="info-value">{{ beforeData.expireTime }}</span>
</div>
</div>
</div>
<!-- 中间AI标识 -->
<div class="ai-center">
<img class="ai-icon-img" src="../../../assets/RiskWarning_img/AI1@2x.png" alt="AI" />
<div class="ai-arrow">
<el-icon><DArrowRight /></el-icon>
</div>
</div>
<!-- AI处理后预警 -->
<div class="panel after-panel panel1" style="background: #2699FF">
<div class="panel-title" style="color:#fff">AI处理后预警</div>
<div class="panel-content">
<!-- 标签页 -->
<div class="tab-header">
<div
v-for="tab in tabs"
:key="tab.key"
class="tab-item"
:class="{ active: activeTab === tab.key }"
@click="activeTab = tab.key"
>
{{ tab.label }}
</div>
</div>
<!-- 标签内容 -->
<div class="tab-content">
<div class="info-item">
<span class="info-label">预警等级&#xff1a;</span>
<span class="info-value">{{ afterData.warningLevel }}</span>
</div>
<div class="info-item">
<span class="info-label">预警信息&#xff1a;</span>
<span class="info-value content-text">{{ afterData.warningInfo }}</span>
</div>
<div class="info-item">
<span class="info-label">预警详情&#xff1a;</span>
<span class="info-value content-text">{{ afterData.warningDetail }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { Close, DArrowRight } from "@element-plus/icons-vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
aiData: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(["update:visible", "close"]);
//
const tabs = ref([
{ key: "frontline", label: "一线人员" },
{ key: "district", label: "区县人员" },
{ key: "handler", label: "处室人员" },
{ key: "leader", label: "中心领导" },
]);
const activeTab = ref("frontline");
// AI
const beforeData = ref({
publishOrg: "潼南区预警中心",
publishContent: '潼南区气象台2025年11月13日0时40分发布"暴雨蓝色预警信号"预计23日0:40-6:40龙形、宝龙、上和、大佛、桂林、玉溪、米心、花岩、双江、古溪、群力、柏梓、崇龛、梓潼、太安等15个乡镇街道强降水趋于减弱未来6小时累计雨量将达50100毫米最大小时雨强将达2040毫米局地伴有雷电、阵性大风请各地注意防范。',
effectiveTime: "2025-11-13 00:31:30.0",
expireTime: "2025-11-13 00:31:30.0",
});
// AI
const afterData = ref({
warningLevel: "重庆市潼南气象台发布大雾黄色预警[III级/较重]",
warningInfo: '潼南区区气象台发布暴雨红色预警按照相关要求启动I级防御响应并请及时关注地质、水文等风险提示信息落实主动封闭管控/"关停撒转"措施',
warningDetail: "请立即按照2小时一次频率对你管养的重点路段/重点部位进行巡查,重点巡查较高及以上风险路段、涉灾隐患点、地质条件复杂路段、临河临崖路段/两区三厂、大型设施设备、取弃土(渣)场、砂石料场、涉水桥梁、富水隧道、围堰、支架脚手架、高切坡、滑坡处置等部位,重点关注涉水桥梁基础及墩台、不良地质隧道、隧道洞口边仰坡及侧切结构、高陡边坡支挡防护以及防排水设施,发现异常情况,立即向上报告,采取紧急排危、告警阻拦、吹哨撒转等措施,并及时报送工作开展情况。",
});
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal && props.aiData) {
Object.assign(beforeData.value, props.aiData.before);
Object.assign(afterData.value, props.aiData.after);
}
}
);
</script>
<style lang="scss" scoped>
.ai-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: 1000;
}
.ai-dialog {
width: 80vw;
max-width: 1000px;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 24px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.content-wrapper {
display: flex;
gap: 20px;
align-items: stretch;
}
//
.panel {
flex: 1;
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 12px;
padding: 20px;
position: relative;
&::before {
content: '';
position: absolute;
top: -1px;
left: -1px;
right: -1px;
bottom: -1px;
border-radius: 12px;
padding: 1px;
background: linear-gradient(135deg, rgba(64, 169, 255, 0.5) 0%, transparent 50%, rgba(64, 169, 255, 0.5) 100%);
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.panel-title {
font-size: 16px;
font-weight: 600;
color: #40a9ff;
text-align: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(64, 169, 255, 0.2);
}
.panel-content {
.info-item {
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.6);
margin-right: 8px;
}
.info-value {
font-size: 13px;
color: rgba(255, 255, 255, 0.9);
line-height: 1.6;
&.content-text {
display: block;
margin-top: 6px;
text-align: justify;
}
}
}
}
// AI
.ai-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
padding: 0 10px;
.ai-icon-img {
width: 60px;
height: 60px;
}
.ai-icon {
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: 700;
color: #fff;
box-shadow: 0 4px 20px rgba(64, 169, 255, 0.4);
}
.ai-arrow {
font-size: 32px;
color: #40a9ff;
animation: pulse 1.5s infinite;
:deep(.el-icon) {
font-size: 32px;
}
}
}
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: translateX(0);
}
50% {
opacity: 0.6;
transform: translateX(5px);
}
}
//
.tab-header {
display: flex;
gap: 8px;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(64, 169, 255, 0.2);
.tab-item {
padding: 6px 14px;
font-size: 12px;
color: rgba(255, 255, 255, 0.7);
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.4);
}
&.active {
// background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
background: #163B6C;
border-color: #40a9ff;
color: #fff;
}
}
}
.tab-content {
.info-item {
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
}
}
</style>

View File

@ -0,0 +1,376 @@
<template>
<div
v-if="props.visible"
class="base-dialog-overlay"
:style="{ zIndex: props.zIndex }"
@click="handleOverlayClick"
>
<div class="base-dialog" @click.stop :style="{ maxWidth: `${props.maxWidth}px` }">
<!-- 四个角的装饰 -->
<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">{{ props.title }}</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 标题栏下方自定义插槽 -->
<div class="header-slot">
<slot name="header"></slot>
</div>
<!-- 筛选区域 -->
<div class="filter-section" v-if="props.showFilter">
<slot name="filter"></slot>
</div>
<!-- 数据表格 -->
<div class="table-section" v-if="props.tableData.length > 0">
<el-table
:data="props.tableData"
:height="props.tableHeight"
style="width: 100%"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
size="small"
>
<el-table-column
v-for="column in props.tableColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
>
<template v-if="column.slot" #default="{ row }">
<slot :name="column.slot" :row="row"></slot>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination" v-if="props.showPagination && props.tableData.length > 0">
<el-pagination
:current-page="props.currentPage"
:page-size="props.pageSize"
:page-sizes="props.pageSizes"
:total="props.total"
background
layout="prev, pager, next, jumper, ->, total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import { ElTable, ElTableColumn, ElPagination } from "element-plus";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: "弹窗标题",
},
zIndex: {
type: Number,
default: 1000,
},
maxWidth: {
type: Number,
default: 1000,
},
showFilter: {
type: Boolean,
default: true,
},
showPagination: {
type: Boolean,
default: true,
},
tableData: {
type: Array,
default: () => [],
},
tableColumns: {
type: Array,
default: () => [],
},
tableHeight: {
type: Number,
default: 300,
},
currentPage: {
type: Number,
default: 1,
},
pageSize: {
type: Number,
default: 10,
},
pageSizes: {
type: Array,
default: () => [10, 20, 30, 40],
},
total: {
type: Number,
default: 0,
},
});
const emit = defineEmits(["update:visible", "close", "size-change", "current-change", "update:current-page", "update:page-size"]);
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
//
}
},
);
//
const handleSizeChange = (val) => {
emit("update:page-size", val);
emit("size-change", val);
};
const handleCurrentChange = (val) => {
emit("update:current-page", val);
emit("current-change", val);
};
//
const headerCellStyle = () => {
return {
backgroundColor: "#1D5194",
color: "#fff",
fontSize: "14px",
fontWeight: "500",
textAlign: "center",
padding: "5px 0px",
border: "none",
};
};
const cellStyle = () => {
return {
backgroundColor: "#16334E",
color: "rgba(255, 255, 255, 0.85)",
fontSize: "13px",
textAlign: "center",
padding: "5px 0px",
border: "none",
};
};
</script>
<style lang="scss" scoped>
.base-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;
}
:deep(.el-table--small) {
background: #16334E;
}
.base-dialog {
width: 80vw;
max-height: 80vh;
position: relative;
background: #16334e;
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
//
.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;
}
}
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20px;
width: 100%;
.header-title {
background-image: url("../../../assets/RiskWarning_img/标题@2x.png");
background-size: 100% 100%;
background-position: right;
font-size: 18px;
font-weight: 600;
color: #fff;
width: 50%;
text-align: center;
padding: 12px 0;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.header-slot {
margin-bottom: 20px;
padding: 0 24px;
overflow-y: auto;
max-height: 70vh;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
//
.filter-section {
margin-bottom: 16px;
padding: 0 24px;
}
//
.table-section {
width: 100%;
padding: 0 24px;
background: #16334E
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 16px;
padding: 12px;
background: #16334E;
:deep(.el-pagination) {
background: #16334E;
.el-pagination__total {
color: rgba(255, 255, 255, 0.6);
}
.el-pagination__sizes .el-input__inner {
background: #16334E;
border: 1px solid rgba(64, 169, 255, 0.3);
color: #fff;
}
.el-pagination__btn {
background: #16334E;
border: 1px solid rgba(64, 169, 255, 0.3);
color: rgba(255, 255, 255, 0.8);
&:hover:not(.is-disabled) {
background: rgba(64, 169, 255, 0.2);
}
}
.el-pagination__item {
background: #16334E;
border: 1px solid rgba(64, 169, 255, 0.3);
color: rgba(255, 255, 255, 0.8);
&:hover:not(.is-disabled) {
background: rgba(64, 169, 255, 0.2);
}
&.is-active {
background: #40a9ff;
border-color: #40a9ff;
}
}
.el-pagination__jump .el-input__inner {
background: #16334E;
border: 1px solid rgba(64, 169, 255, 0.3);
color: #fff;
}
}
}
</style>

View File

@ -1,590 +0,0 @@
<template>
<div v-if="visible" class="clearance-dialog-overlay" @click="handleOverlayClick">
<div class="clearance-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">抢通情况</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 筛选区域 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">行政区域</span>
<el-select v-model="filterForm.region" placeholder="请选择" class="filter-select" clearable>
<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.type" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in typeOptions"
: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.controlMeasure" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in controlMeasureOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div
v-for="(column, index) in tableColumns"
:key="index"
class="th"
:style="{ width: column.width, flex: column.flex || 'none' }"
>
{{ column.label }}
</div>
</div>
<div class="table-body">
<div
v-for="(item, index) in tableData"
:key="item.id"
class="table-row"
:class="{ 'row-even': index % 2 === 1 }"
>
<div class="td" style="width: 50px">{{ item.id }}</div>
<div class="td" style="width: 80px">{{ item.region }}</div>
<div class="td" style="width: 80px">{{ item.routeNo }}</div>
<div class="td" style="width: 100px">
<el-tooltip :content="item.stakeNo" placement="top" :show-after="500">
<span class="ellipsis-text">{{ item.stakeNo }}</span>
</el-tooltip>
</div>
<div class="td" style="width: 100px">
<el-tooltip :content="item.location" placement="top" :show-after="500">
<span class="ellipsis-text">{{ item.location }}</span>
</el-tooltip>
</div>
<div class="td" style="width: 140px">{{ item.occurrenceTime }}</div>
<div class="td" style="width: 80px">{{ item.routeNo2 }}</div>
<div class="td" style="width: 80px">{{ item.type }}</div>
<div class="td" style="width: 100px">
<span :class="['control-tag', getControlClass(item.controlMeasure)]">{{ item.controlMeasure }}</span>
</div>
<div class="td" style="flex: 1">
<span class="detail-link" @click="handleDetail(item)">详情</span>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination">
<div class="page-btn" :class="{ disabled: currentPage === 1 }" @click="prevPage">
上一个
</div>
<div class="page-numbers">
<div
v-for="page in visiblePages"
:key="page"
class="page-num"
:class="{ active: currentPage === page }"
@click="goToPage(page)"
>
{{ page }}
</div>
</div>
<div class="page-btn" :class="{ disabled: currentPage === totalPages }" @click="nextPage">
下一个
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import { regionOptions, typeOptions, controlMeasureOptions } from "./index.js";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "detail"]);
//
const filterForm = ref({
region: "",
type: "",
controlMeasure: "",
});
//
// index.js
//
// index.js
//
// index.js
//
const tableColumns = ref([
{ label: "序号", width: "50px" },
{ label: "行政区域", width: "80px" },
{ label: "线路编号", width: "80px" },
{ label: "起止桩号", width: "100px" },
{ label: "路况位置", width: "100px" },
{ label: "发生时间", width: "140px" },
{ label: "线路编号", width: "80px" },
{ label: "类型", width: "80px" },
{ label: "管控措施", width: "100px" },
{ label: "操作", flex: "1" },
]);
//
const tableData = ref([
{
id: 1,
region: "巫溪县",
routeNo: "G242",
stakeNo: "336.800-338.850",
location: "三星乡五斗村",
occurrenceTime: "2025-08-11 04:53:42",
routeNo2: "G242",
type: "边坡坍塌",
controlMeasure: "全幅封闭",
},
{
id: 2,
region: "万州区",
routeNo: "G242",
stakeNo: "338.800-338.850",
location: "三星乡五斗村",
occurrenceTime: "2025-08-11 04:53:42",
routeNo2: "G242",
type: "边坡坍塌",
controlMeasure: "正常通行",
},
{
id: 3,
region: "沙坪坝区",
routeNo: "G319",
stakeNo: "120.500-122.000",
location: "沙坪坝镇",
occurrenceTime: "2025-08-10 14:30:00",
routeNo2: "G319",
type: "路面塌陷",
controlMeasure: "半幅封闭",
},
{
id: 4,
region: "渝中区",
routeNo: "G212",
stakeNo: "88.200-89.100",
location: "渝中大道",
occurrenceTime: "2025-08-09 09:15:30",
routeNo2: "G212",
type: "桥梁损坏",
controlMeasure: "限制通行",
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 5;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const getControlClass = (measure) => {
const classMap = {
"全幅封闭": "control-close",
"半幅封闭": "control-half",
"正常通行": "control-normal",
"限制通行": "control-limit",
};
return classMap[measure] || "";
};
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
//
const handleDetail = (item) => {
emit("detail", item);
};
//
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
fetchData();
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
fetchData();
}
};
const goToPage = (page) => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
.clearance-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: 2000;
}
.clearance-dialog {
width: 80vw;
max-width: 1000px;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.filter-section {
margin-bottom: 16px;
.filter-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.filter-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.filter-select {
width: 120px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
}
//
.table-section {
background-color: rgba(30, 70, 120, 0.3);
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
.table-header {
display: flex;
background-color: rgba(64, 169, 255, 0.2);
padding: 12px 16px;
.th {
font-size: 14px;
font-weight: 500;
color: #fff;
text-align: center;
}
}
.table-body {
max-height: 320px;
overflow-y: auto;
.table-row {
display: flex;
padding: 14px 16px;
align-items: center;
transition: background-color 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&.row-even {
background-color: rgba(30, 70, 120, 0.2);
}
.td {
font-size: 13px;
color: rgba(255, 255, 255, 0.85);
text-align: center;
.ellipsis-text {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
cursor: pointer;
}
//
.control-tag {
display: inline-block;
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
&.control-close {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
&.control-half {
background-color: rgba(255, 122, 0, 0.2);
color: #ff7a00;
border: 1px solid rgba(255, 122, 0, 0.4);
}
&.control-normal {
background-color: rgba(82, 196, 26, 0.2);
color: #52c41a;
border: 1px solid rgba(82, 196, 26, 0.4);
}
&.control-limit {
background-color: rgba(250, 219, 20, 0.2);
color: #fadb14;
border: 1px solid rgba(250, 219, 20, 0.4);
}
}
//
.detail-link {
color: #40a9ff;
cursor: pointer;
font-size: 13px;
transition: color 0.3s;
&:hover {
color: #69c0ff;
text-decoration: underline;
}
}
}
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
.page-btn {
padding: 6px 16px;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.disabled) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.5);
}
&.disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
.page-numbers {
display: flex;
gap: 8px;
.page-num {
min-width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.active) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.5);
}
&.active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
}
}
}
//
.table-body::-webkit-scrollbar {
width: 6px;
}
.table-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
</style>

View File

@ -1,535 +0,0 @@
<template>
<div v-if="visible" class="control-dialog-overlay" @click="handleOverlayClick">
<div class="control-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">管控情况</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 筛选区域 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">行政区域</span>
<el-select v-model="filterForm.region" placeholder="请选择" class="filter-select" clearable>
<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.riskLevel" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in riskLevelOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div
v-for="(column, index) in tableColumns"
:key="index"
class="th"
:style="{ width: column.width, flex: column.flex || 'none' }"
>
{{ column.label }}
</div>
</div>
<div class="table-body">
<div
v-for="(item, index) in tableData"
:key="item.id"
class="table-row"
:class="{ 'row-even': index % 2 === 1 }"
>
<div class="td" style="width: 60px">{{ item.id }}</div>
<div class="td" style="width: 100px">{{ item.region }}</div>
<div class="td" style="width: 200px">
<el-tooltip :content="item.stationName" placement="top" :show-after="500">
<span class="ellipsis-text">{{ item.stationName }}</span>
</el-tooltip>
</div>
<div class="td" style="width: 200px">
<el-tooltip :content="item.project" placement="top" :show-after="500">
<span class="ellipsis-text">{{ item.project }}</span>
</el-tooltip>
</div>
<div class="td" style="width: 80px">{{ item.peopleCount }}</div>
<div class="td" style="flex: 1">
<span :class="['risk-tag', getRiskClass(item.riskLevel)]">{{ item.riskLevel }}</span>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination">
<div class="page-btn" :class="{ disabled: currentPage === 1 }" @click="prevPage">
上一个
</div>
<div class="page-numbers">
<div
v-for="page in visiblePages"
:key="page"
class="page-num"
:class="{ active: currentPage === page }"
@click="goToPage(page)"
>
{{ page }}
</div>
</div>
<div class="page-btn" :class="{ disabled: currentPage === totalPages }" @click="nextPage">
下一个
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import { regionOptions, riskLevelOptions } from "./index.js";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close"]);
//
const filterForm = ref({
region: "",
riskLevel: "",
});
//
// index.js
//
// index.js
//
const tableColumns = ref([
{ label: "序号", width: "60px" },
{ label: "行政区域", width: "100px" },
{ label: "驻地名称", width: "200px" },
{ label: "所属项目", width: "200px" },
{ label: "驻地人数", width: "80px" },
{ label: "驻地风险等级", flex: "1" },
]);
//
const tableData = ref([
{
id: 1,
region: "沙坪坝区",
stationName: "沙坪坝区S545茅山峡公路桥新建工程渝黔铁路扩能改造工程项目经理部",
project: "沙坪坝区S545茅山峡公路桥新建工程渝黔铁路扩能改造工程",
peopleCount: 21,
riskLevel: "Ⅳ级",
},
{
id: 2,
region: "万州区",
stationName: "万州区项目经理部",
project: "万州区公路改造项目",
peopleCount: 15,
riskLevel: "Ⅲ级",
},
{
id: 3,
region: "渝中区",
stationName: "渝中区桥梁维护项目部",
project: "渝中区桥梁维护工程",
peopleCount: 8,
riskLevel: "Ⅱ级",
},
{
id: 4,
region: "江北区",
stationName: "江北区道路施工项目部",
project: "江北区道路施工项目",
peopleCount: 32,
riskLevel: "Ⅰ级",
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 5;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const getRiskClass = (level) => {
const classMap = {
"Ⅰ级": "risk-level-1",
"Ⅱ级": "risk-level-2",
"Ⅲ级": "risk-level-3",
"Ⅳ级": "risk-level-4",
};
return classMap[level] || "";
};
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
//
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
fetchData();
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
fetchData();
}
};
const goToPage = (page) => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
.control-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: 2000;
}
.control-dialog {
width: 80vw;
max-width: 900px;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.filter-section {
margin-bottom: 16px;
.filter-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.filter-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.filter-select {
width: 140px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
}
//
.table-section {
background-color: rgba(30, 70, 120, 0.3);
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
.table-header {
display: flex;
background-color: rgba(64, 169, 255, 0.2);
padding: 12px 16px;
.th {
font-size: 14px;
font-weight: 500;
color: #fff;
text-align: center;
}
}
.table-body {
max-height: 320px;
overflow-y: auto;
.table-row {
display: flex;
padding: 14px 16px;
align-items: center;
transition: background-color 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&.row-even {
background-color: rgba(30, 70, 120, 0.2);
}
.td {
font-size: 13px;
color: rgba(255, 255, 255, 0.85);
text-align: center;
.ellipsis-text {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
cursor: pointer;
}
//
.risk-tag {
display: inline-block;
padding: 2px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
&.risk-level-1 {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
&.risk-level-2 {
background-color: rgba(255, 122, 0, 0.2);
color: #ff7a00;
border: 1px solid rgba(255, 122, 0, 0.4);
}
&.risk-level-3 {
background-color: rgba(250, 219, 20, 0.2);
color: #fadb14;
border: 1px solid rgba(250, 219, 20, 0.4);
}
&.risk-level-4 {
background-color: rgba(82, 196, 26, 0.2);
color: #52c41a;
border: 1px solid rgba(82, 196, 26, 0.4);
}
}
}
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
.page-btn {
padding: 6px 16px;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.disabled) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.5);
}
&.disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
.page-numbers {
display: flex;
gap: 8px;
.page-num {
min-width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.active) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.5);
}
&.active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
}
}
}
//
.table-body::-webkit-scrollbar {
width: 6px;
}
.table-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
</style>

View File

@ -1,478 +0,0 @@
<template>
<div v-if="visible" class="dispatch-detail-overlay" @click="handleOverlayClick">
<div class="dispatch-detail-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">调度区县情况</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 筛选区域 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">行政区域</span>
<el-select v-model="filterForm.region" placeholder="请选择" class="filter-select" clearable>
<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.type" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in typeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div
v-for="(column, index) in tableColumns"
:key="index"
class="th"
:style="{ width: column.width, flex: column.flex || 'none' }"
>
{{ column.label }}
</div>
</div>
<div class="table-body">
<div
v-for="(item, index) in tableData"
:key="item.id"
class="table-row"
:class="{ 'row-even': index % 2 === 1 }"
>
<div class="td" style="width: 50px">{{ item.id }}</div>
<div class="td" style="width: 100px">{{ item.district }}</div>
<div class="td" style="width: 80px">{{ item.name }}</div>
<div class="td" style="width: 120px">{{ item.phone }}</div>
<div class="td" style="width: 100px">{{ item.type }}</div>
<div class="td" style="width: 140px">{{ item.role }}</div>
<div class="td" style="flex: 1">{{ item.dispatchTime }}</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination">
<div class="page-btn" :class="{ disabled: currentPage === 1 }" @click="prevPage">
上一个
</div>
<div class="page-numbers">
<div
v-for="page in visiblePages"
:key="page"
class="page-num"
:class="{ active: currentPage === page }"
@click="goToPage(page)"
>
{{ page }}
</div>
</div>
<div class="page-btn" :class="{ disabled: currentPage === totalPages }" @click="nextPage">
下一个
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import { regionOptions, typeOptions } from "./index.js";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close"]);
//
const filterForm = ref({
region: "",
type: "",
});
//
// index.js
//
// index.js
//
const tableColumns = ref([
{ label: "序号", width: "50px" },
{ label: "区县/镇街", width: "100px" },
{ label: "姓名", width: "80px" },
{ label: "电话", width: "120px" },
{ label: "类型", width: "100px" },
{ label: "角色", width: "140px" },
{ label: "调度时间", flex: "1" },
]);
//
const tableData = ref([
{
id: 1,
district: "柏梓镇",
name: "赵海浪",
phone: "18623520681",
type: "交通主管部门",
role: "一般人员(路长履职)",
dispatchTime: "2025-08-11 04:53:42",
},
{
id: 2,
district: "柏梓镇",
name: "赵海浪",
phone: "18623520681",
type: "公路机构",
role: "一般人员(路长履职)",
dispatchTime: "2025-08-11 04:53:42",
},
{
id: 3,
district: "万州区",
name: "王鑫",
phone: "18623520682",
type: "养护站",
role: "站长",
dispatchTime: "2025-08-10 14:30:00",
},
{
id: 4,
district: "沙坪坝区",
name: "李华",
phone: "18623520683",
type: "护路员",
role: "一般人员",
dispatchTime: "2025-08-09 09:15:30",
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 5;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
//
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
fetchData();
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
fetchData();
}
};
const goToPage = (page) => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
.dispatch-detail-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: 2100;
}
.dispatch-detail-dialog {
width: 80vw;
max-width: 900px;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.filter-section {
margin-bottom: 16px;
.filter-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.filter-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.filter-select {
width: 140px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
}
//
.table-section {
background-color: rgba(30, 70, 120, 0.3);
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
.table-header {
display: flex;
background-color: rgba(64, 169, 255, 0.2);
padding: 12px 16px;
.th {
font-size: 14px;
font-weight: 500;
color: #fff;
text-align: center;
}
}
.table-body {
max-height: 320px;
overflow-y: auto;
.table-row {
display: flex;
padding: 14px 16px;
align-items: center;
transition: background-color 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&.row-even {
background-color: rgba(30, 70, 120, 0.2);
}
.td {
font-size: 13px;
color: rgba(255, 255, 255, 0.85);
text-align: center;
}
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
.page-btn {
padding: 6px 16px;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.disabled) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.5);
}
&.disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
.page-numbers {
display: flex;
gap: 8px;
.page-num {
min-width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.active) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.5);
}
&.active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
}
}
}
//
.table-body::-webkit-scrollbar {
width: 6px;
}
.table-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
</style>

View File

@ -1,465 +0,0 @@
<template>
<div v-if="visible" class="dispatch-dialog-overlay" @click="handleOverlayClick">
<div class="dispatch-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">调度区县情况</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 筛选区域 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">行政区域</span>
<el-select v-model="filterForm.region" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in regionOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div
v-for="(column, index) in tableColumns"
:key="index"
class="th"
:style="{ width: column.width, flex: column.flex || 'none' }"
>
{{ column.label }}
</div>
</div>
<div class="table-body">
<div
v-for="(item, index) in tableData"
:key="item.id"
class="table-row"
:class="{ 'row-even': index % 2 === 1 }"
>
<div class="td" style="width: 60px">{{ item.id }}</div>
<div class="td" style="width: 120px">{{ item.region }}</div>
<div class="td" style="width: 100px">
<span class="dispatch-count" @click="handleDispatchClick(item)">{{ item.dispatchCount }}</span>
</div>
<div class="td" style="flex: 1">{{ item.lastDispatchTime }}</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination">
<div class="page-btn" :class="{ disabled: currentPage === 1 }" @click="prevPage">
上一个
</div>
<div class="page-numbers">
<div
v-for="page in visiblePages"
:key="page"
class="page-num"
:class="{ active: currentPage === page }"
@click="goToPage(page)"
>
{{ page }}
</div>
</div>
<div class="page-btn" :class="{ disabled: currentPage === totalPages }" @click="nextPage">
下一个
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import { regionOptions } from "./index.js";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "dispatchClick"]);
//
const filterForm = ref({
region: "",
});
//
// index.js
//
const tableColumns = ref([
{ label: "序号", width: "60px" },
{ label: "行政区域", width: "120px" },
{ label: "调度数", width: "100px" },
{ label: "最近调度时间", flex: "1" },
]);
//
const tableData = ref([
{
id: 1,
region: "重庆市",
dispatchCount: 1,
lastDispatchTime: "2025-08-11 04:53:42",
},
{
id: 2,
region: "万州区",
dispatchCount: 1,
lastDispatchTime: "2025-08-11 04:53:42",
},
{
id: 3,
region: "沙坪坝区",
dispatchCount: 3,
lastDispatchTime: "2025-08-10 16:20:15",
},
{
id: 4,
region: "渝中区",
dispatchCount: 2,
lastDispatchTime: "2025-08-09 11:45:30",
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 5;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
//
const handleDispatchClick = (item) => {
emit("dispatchClick", item);
};
//
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
fetchData();
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
fetchData();
}
};
const goToPage = (page) => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
.dispatch-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: 2000;
}
.dispatch-dialog {
width: 80vw;
max-width: 700px;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.filter-section {
margin-bottom: 16px;
.filter-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.filter-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.filter-select {
width: 140px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
}
//
.table-section {
background-color: rgba(30, 70, 120, 0.3);
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
.table-header {
display: flex;
background-color: rgba(64, 169, 255, 0.2);
padding: 12px 16px;
.th {
font-size: 14px;
font-weight: 500;
color: #fff;
text-align: center;
}
}
.table-body {
max-height: 320px;
overflow-y: auto;
.table-row {
display: flex;
padding: 14px 16px;
align-items: center;
transition: background-color 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&.row-even {
background-color: rgba(30, 70, 120, 0.2);
}
.td {
font-size: 13px;
color: rgba(255, 255, 255, 0.85);
text-align: center;
//
.dispatch-count {
color: #40a9ff;
cursor: pointer;
font-weight: 600;
transition: all 0.3s;
&:hover {
color: #69c0ff;
text-shadow: 0 0 8px rgba(105, 192, 255, 0.6);
}
}
}
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
.page-btn {
padding: 6px 16px;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.disabled) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.5);
}
&.disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
.page-numbers {
display: flex;
gap: 8px;
.page-num {
min-width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.active) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.5);
}
&.active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
}
}
}
//
.table-body::-webkit-scrollbar {
width: 6px;
}
.table-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
</style>

View File

@ -1,655 +0,0 @@
<template>
<div v-if="visible" class="impact-dialog-overlay" @click="handleOverlayClick">
<div class="impact-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">影响点情况</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<div></div>
<!-- 统计卡片 -->
<div class="stats-cards">
<div class="stat-card">
<div class="stat-label">影响桥梁</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响边坡</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响隧道</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响项目</div>
<div class="stat-value">2933</div>
</div>
</div>
<!-- 筛选区域 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<el-select 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">
<el-select 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">
<el-select v-model="filterForm.region" placeholder="行政区域" class="filter-select">
<el-option
v-for="option in regionOptionsWithAll"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div class="filter-item">
<el-button type="primary" class="search-btn" @click="handleSearch">
查询
</el-button>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div
v-for="(column, index) in tableColumns"
:key="index"
class="th"
:style="{ width: column.width, flex: column.flex || 'none' }"
>
{{ column.label }}
</div>
</div>
<div class="table-body">
<div
v-for="(item, index) in tableData"
:key="item.id"
class="table-row"
:class="{ 'row-even': index % 2 === 1 }"
>
<div class="td" style="width: 50px">{{ item.id }}</div>
<div class="td" style="width: 80px">{{ item.region }}</div>
<div class="td" style="width: 80px">{{ item.pointType }}</div>
<div class="td" style="width: 180px">{{ item.pointLocation }}</div>
<div class="td" style="width: 90px">
<span class="level-tag" :class="item.levelClass">{{ item.pointLevel }}</span>
</div>
<div class="td" style="width: 130px">
<div class="person-info">
<span class="person-name">{{ item.trafficDept.name }}</span>
<span class="person-phone">{{ item.trafficDept.phone }}</span>
</div>
</div>
<div class="td" style="width: 110px">
<div class="person-info">
<span class="person-name">{{ item.roadOrg.name }}</span>
<span class="person-phone">{{ item.roadOrg.phone }}</span>
</div>
</div>
<div class="td" style="width: 110px">
<div class="person-info">
<span class="person-name">{{ item.maintenance.name }}</span>
<span class="person-phone">{{ item.maintenance.phone }}</span>
</div>
</div>
<div class="td" style="width: 100px">
<div class="person-info">
<span class="person-name">{{ item.roadKeeper.name }}</span>
<span class="person-phone">{{ item.roadKeeper.phone }}</span>
</div>
</div>
<div class="td" style="width: 60px">
<span class="detail-link" @click="handleDetail(item)">详情</span>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination">
<span class="total">{{ total }}条数据</span>
<div class="page-btns">
<div class="page-btn" :class="{ disabled: currentPage === 1 }" @click="prevPage">
<el-icon><ArrowLeft /></el-icon>
</div>
<div
v-for="page in visiblePages"
:key="page"
class="page-btn"
:class="{ active: currentPage === page }"
@click="goToPage(page)"
>
{{ page }}
</div>
<div class="page-btn" :class="{ disabled: currentPage === totalPages }" @click="nextPage">
<el-icon><ArrowRight /></el-icon>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close, ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
import { pointTypeOptions, pointLevelOptions, regionOptionsWithAll } from "./index.js";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "detail"]);
//
const filterForm = ref({
pointType: "",
pointLevel: "",
region: "",
});
//
const tableColumns = ref([
{ label: "序号", width: "50px" },
{ label: "行政区域", width: "80px" },
{ label: "影响点类型", width: "80px" },
{ label: "影响点位置", width: "180px" },
{ label: "影响点等级", width: "90px" },
{ label: "交通主管部门负责人", width: "130px" },
{ label: "公路机构责任人", width: "110px" },
{ label: "养护站负责人", width: "110px" },
{ label: "护路员", width: "100px" },
{ label: "操作", width: "60px" },
]);
//
const tableData = ref([
{
id: 1,
region: "重庆市",
pointType: "边坡",
pointLocation: "武汉-大理(K1452+951至K1462+209)",
pointLevel: "一般隐患",
levelClass: "level-normal",
trafficDept: { name: "罗宸", phone: "17623865172" },
roadOrg: { name: "李海平", phone: "13708320801" },
maintenance: { name: "苏祖兵", phone: "13594331090" },
roadKeeper: { name: "凌承礼", phone: "1592393704" },
},
{
id: 2,
region: "重庆市",
pointType: "边坡",
pointLocation: "武汉-大理(K1452+951至K1462+209)",
pointLevel: "一般隐患",
levelClass: "level-normal",
trafficDept: { name: "罗宸", phone: "17623865172" },
roadOrg: { name: "李海平", phone: "13708320801" },
maintenance: { name: "苏祖兵", phone: "13594331090" },
roadKeeper: { name: "凌承礼", phone: "1592393704" },
},
{
id: 3,
region: "重庆市",
pointType: "边坡",
pointLocation: "武汉-大理(K1452+951至K1462+209)",
pointLevel: "一般隐患",
levelClass: "level-normal",
trafficDept: { name: "罗宸", phone: "17623865172" },
roadOrg: { name: "李海平", phone: "13708320801" },
maintenance: { name: "苏祖兵", phone: "13594331090" },
roadKeeper: { name: "凌承礼", phone: "1592393704" },
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 4;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
//
const handleSearch = () => {
console.log("查询条件:", filterForm.value);
currentPage.value = 1;
fetchData();
};
//
const handleDetail = (item) => {
emit("detail", item);
};
//
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
fetchData();
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
fetchData();
}
};
const goToPage = (page) => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
// -
@function vw($px) {
@return calc($px / 1920 * 100vw);
}
.impact-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: 2000;
}
.impact-dialog {
width: 80vw;
max-width: 1200px;
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);
border-radius: vw(12);
padding: vw(24);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: vw(20);
.header-title {
font-size: vw(20);
font-weight: 600;
color: #fff;
padding: vw(8) vw(40);
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: vw(2) solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: vw(32);
height: vw(32);
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: vw(20);
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.stats-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: vw(16);
margin-bottom: vw(20);
.stat-card {
background: linear-gradient(135deg, rgba(30, 70, 120, 0.6) 0%, rgba(20, 50, 90, 0.8) 100%);
border: vw(2) solid rgba(64, 169, 255, 0.4);
border-radius: vw(8);
padding: vw(16) vw(20);
text-align: center;
transition: all 0.3s;
cursor: pointer;
&:hover {
border-color: rgba(64, 169, 255, 0.8);
box-shadow: 0 0 20px rgba(64, 169, 255, 0.3);
transform: translateY(-2px);
}
.stat-label {
font-size: vw(14);
color: rgba(255, 255, 255, 0.8);
margin-bottom: vw(8);
font-weight: 500;
}
.stat-value {
font-size: vw(28);
font-weight: bold;
color: #40a9ff;
text-shadow: 0 0 10px rgba(64, 169, 255, 0.5);
}
}
}
//
.filter-section {
margin-bottom: vw(20);
.filter-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.filter-item {
.filter-select {
width: vw(150);
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: vw(4);
.el-input__inner {
color: #fff;
font-size: vw(13);
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
.search-btn {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
border: none;
border-radius: vw(4);
padding: 0 vw(24);
height: vw(32);
font-size: vw(13);
&:hover {
background: linear-gradient(135deg, #69c0ff 0%, #40a9ff 100%);
}
}
}
}
//
.table-section {
background-color: rgba(30, 70, 120, 0.3);
border-radius: vw(8);
overflow: hidden;
margin-bottom: vw(20);
.table-header {
display: flex;
background-color: rgba(64, 169, 255, 0.2);
padding: vw(12) vw(16);
.th {
font-size: vw(13);
font-weight: 500;
color: #fff;
text-align: center;
}
}
.table-body {
max-height: 320px;
overflow-y: auto;
.table-row {
display: flex;
padding: vw(12) vw(16);
align-items: center;
transition: background-color 0.3s;
min-height: vw(60);
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&.row-even {
background-color: rgba(30, 70, 120, 0.2);
}
.td {
font-size: vw(12);
color: rgba(255, 255, 255, 0.85);
text-align: center;
word-break: break-all;
padding: 0 vw(4);
.level-tag {
display: inline-block;
padding: vw(2) vw(8);
border-radius: vw(4);
font-size: vw(11);
&.level-normal {
background-color: rgba(250, 219, 95, 0.2);
color: #fadb5f;
border: 1px solid rgba(250, 219, 95, 0.4);
}
&.level-serious {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
}
.person-info {
display: flex;
flex-direction: column;
gap: vw(2);
.person-name {
font-size: vw(12);
color: rgba(255, 255, 255, 0.9);
}
.person-phone {
font-size: vw(11);
color: rgba(255, 255, 255, 0.6);
}
}
.detail-link {
color: #40a9ff;
cursor: pointer;
font-size: vw(12);
&:hover {
color: #69c0ff;
text-decoration: underline;
}
}
}
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: vw(16);
.total {
font-size: vw(13);
color: rgba(255, 255, 255, 0.6);
}
.page-btns {
display: flex;
gap: vw(8);
.page-btn {
min-width: vw(28);
height: vw(28);
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: vw(4);
font-size: vw(12);
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.disabled):not(.active) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.4);
}
&.active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
&.disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
}
}
//
.table-body::-webkit-scrollbar {
width: vw(6);
}
.table-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: vw(3);
}
.table-body::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: vw(3);
}
.table-body::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
//
:deep(.el-select-dropdown) {
background-color: rgba(20, 50, 90, 0.98);
border: 1px solid rgba(64, 169, 255, 0.3);
.el-select-dropdown__item {
color: rgba(255, 255, 255, 0.85);
&:hover {
background-color: rgba(64, 169, 255, 0.2);
}
&.selected {
background-color: rgba(64, 169, 255, 0.3);
color: #40a9ff;
}
}
}
</style>

View File

@ -1,516 +0,0 @@
<template>
<div v-if="visible" class="response-dialog-overlay" @click="handleOverlayClick">
<div class="response-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">潼南三级路长明细</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 统计数据卡片 -->
<div class="stats-cards">
<div class="stat-card">
<div class="card-icon">
<el-icon class="stat-icon"><User /></el-icon>
</div>
<div class="card-info">
<div class="card-label">路长总人数</div>
<div class="card-value">
<span class="value-num">{{ stats.total }}</span>
<span class="value-unit"></span>
</div>
</div>
</div>
<div class="stat-card">
<div class="card-icon">
<el-icon class="stat-icon"><OfficeBuilding /></el-icon>
</div>
<div class="card-info">
<div class="card-label">县级路长</div>
<div class="card-value">
<span class="value-num">{{ stats.county }}</span>
<span class="value-unit">公里</span>
</div>
</div>
</div>
<div class="stat-card">
<div class="card-icon">
<el-icon class="stat-icon"><MapLocation /></el-icon>
</div>
<div class="card-info">
<div class="card-label">村路长</div>
<div class="card-value">
<span class="value-num">{{ stats.village }}</span>
<span class="value-unit"></span>
</div>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div
v-for="(column, index) in tableColumns"
:key="index"
class="th"
:style="{ width: column.width, flex: column.flex || 'none' }"
>
{{ column.label }}
</div>
</div>
<div class="table-body">
<div
v-for="(item, index) in tableData"
:key="item.id"
class="table-row"
:class="{ 'row-even': index % 2 === 1 }"
>
<div class="td" style="width: 60px">{{ index + 1 }}</div>
<div class="td" style="width: 120px">{{ item.district }}</div>
<div class="td" style="width: 100px">{{ item.name }}</div>
<div class="td" style="width: 120px">{{ item.phone }}</div>
<div class="td" style="flex: 1">{{ item.role }}</div>
<div class="td" style="width: 100px">{{ item.position }}</div>
<div class="td" style="width: 120px">
<div class="action-btns">
<div class="action-btn" @click="handleView(item)">
<el-icon><VideoCamera /></el-icon>
</div>
<div class="action-btn" @click="handleVoice(item)">
<el-icon><Microphone /></el-icon>
</div>
<div class="action-btn" @click="handleCall(item)">
<el-icon><Phone /></el-icon>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination">
<span class="total">{{ total }}条数据</span>
<div class="page-btns">
<div class="page-btn" :class="{ disabled: currentPage === 1 }" @click="prevPage">
<el-icon><ArrowLeft /></el-icon>
</div>
<div
v-for="page in visiblePages"
:key="page"
class="page-btn"
:class="{ active: currentPage === page }"
@click="goToPage(page)"
>
{{ page }}
</div>
<div class="page-btn" :class="{ disabled: currentPage === totalPages }" @click="nextPage">
<el-icon><ArrowRight /></el-icon>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close, VideoCamera, Microphone, Phone, ArrowLeft, ArrowRight, User, OfficeBuilding, MapLocation } from "@element-plus/icons-vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close"]);
//
const stats = ref({
total: 1127,
county: 216,
village: 1099,
});
//
const tableColumns = ref([
{ label: "序号", width: "60px" },
{ label: "区县/镇街", width: "120px" },
{ label: "姓名", width: "100px" },
{ label: "电话", width: "120px" },
{ label: "角色", flex: "1" },
{ label: "职务", width: "100px" },
{ label: "操作", width: "120px" },
]);
//
const tableData = ref([
{ id: 1, district: "万州区柏梓镇", name: "赵海浪", phone: "1862352068", role: "一般人员(路长履职)", position: "其他" },
{ id: 2, district: "万州区柏梓镇", name: "赵海浪", phone: "1862352068", role: "一般人员(路长履职)", position: "其他" },
{ id: 3, district: "万州区柏梓镇", name: "赵海浪", phone: "1862352068", role: "一般人员(路长履职)", position: "其他" },
{ id: 4, district: "万州区柏梓镇", name: "赵海浪", phone: "1862352068", role: "一般人员(路长履职)", position: "其他" },
{ id: 5, district: "万州区李河镇", name: "王建国", phone: "1398324567", role: "一般人员(路长履职)", position: "其他" },
{ id: 6, district: "万州区李河镇", name: "王建国", phone: "1398324567", role: "一般人员(路长履职)", position: "其他" },
{ id: 7, district: "万州区李河镇", name: "王建国", phone: "1398324567", role: "一般人员(路长履职)", position: "其他" },
{ id: 8, district: "万州区李河镇", name: "王建国", phone: "1398324567", role: "一般人员(路长履职)", position: "其他" },
{ id: 9, district: "万州区分水镇", name: "刘志强", phone: "1387654321", role: "一般人员(路长履职)", position: "其他" },
{ id: 10, district: "万州区分水镇", name: "刘志强", phone: "1387654321", role: "一般人员(路长履职)", position: "其他" },
{ id: 11, district: "万州区分水镇", name: "刘志强", phone: "1387654321", role: "一般人员(路长履职)", position: "其他" },
{ id: 12, district: "万州区分水镇", name: "刘志强", phone: "1387654321", role: "一般人员(路长履职)", position: "其他" },
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 4;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
//
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
fetchData();
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
fetchData();
}
};
const goToPage = (page) => {
currentPage.value = page;
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>
.response-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: 1000;
}
.response-dialog {
width: 70vw;
max-width: 700px;
max-height: 80vh;
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);
border-radius: 12px;
padding: 16px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 12px;
.header-title {
font-size: 14px;
font-weight: 600;
color: #fff;
padding: 4px 24px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 16px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.stats-cards {
display: flex;
gap: 12px;
margin-bottom: 12px;
.stat-card {
flex: 1;
display: flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, rgba(30, 80, 140, 0.6) 0%, rgba(20, 60, 110, 0.8) 100%);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 4px;
padding: 10px 12px;
.card-icon {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.15);
border-radius: 4px;
.stat-icon {
font-size: 18px;
color: #40a9ff;
}
}
.card-info {
.card-label {
font-size: 11px;
color: rgba(255, 255, 255, 0.7);
margin-bottom: 1px;
}
.card-value {
display: flex;
align-items: baseline;
gap: 2px;
.value-num {
font-size: 18px;
font-weight: 700;
color: #40a9ff;
text-shadow: 0 0 6px rgba(64, 169, 255, 0.5);
}
.value-unit {
font-size: 10px;
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
//
.table-section {
background-color: rgba(30, 70, 120, 0.3);
border-radius: 4px;
overflow: hidden;
margin-bottom: 12px;
.table-header {
display: flex;
background-color: rgba(64, 169, 255, 0.2);
padding: 8px 12px;
.th {
font-size: 12px;
font-weight: 500;
color: #fff;
text-align: center;
}
}
.table-body {
max-height: 30vh;
overflow-y: auto;
.table-row {
display: flex;
padding: 10px 12px;
align-items: center;
transition: background-color 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&.row-even {
background-color: rgba(30, 70, 120, 0.2);
}
.td {
font-size: 11px;
color: rgba(255, 255, 255, 0.85);
text-align: center;
.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);
}
}
}
}
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
.total {
font-size: 11px;
color: rgba(255, 255, 255, 0.6);
}
.page-btns {
display: flex;
gap: 4px;
.page-btn {
min-width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 2px;
font-size: 11px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.disabled):not(.active) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.4);
}
&.active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
&.disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
}
}
//
.table-body::-webkit-scrollbar {
width: 4px;
}
.table-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 2px;
}
.table-body::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 2px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.table-body::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
//
.table-body::-webkit-scrollbar-corner {
background: transparent;
}
</style>

View File

@ -1,697 +0,0 @@
<template>
<div
v-if="visible"
class="response-status-dialog-overlay"
@click="handleOverlayClick"
>
<div class="response-status-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">响应情况</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 统计卡片 -->
<div class="stats-cards">
<div class="stat-card">
<div class="stat-label">影响桥梁</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响边坡</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响隧道</div>
<div class="stat-value">2933</div>
</div>
<div class="stat-card">
<div class="stat-label">影响项目</div>
<div class="stat-value">2933</div>
</div>
</div>
<!-- 筛选区域 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<el-select
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">
<el-select
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">
<el-select
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>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div
v-for="(column, index) in tableColumns"
:key="index"
class="th"
:style="{ width: column.width, flex: column.flex || 'none' }"
>
{{ column.label }}
</div>
</div>
<div class="table-body">
<div
v-for="(item, index) in tableData"
:key="item.id"
class="table-row"
:class="{ 'row-even': index % 2 === 1 }"
>
<div class="td" style="width: 50px">{{ item.id }}</div>
<div class="td" style="width: 80px">{{ item.pointType }}</div>
<div class="td" style="width: 150px">{{ item.pointLocation }}</div>
<div class="td" style="width: 90px">
<span class="level-tag" :class="item.levelClass">{{
item.pointLevel
}}</span>
</div>
<div class="td" style="width: 60px">{{ item.checkCount }}</div>
<div class="td" style="width: 120px">
<div class="person-info">
<span class="person-name">{{ item.trafficDept.name }}</span>
<span class="person-phone">{{ item.trafficDept.phone }}</span>
</div>
</div>
<div class="td" style="width: 110px">
<div class="person-info">
<span class="person-name">{{ item.roadOrg.name }}</span>
<span class="person-phone">{{ item.roadOrg.phone }}</span>
</div>
</div>
<div class="td" style="width: 110px">
<div class="person-info">
<span class="person-name">{{ item.maintenance.name }}</span>
<span class="person-phone">{{ item.maintenance.phone }}</span>
</div>
</div>
<div class="td" style="width: 80px">
<div class="person-info">
<span class="person-name">{{ item.roadKeeper.name }}</span>
<span class="person-phone">{{ item.roadKeeper.phone }}</span>
</div>
</div>
<div class="td" style="width: 70px">
<span class="response-status" :class="item.responseClass">{{
item.responseStatus
}}</span>
</div>
<div class="td" style="width: 110px">
<div class="time-info">
<span class="time-date">{{ item.urgeTime.date }}</span>
<span class="time-clock">{{ item.urgeTime.time }}</span>
</div>
</div>
<div class="td" style="width: 50px">
<span class="detail-link" @click="handleDetail(item)">详情</span>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination">
<span class="total">{{ total }}条数据</span>
<div class="page-btns">
<div
class="page-btn"
:class="{ disabled: currentPage === 1 }"
@click="prevPage"
>
<el-icon><ArrowLeft /></el-icon>
</div>
<div
v-for="page in visiblePages"
:key="page"
class="page-btn"
:class="{ active: currentPage === page }"
@click="goToPage(page)"
>
{{ page }}
</div>
<div
class="page-btn"
:class="{ disabled: currentPage === totalPages }"
@click="nextPage"
>
<el-icon><ArrowRight /></el-icon>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close, ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
import { pointTypeOptions, pointLevelOptions, isRespondedOptions } from "./index.js";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "detail"]);
//
const filterForm = ref({
pointType: "",
pointLevel: "",
isResponded: "",
});
//
const tableColumns = ref([
{ label: "序号", width: "50px" },
{ label: "影响点类型", width: "80px" },
{ label: "影响点位置", width: "150px" },
{ label: "影响点等级", width: "90px" },
{ label: "查次数", width: "60px" },
{ label: "交通主管部门负责人", width: "120px" },
{ label: "公路机构责任人", width: "110px" },
{ label: "养护站负责人", width: "110px" },
{ label: "护路员", width: "80px" },
{ label: "回应状态", width: "70px" },
{ label: "最新催告时间", width: "110px" },
{ label: "操作", width: "50px" },
]);
//
const tableData = ref([
{
id: 1,
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" },
},
{
id: 12,
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" },
},
]);
tableData.value.push(...tableData.value);
tableData.value.push(...tableData.value);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 4;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
//
const handleDetail = (item) => {
emit("detail", item);
};
//
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
fetchData();
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
fetchData();
}
};
const goToPage = (page) => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
},
);
</script>
<style lang="scss" scoped>
.response-status-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: 1000;
}
.response-status-dialog {
width: 80vw;
max-width: 1150px;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(
90deg,
transparent 0%,
rgba(64, 169, 255, 0.2) 20%,
rgba(64, 169, 255, 0.2) 80%,
transparent 100%
);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.filter-section {
margin-bottom: 20px;
.filter-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.filter-item {
.filter-select {
width: 150px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
}
//
.table-section {
background-color: rgba(30, 70, 120, 0.3);
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
.table-header {
display: flex;
background-color: rgba(64, 169, 255, 0.2);
padding: 12px 0;
.th {
font-size: 13px;
font-weight: 500;
color: #fff;
text-align: center;
}
}
.table-body {
max-height: 200px;
overflow-y: auto;
.table-row {
display: flex;
padding: 12px 0px;
align-items: center;
transition: background-color 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&.row-even {
background-color: rgba(30, 70, 120, 0.2);
}
.td {
font-size: 12px;
color: rgba(255, 255, 255, 0.85);
text-align: center;
.level-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
&.level-normal {
background-color: rgba(250, 219, 95, 0.2);
color: #fadb5f;
border: 1px solid rgba(250, 219, 95, 0.4);
}
&.level-serious {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
}
.person-info {
display: flex;
flex-direction: column;
gap: 2px;
.person-name {
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
}
.person-phone {
font-size: 11px;
color: rgba(255, 255, 255, 0.6);
}
}
.response-status {
font-size: 12px;
&.status-responded {
color: #52c41a;
}
&.status-unresponded {
color: #ff7a45;
}
}
.time-info {
display: flex;
flex-direction: column;
gap: 2px;
.time-date {
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
}
.time-clock {
font-size: 11px;
color: rgba(255, 255, 255, 0.6);
}
}
.detail-link {
color: #40a9ff;
cursor: pointer;
font-size: 12px;
&:hover {
color: #69c0ff;
text-decoration: underline;
}
}
}
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 16px;
.total {
font-size: 13px;
color: rgba(255, 255, 255, 0.6);
}
.page-btns {
display: flex;
gap: 8px;
.page-btn {
min-width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 4px;
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.disabled):not(.active) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.4);
}
&.active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
&.disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
}
}
//
.table-body::-webkit-scrollbar {
width: 6px;
}
.table-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
//
:deep(.el-select-dropdown) {
background-color: rgba(20, 50, 90, 0.98);
border: 1px solid rgba(64, 169, 255, 0.3);
.el-select-dropdown__item {
color: rgba(255, 255, 255, 0.85);
&:hover {
background-color: rgba(64, 169, 255, 0.2);
}
&.selected {
background-color: rgba(64, 169, 255, 0.3);
color: #40a9ff;
}
}
}
//
.stats-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 20px;
.stat-card {
background: linear-gradient(
135deg,
rgba(30, 70, 120, 0.6) 0%,
rgba(20, 50, 90, 0.8) 100%
);
border: 2px solid rgba(64, 169, 255, 0.4);
border-radius: 8px;
padding: 8px 20px;
text-align: center;
transition: all 0.3s;
cursor: pointer;
&:hover {
border-color: rgba(64, 169, 255, 0.8);
box-shadow: 0 0 20px rgba(64, 169, 255, 0.3);
transform: translateY(-2px);
}
.stat-label {
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 8px;
font-weight: 500;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #40a9ff;
text-shadow: 0 0 10px rgba(64, 169, 255, 0.5);
}
}
}
</style>

View File

@ -1,665 +0,0 @@
<template>
<div v-if="visible" class="warning-dialog-overlay" @click="handleOverlayClick">
<div class="warning-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">响应情况</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 筛选区域 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<el-select v-model="filterForm.warningLevel" placeholder="预警等级" class="filter-select">
<el-option
v-for="option in warningLevelOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div class="filter-item">
<el-select v-model="filterForm.region" placeholder="行政区域" class="filter-select">
<el-option
v-for="option in regionOptionsWithAll"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div class="filter-item">
<el-select v-model="filterForm.isEnded" placeholder="是否结束" class="filter-select">
<el-option
v-for="option in isEndedOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div class="filter-item">
<el-select 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" class="search-btn" @click="handleSearch">
<el-icon><Search /></el-icon>
查询
</el-button>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div
v-for="(column, index) in tableColumns"
:key="index"
class="th"
:style="{ width: column.width, flex: column.flex || 'none' }"
>
{{ column.label }}
</div>
</div>
<div class="table-body">
<div
v-for="(item, index) in tableData"
:key="item.id"
class="table-row"
:class="{ 'row-even': index % 2 === 1 }"
>
<div class="td" style="width: 60px">{{ index + 1 }}</div>
<div class="td" style="width: 100px">
<span class="warning-level" :class="item.levelClass">{{ item.warningLevel }}</span>
</div>
<div class="td" style="width: 100px">{{ item.region }}</div>
<div class="td" style="width: 160px">{{ item.warningTime }}</div>
<div class="td" style="width: 160px">{{ item.endTime }}</div>
<div class="td" style="width: 100px">{{ item.impactPoints }}</div>
<div class="td clickable-cell" style="width: 80px" @click.stop="handleCalledClick(item)">{{ item.called }}</div>
<div class="td" style="width: 80px">{{ item.responded }}</div>
<div class="td" style="width: 80px">{{ item.notResponded }}</div>
<div class="td" style="width: 80px">{{ item.urged }}</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination">
<span class="total">{{ total }}条数据</span>
<div class="page-btns">
<div class="page-btn" :class="{ disabled: currentPage === 1 }" @click="prevPage">
<el-icon><ArrowLeft /></el-icon>
</div>
<div
v-for="page in visiblePages"
:key="page"
class="page-btn"
:class="{ active: currentPage === page }"
@click="goToPage(page)"
>
{{ page }}
</div>
<div class="page-btn" :class="{ disabled: currentPage === totalPages }" @click="nextPage">
<el-icon><ArrowRight /></el-icon>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close, Search, ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
import { warningLevelOptions, regionOptionsWithAll, isEndedOptions, isRespondedOptions } from "./index.js";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "openImpactPoint"]);
//
const filterForm = ref({
warningLevel: "",
region: "",
isEnded: "",
isResponded: "",
});
//
const tableColumns = ref([
{ label: "序号", width: "60px" },
{ label: "预警等级", width: "100px" },
{ label: "行政区域", width: "100px" },
{ label: "预警时间", width: "160px" },
{ label: "结束时间", width: "160px" },
{ label: "影响点数量", width: "100px" },
{ label: "已叫应", width: "80px" },
{ label: "已回应", width: "80px" },
{ label: "未回应", width: "80px" },
{ label: "已催告", width: "80px" },
]);
//
const tableData = ref([
{
id: 1,
warningLevel: "红色预警",
levelClass: "level-red",
region: "万州区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactPoints: 4,
called: 2,
responded: 2,
notResponded: 2,
urged: 22,
},
{
id: 2,
warningLevel: "橙色预警",
levelClass: "level-orange",
region: "涪陵区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactPoints: 0,
called: 0,
responded: 0,
notResponded: 0,
urged: 18,
},
{
id: 3,
warningLevel: "红色预警",
levelClass: "level-red",
region: "万盛区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactPoints: 0,
called: 0,
responded: 0,
notResponded: 0,
urged: 15,
},
{
id: 4,
warningLevel: "黄色预警",
levelClass: "level-yellow",
region: "长寿区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactPoints: 0,
called: 0,
responded: 0,
notResponded: 0,
urged: 20,
},
{
id: 5,
warningLevel: "红色预警",
levelClass: "level-red",
region: "城口区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactPoints: 0,
called: 0,
responded: 0,
notResponded: 0,
urged: 15,
},
{
id: 5,
warningLevel: "红色预警",
levelClass: "level-red",
region: "城口区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactPoints: 0,
called: 0,
responded: 0,
notResponded: 0,
urged: 15,
},
{
id: 5,
warningLevel: "红色预警",
levelClass: "level-red",
region: "城口区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactPoints: 0,
called: 0,
responded: 0,
notResponded: 0,
urged: 15,
},
{
id: 5,
warningLevel: "红色预警",
levelClass: "level-red",
region: "城口区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactPoints: 0,
called: 0,
responded: 0,
notResponded: 0,
urged: 15,
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 4;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
//
const handleCalledClick = () => {
emit("responseStatus");
};
//
const handleSearch = () => {
console.log("查询条件:", filterForm.value);
currentPage.value = 1;
fetchData();
};
//
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
fetchData();
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
fetchData();
}
};
const goToPage = (page) => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
.warning-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: 1000;
}
.warning-dialog {
width: 80vw;
max-width: 1000px;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.filter-section {
margin-bottom: 20px;
.filter-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.filter-item {
.filter-select {
width: 120px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
.search-btn {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
border: none;
border-radius: 4px;
padding: 0 20px;
height: 32px;
font-size: 13px;
display: flex;
align-items: center;
gap: 6px;
&:hover {
background: linear-gradient(135deg, #69c0ff 0%, #40a9ff 100%);
}
.el-icon {
font-size: 14px;
}
}
}
}
//
.table-section {
background-color: rgba(30, 70, 120, 0.3);
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
.table-header {
display: flex;
background-color: rgba(64, 169, 255, 0.2);
padding: 12px 16px;
.th {
font-size: 14px;
font-weight: 500;
color: #fff;
text-align: center;
&.clickable {
cursor: pointer;
transition: all 0.3s;
&:hover {
color: #69c0ff;
text-shadow: 0 0 8px rgba(105, 192, 255, 0.6);
}
}
}
}
.table-body {
max-height: 280px;
overflow-y: auto;
.table-row {
display: flex;
padding: 12px 16px;
align-items: center;
transition: background-color 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&.row-even {
background-color: rgba(30, 70, 120, 0.2);
}
.td {
font-size: 13px;
color: rgba(255, 255, 255, 0.85);
text-align: center;
&.clickable-cell {
cursor: pointer;
color: #40a9ff;
font-weight: 500;
transition: all 0.3s;
&:hover {
color: #69c0ff;
text-shadow: 0 0 8px rgba(105, 192, 255, 0.6);
}
}
.warning-level {
display: inline-block;
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
&.level-red {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
&.level-orange {
background-color: rgba(255, 122, 69, 0.2);
color: #ff7a45;
border: 1px solid rgba(255, 122, 69, 0.4);
}
&.level-yellow {
background-color: rgba(250, 219, 95, 0.2);
color: #fadb5f;
border: 1px solid rgba(250, 219, 95, 0.4);
}
&.level-blue {
background-color: rgba(64, 169, 255, 0.2);
color: #40a9ff;
border: 1px solid rgba(64, 169, 255, 0.4);
}
}
}
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 16px;
.total {
font-size: 13px;
color: rgba(255, 255, 255, 0.6);
}
.page-btns {
display: flex;
gap: 8px;
.page-btn {
min-width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.disabled):not(.active) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.4);
}
&.active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
&.disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
}
}
//
.table-body::-webkit-scrollbar {
width: 8px;
}
.table-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.table-body::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.table-body::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
.table-body::-webkit-scrollbar-corner {
background: transparent;
}
//
:deep(.el-select-dropdown) {
background-color: rgba(20, 50, 90, 0.98);
border: 1px solid rgba(64, 169, 255, 0.3);
.el-select-dropdown__item {
color: rgba(255, 255, 255, 0.85);
&:hover {
background-color: rgba(64, 169, 255, 0.2);
}
&.selected {
background-color: rgba(64, 169, 255, 0.3);
color: #40a9ff;
}
}
}
</style>

View File

@ -1,676 +0,0 @@
<template>
<div v-if="visible" class="warning-dialog-overlay" @click="handleOverlayClick">
<div class="warning-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">预警情况</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 筛选区域 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">预警等级</span>
<el-select v-model="filterForm.warningLevel" placeholder="请选择" class="filter-select" clearable>
<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.region" placeholder="请选择" class="filter-select" clearable>
<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>
<el-option
v-for="item in isEndedOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
<div class="filter-item">
<span class="filter-label">时间范围</span>
<el-date-picker
v-model="filterForm.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
class="date-range-picker"
width="210px"
style="width: 210px"
clearable
value-format="YYYY-MM-DD"
/>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div
v-for="(column, index) in tableColumns"
:key="index"
class="th"
:style="{ width: column.width, flex: column.flex || 'none' }"
>
{{ column.label }}
</div>
</div>
<div class="table-body">
<div
v-for="(item, index) in tableData"
:key="item.id"
class="table-row"
:class="{ 'row-even': index % 2 === 1 }"
>
<div class="td" style="width: 50px">{{ item.id }}</div>
<div class="td" style="width: 100px">
<span :class="['warning-level-tag', getWarningClass(item.warningLevel)]">{{ item.warningLevel }}</span>
</div>
<div class="td" style="width: 100px">{{ item.weatherType }}</div>
<div class="td" style="width: 100px">{{ item.region }}</div>
<div class="td" style="width: 160px">{{ item.warningTime }}</div>
<div class="td" style="width: 160px">{{ item.endTime }}</div>
<div class="td" style="flex: 1">
<span class="impact-count" @click="handleImpactClick(item)">{{ item.impactCount }}</span>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination">
<div class="page-btn" :class="{ disabled: currentPage === 1 }" @click="prevPage">
上一个
</div>
<div class="page-numbers">
<div
v-for="page in visiblePages"
:key="page"
class="page-num"
:class="{ active: currentPage === page }"
@click="goToPage(page)"
>
{{ page }}
</div>
</div>
<div class="page-btn" :class="{ disabled: currentPage === totalPages }" @click="nextPage">
下一个
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import { warningLevelOptions, regionOptions, isEndedOptions } from "./index.js";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close", "impactClick"]);
//
const filterForm = ref({
warningLevel: "",
region: "",
isEnded: "",
dateRange: [],
});
//
// index.js
//
// index.js
//
// index.js
//
const tableColumns = ref([
{ label: "序号", width: "50px" },
{ label: "预警等级", width: "100px" },
{ label: "气象类型", width: "100px" },
{ label: "行政区域", width: "100px" },
{ label: "预警时间", width: "160px" },
{ label: "结束时间", width: "160px" },
{ label: "影响点数量", flex: "1" },
]);
//
const tableData = ref([
{
id: 1,
warningLevel: "红色预警",
weatherType: "暴雨",
region: "重庆市",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactCount: 0,
},
{
id: 2,
warningLevel: "橙色预警",
weatherType: "暴雨",
region: "万州区",
warningTime: "2025-08-11 04:53:42",
endTime: "2025-08-11 04:53:42",
impactCount: 1,
},
{
id: 3,
warningLevel: "黄色预警",
weatherType: "大风",
region: "沙坪坝区",
warningTime: "2025-08-10 16:20:15",
endTime: "2025-08-10 20:30:00",
impactCount: 3,
},
{
id: 4,
warningLevel: "蓝色预警",
weatherType: "雷电",
region: "渝中区",
warningTime: "2025-08-09 09:15:30",
endTime: "2025-08-09 12:00:00",
impactCount: 2,
},
]);
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const visiblePages = computed(() => {
const pages = [];
const maxVisible = 5;
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
let end = Math.min(totalPages.value, start + maxVisible - 1);
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
});
//
const getWarningClass = (level) => {
const classMap = {
"红色预警": "warning-red",
"橙色预警": "warning-orange",
"黄色预警": "warning-yellow",
"蓝色预警": "warning-blue",
};
return classMap[level] || "";
};
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
//
const handleOverlayClick = () => {
handleClose();
};
//
const handleImpactClick = (item) => {
emit("impactClick", item);
};
//
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
fetchData();
}
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
fetchData();
}
};
const goToPage = (page) => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
.warning-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: 1000;
}
.warning-dialog {
width: 80vw;
max-width: 900px;
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);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
//
.filter-section {
margin-bottom: 16px;
.filter-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.filter-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.filter-select {
width: 120px;
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
width: 210px !important;
.el-input__inner {
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
.date-range-picker {
:deep(.el-input__wrapper) {
background-color: rgba(30, 70, 120, 0.4);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: none;
border-radius: 4px;
width: 210px !important;
.el-input__inner {
color: #fff;
font-size: 13px;
background: transparent;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
.el-input__suffix {
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
}
}
:deep(.el-range-input) {
background: transparent;
color: #fff;
font-size: 13px;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
:deep(.el-range-separator) {
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
}
}
}
}
//
.table-section {
background-color: rgba(30, 70, 120, 0.3);
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
.table-header {
display: flex;
background-color: rgba(64, 169, 255, 0.2);
padding: 12px 16px;
.th {
font-size: 14px;
font-weight: 500;
color: #fff;
text-align: center;
}
}
.table-body {
max-height: 320px;
overflow-y: auto;
.table-row {
display: flex;
padding: 14px 16px;
align-items: center;
transition: background-color 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
}
&.row-even {
background-color: rgba(30, 70, 120, 0.2);
}
.td {
font-size: 13px;
color: rgba(255, 255, 255, 0.85);
text-align: center;
//
.warning-level-tag {
display: inline-block;
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
&.warning-red {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
&.warning-orange {
background-color: rgba(255, 122, 0, 0.2);
color: #ff7a00;
border: 1px solid rgba(255, 122, 0, 0.4);
}
&.warning-yellow {
background-color: rgba(250, 219, 20, 0.2);
color: #fadb14;
border: 1px solid rgba(250, 219, 20, 0.4);
}
&.warning-blue {
background-color: rgba(64, 169, 255, 0.2);
color: #40a9ff;
border: 1px solid rgba(64, 169, 255, 0.4);
}
}
//
.impact-count {
color: #ff4d4f;
cursor: pointer;
font-weight: 600;
transition: all 0.3s;
&:hover {
color: #ff7875;
text-shadow: 0 0 8px rgba(255, 77, 79, 0.6);
}
}
}
}
}
}
//
.pagination {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
.page-btn {
padding: 6px 16px;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.disabled) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.5);
}
&.disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
.page-numbers {
display: flex;
gap: 8px;
.page-num {
min-width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s;
&:hover:not(.active) {
background-color: rgba(64, 169, 255, 0.2);
border-color: rgba(64, 169, 255, 0.5);
}
&.active {
background-color: #40a9ff;
border-color: #40a9ff;
color: #fff;
}
}
}
}
//
.table-body::-webkit-scrollbar {
width: 6px;
}
.table-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #40a9ff 0%, #1890ff 100%);
border-radius: 3px;
}
.table-body::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, #69c0ff 0%, #40a9ff 100%);
}
</style>
<style lang="scss">
//
.el-picker-panel {
background: rgba(20, 50, 90, 0.98) !important;
border: 1px solid rgba(64, 169, 255, 0.3) !important;
.el-picker-panel__content {
color: #fff;
.el-date-table th {
color: rgba(255, 255, 255, 0.6);
border-bottom-color: rgba(64, 169, 255, 0.3);
}
.el-date-table td {
color: rgba(255, 255, 255, 0.8);
&.selected .el-date-table-cell {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
}
&.today span {
color: #40a9ff;
font-weight: bold;
}
&.in-range,
&.start-date,
&.end-date {
background: rgba(64, 169, 255, 0.2);
}
}
}
.el-picker-panel__footer {
border-top: 1px solid rgba(64, 169, 255, 0.3);
.el-button {
background: rgba(30, 70, 120, 0.4);
border-color: rgba(64, 169, 255, 0.3);
color: #fff;
&:hover {
background: rgba(64, 169, 255, 0.3);
}
}
.el-button--primary {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
border: none;
&:hover {
background: linear-gradient(135deg, #69c0ff 0%, #40a9ff 100%);
}
}
}
}
</style>

View File

@ -249,7 +249,7 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted, provide } from "vue";
import useMapStore from "@/map/stores/mapStore"; import useMapStore from "@/map/stores/mapStore";
import { useMapBase } from "../cockpit/composables/useMapBase"; import { useMapBase } from "../cockpit/composables/useMapBase";
import left from "./left.vue"; import left from "./left.vue";
@ -259,26 +259,26 @@ import top from "./top.vue";
import ChongqingMap from "./component/ChongqingMap.vue"; import ChongqingMap from "./component/ChongqingMap.vue";
// //
import responseSituationDiaLog from "./component/responseSituationDiaLog.vue"; import responseSituationDiaLog from "./Dialog/responseSituationDiaLog.vue";
import warningInfoDialog from "./component/warningInfoDialog.vue"; import warningInfoDialog from "./Dialog/warningInfoDialog.vue";
import eventDetailDialog from "./component/eventDetailDialog.vue"; import eventDetailDialog from "./Dialog/eventDetailDialog.vue";
import confirmDialog from "./component/confirmDialog.vue"; import confirmDialog from "./Dialog/confirmDialog.vue";
import riskPointDetailDialog from "./component/riskPointDetailDialog.vue"; import riskPointDetailDialog from "./Dialog/riskPointDetailDialog.vue";
import impactPointDialog from "./component/impactPointDialog.vue"; import impactPointDialog from "./Dialog/impactPointDialog.vue";
import impactPointDetailDialog from "./component/impactPointDetailDialog.vue"; import impactPointDetailDialog from "./Dialog/impactPointDetailDialog.vue";
import responsePointDetailDialog from "./component/responsePointDetailDialog.vue"; import responsePointDetailDialog from "./Dialog/responsePointDetailDialog.vue";
import responsePointInfoDialog from "./component/responsePointInfoDialog.vue"; import responsePointInfoDialog from "./Dialog/responsePointInfoDialog.vue";
import responseStatusDialog from "./component/responseStatusDialog.vue"; import responseStatusDialog from "./Dialog/responseStatusDialog.vue";
import aiWarningResultDialog from "./component/aiWarningResultDialog.vue"; import aiWarningResultDialog from "./Dialog/aiWarningResultDialog.vue";
import tongnanInfoDialog from "./component/tongnanInfoDialog.vue"; import tongnanInfoDialog from "./Dialog/tongnanInfoDialog.vue";
import tongnanResponsibleDialog from "./component/tongnanResponsibleDialog.vue"; import tongnanResponsibleDialog from "./Dialog/tongnanResponsibleDialog.vue";
import clearanceSituationDialog from "./component/clearanceSituationDialog.vue"; import clearanceSituationDialog from "./Dialog/clearanceSituationDialog.vue";
import controlSituationDialog from "./component/controlSituationDialog.vue"; import controlSituationDialog from "./Dialog/controlSituationDialog.vue";
import dispatchDetailDialog from "./component/dispatchDetailDialog.vue"; import dispatchDetailDialog from "./Dialog/dispatchDetailDialog.vue";
import dispatchDistrictDialog from "./component/dispatchDistrictDialog.vue"; import dispatchDistrictDialog from "./Dialog/dispatchDistrictDialog.vue";
import tongnanTeamDialog from "./component/tongnanTeamDialog.vue"; import tongnanTeamDialog from "./Dialog/tongnanTeamDialog.vue";
import warningSituationDialog from "./component/warningSituationDialog.vue"; import warningSituationDialog from "./Dialog/warningSituationDialog.vue";
import tunnelInfoDialog from "./component/tunnelInfoDialog.vue"; import tunnelInfoDialog from "./Dialog/tunnelInfoDialog.vue";
// //
const dialogVisible = ref({ const dialogVisible = ref({
@ -300,8 +300,8 @@ const dialogVisible = ref({
dispatchDetail: false, dispatchDetail: false,
dispatchDistrict: false, dispatchDistrict: false,
tongnanTeam: false, tongnanTeam: false,
warningSituation: false, warningSituation: false,
tunnelInfo: false, tunnelInfo: false
}); });
// //
@ -333,6 +333,25 @@ const openConfirm = (config) => {
// //
const showCenterCard = ref(false); const showCenterCard = ref(false);
//
const refreshLeftData = ref(null);
//
const setRefreshLeftData = (callback) => {
refreshLeftData.value = callback;
};
//
const triggerRefreshLeftData = () => {
if (refreshLeftData.value) {
refreshLeftData.value();
}
};
//
provide('setRefreshLeftData', setRefreshLeftData);
provide('triggerRefreshLeftData', triggerRefreshLeftData);
// ==================== ==================== // ==================== ====================
const mapStore = useMapStore(); const mapStore = useMapStore();
@ -405,27 +424,27 @@ onMounted(() => {
.title_img2 { .title_img2 {
height: vw(40); height: vw(40);
min-height: 28px; min-height: 40px;
width: auto; width: auto;
max-width: vw(300); width: vw(550);
object-fit: contain; object-fit: contain;
} }
} }
.left { .left {
position: absolute; position: absolute;
left: 0; left: 0;
top: vw(90); top: vw(60);
width: 25%; width: 25%;
height: calc(100% - #{vw(90)}); height: calc(100% - #{vw(60)});
z-index: 2; z-index: 2;
} }
.right { .right {
position: absolute; position: absolute;
right: 0; right: 0;
top: vw(90); top: vw(60);
width: 25%; width: 25%;
height: calc(100% - #{vw(90)}); height: calc(100% - #{vw(60)});
z-index: 2; z-index: 2;
} }
@ -485,14 +504,14 @@ onMounted(() => {
pointer-events: none; pointer-events: none;
&.corner-top-left { &.corner-top-left {
top: vw(110); top: vw(60);
left: vw(10); left: vw(10);
border-right: none; border-right: none;
border-bottom: none; border-bottom: none;
} }
&.corner-top-right { &.corner-top-right {
top: vw(110); top: vw(60);
right: vw(10); right: vw(10);
border-left: none; border-left: none;
border-bottom: none; border-bottom: none;
@ -574,7 +593,7 @@ onMounted(() => {
} }
.info-unit { .info-unit {
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.6); color: rgba(255, 255, 255, 0.6);
} }

View File

@ -57,10 +57,10 @@
> >
<div <div
class="bar" class="bar"
:style="{ height: getBarHeight(item.value) + '%' }" :style="{ height: getBarHeight(item) + '%' }"
></div> ></div>
<div class="bar-value">{{ item.value }}</div> <div class="bar-value">{{ item.count }}</div>
<div class="bar-label">{{ item.label }}</div> <div class="bar-label">{{ item.name }}</div>
</div> </div>
</div> </div>
<div class="chart-x-label">类型</div> <div class="chart-x-label">类型</div>
@ -71,13 +71,13 @@
<div class="road-type-section"> <div class="road-type-section">
<div class="section-title">影响公路类型情况</div> <div class="section-title">影响公路类型情况</div>
<div class="road-type-cards"> <div class="road-type-cards">
<div class="road-card"> <div
<span class="card-label">国省道</span> class="road-card"
<span class="card-value">100</span> v-for="(item, index) in roadTypeData"
</div> :key="index"
<div class="road-card"> >
<span class="card-label">农村公路</span> <span class="card-label">{{ item.extension }}</span>
<span class="card-value">300</span> <span class="card-value">{{ item.count }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -99,11 +99,15 @@
:scroll="{ y: true }" :scroll="{ y: true }"
> >
<el-table-column prop="name" label="区县名称" :min-width="vw(80)" /> <el-table-column prop="name" label="区县名称" :min-width="vw(80)" />
<el-table-column prop="road" label="路段" :min-width="vw(50)" /> <el-table-column
<el-table-column prop="bridge" label="桥梁" :min-width="vw(50)" /> prop="roadSectionCount"
<el-table-column prop="tunnel" label="隧道" :min-width="vw(50)" /> label="路段"
<el-table-column prop="slope" label="边坡" :min-width="vw(50)" /> :min-width="vw(50)"
<el-table-column prop="project" label="项目" :min-width="vw(50)" /> />
<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> </el-table>
</div> </div>
@ -123,6 +127,7 @@
size="small" size="small"
popper-class="dark-date-picker" popper-class="dark-date-picker"
:prefix-icon="Calendar" :prefix-icon="Calendar"
@change="handleDateChange"
/> />
</div> </div>
</div> </div>
@ -157,13 +162,7 @@
<div <div
v-for="(item, index) in dispatchList" v-for="(item, index) in dispatchList"
:key="index" :key="index"
class="dispatch-card" class="dispatch-card clickable"
:class="{
clickable:
item.label === '国省道调度清单' ||
item.label === '农村公路调度清单' ||
item.label === '建设工程调度清单',
}"
@click="handleDispatchCardClick(item)" @click="handleDispatchCardClick(item)"
> >
<div class="card-num"> <div class="card-num">
@ -177,10 +176,55 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted } from "vue"; import { ref, computed, onMounted, watch, inject } from "vue";
import { request } from "@/utils/request"; import { request } from "@/utils/request";
import SectionHeader from "./component/sectionHeader.vue"; import SectionHeader from "./component/sectionHeader.vue";
//
import imgCall from "../../assets/RiskWarning_img/回应icon@2x.png";
import imgReply from "../../assets/RiskWarning_img/已回应icon@2x.png";
import imgRate from "../../assets/RiskWarning_img/回应率icon@2x.png";
import imgDistrict from "../../assets/RiskWarning_img/区县icon@2x.png";
import imgHelp from "../../assets/RiskWarning_img/响应图标5@2x.png";
import imgCheck from "../../assets/RiskWarning_img/抽查人数icon@2x.png";
//
const setRefreshLeftData = inject('setRefreshLeftData');
const props = defineProps({
visible: {
type: Boolean,
default: true,
},
});
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
// loadData();
// loadBarChartData();
}
},
);
onMounted(() => {
if (props.visible) {
init();
}
//
if (setRefreshLeftData) {
setRefreshLeftData(init);
}
});
const init = () => {
roadTypeLoad();
districtLoadLoad();
dispatchLoadLoad();
loadData();
loadBarChartData();
};
const emit = defineEmits([ const emit = defineEmits([
"openWarningInfo", "openWarningInfo",
@ -215,94 +259,248 @@ const handleImpactDetailClick = () => {
// //
const handleDispatchCardClick = (item) => { const handleDispatchCardClick = (item) => {
if (item.label === "建设工程调度清单") { if (item.label === dispatchList.value[0].label) {
emit("showCenterCard", {
type: "first",
value: item.value,
});
} else if (item.label === "国省道调度清单") {
emit("showCenterCard", { emit("showCenterCard", {
type: "second", type: "second",
value: item.value, value: item.value,
}); });
} else if (item.label === "农村公路调度清单") { } else if (item.label === dispatchList.value[1].label) {
emit("showCenterCard", { emit("showCenterCard", {
type: "third", type: "third",
value: item.value, value: item.value,
}); });
} else if (item.label === dispatchList.value[2].label) {
emit("showCenterCard", {
type: "first",
value: item.value,
});
} }
}; };
//
import imgCall from "../../assets/RiskWarning_img/回应icon@2x.png";
import imgReply from "../../assets/RiskWarning_img/已回应icon@2x.png";
import imgRate from "../../assets/RiskWarning_img/回应率icon@2x.png";
import imgDistrict from "../../assets/RiskWarning_img/区县icon@2x.png";
import imgHelp from "../../assets/RiskWarning_img/响应图标5@2x.png";
import imgCheck from "../../assets/RiskWarning_img/抽查人数icon@2x.png";
// //
const weatherWarningData = ref([ const weatherWarningData = ref([
{ label: "红色预警", value: "", class: "red" }, { label: "红色预警", value: "0", class: "red" },
{ label: "橙色预警", value: "", class: "orange" }, { label: "橙色预警", value: "0", class: "orange" },
{ label: "黄色预警", value: "", class: "yellow" }, { label: "黄色预警", value: "0", class: "yellow" },
{ label: "蓝色预警", value: "", class: "blue" }, { label: "蓝色预警", value: "0", class: "blue" },
]); ]);
// //
const impactData = ref([ const impactData = ref([
{ label: "路段", value: 830 }, { name: "路段", count: 830 },
{ label: "桥梁", value: 312 }, { name: "桥梁", count: 312 },
{ label: "隧道", value: 405 }, { name: "隧道", count: 405 },
{ label: "边坡", value: 634 }, { name: "边坡", count: 634 },
{ label: "项目", value: 523 }, { name: "项目", count: 523 },
]); ]);
// //
const handleDateChange = (val) => {
dateRange.value = val;
dispatchLoadLoad();
};
//
const dateRange = ref([]); const dateRange = ref([]);
//
const dispatchLoadLoad = async () => {
try {
// 00:00:0023:59:59
let startTime = "";
let endTime = "";
if (dateRange.value && dateRange.value.length === 2) {
const startDate = new Date(dateRange.value[0]);
const endDate = new Date(dateRange.value[1]);
// 00:00:00
startDate.setHours(0, 0, 0, 0);
startTime = startDate.getTime();
// 23:59:59
endDate.setHours(23, 59, 59, 999);
endTime = endDate.getTime();
}
const res = await request({
url: "/snow-ops-platform/weather-warning/schedule-statistics",
method: "GET",
params: {
start: startTime,
end: endTime,
},
});
console.log(res);
if (res.code == "00000") {
const data = res.data;
if (data) {
responseStats.value.forEach((item) => {
if (item.label == "叫应总数") {
item.value = data["noticeCount"] || 0;
} else if (item.label == "已回应数") {
item.value = data["replyCount"] || 0;
} else if (item.label == "回应率") {
item.value =
data["replyPrecent"] >= 0 ? data["replyPrecent"] + "%" : 0;
} else if (item.label == "调度区县数") {
item.value = data["countyCount"] || 0;
} else if (item.label == "线下帮扶数") {
item.value = data["supportCount"] || 0;
} else if (item.label == "抽查人次") {
item.value = data["inspectionCount"] || 0;
}
});
dispatchList.value.forEach((item) => {
if (item.label == "国省道调度") {
item.value = data["nationalRoadCount"] || 0;
} else if (item.label == "农村公路调度") {
item.value = data["ruralRoadCount"] || 0;
} else if (item.label == "建设工程调度") {
item.value = data["projectCount"] || 0;
}
});
// responseStats.value = data;
// dispatchList.value = data;
}
}
} catch (error) {
console.error("加载数据失败:", error);
}
};
//
const districtLoadLoad = async () => {
try {
const res = await request({
url: "/snow-ops-platform/weather-warning/affected-count/_by_county",
method: "GET",
});
console.log(res);
if (res.code == "00000") {
const data = res.data;
if (data) {
districtData.value = data;
}
}
} catch (error) {
console.error("加载数据失败:", error);
}
};
const roadTypeData = ref([]);
//
const roadTypeLoad = async () => {
try {
const res = await request({
url: "/snow-ops-platform/weather-warning/affected-count/_by_road",
method: "GET",
});
console.log(res);
if (res.code == "00000") {
const data = res.data;
if (data) {
roadTypeData.value = data.reverse();
}
}
} catch (error) {
console.error("加载数据失败:", error);
}
};
// //
const loadData = async () => { const loadData = async () => {
try { try {
const res = await request({ const res = await request({
url: "/snow-ops-platform/weatherWarning/statistics", url: "/snow-ops-platform/weather-warning/warning-count",
method: "GET", method: "GET",
}); });
if (res.code == "00000") { if (res.code == "00000") {
const data = res.data; const data = res.data;
if (data) { if (data) {
let obj = {};
data.forEach((item) => {
obj[item.name] = item.count || 0;
});
console.log(obj);
weatherWarningData.value.forEach((item) => { weatherWarningData.value.forEach((item) => {
if (item.label == "红色预警") { if (item.label == "红色预警") {
item.value = data.redWarningCount || 0; item.value =
obj["warning-red-count"] + "/" + obj["warning-red-total"] ||
"0/0";
} else if (item.label == "橙色预警") { } else if (item.label == "橙色预警") {
item.value = data.orangeWarningCount || 0; item.value =
obj["warning-orange-count"] + "/" + obj["warning-orange-total"] ||
"0/0";
} else if (item.label == "黄色预警") { } else if (item.label == "黄色预警") {
item.value = data.yellowWarningCount || 0; item.value =
obj["warning-yellow-count"] + "/" + obj["warning-yellow-total"] ||
"0/0";
} else if (item.label == "蓝色预警") { } else if (item.label == "蓝色预警") {
item.value = data.blueWarningCount || 0; item.value =
obj["warning-blue-count"] + "/" + obj["warning-blue-total"] ||
"0/0";
} }
}); });
} }
if (data.impactData) {
impactData.value = data.impactData;
}
} }
} catch (error) { } catch (error) {
console.error("加载数据失败:", error); console.error("加载数据失败:", error);
} }
}; };
//
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: "项目",
};
// name
const convertedData = data.map((item) => {
const name = nameMap[item.name] || item.name;
return { ...item, name };
});
impactData.value = convertedData;
}
}
} catch (error) {
console.error("加载柱状图数据失败:", error);
}
};
// //
const maxValue = computed(() => { //
return Math.max(...impactData.value.map((item) => item.value)); const totalValue = computed(() => {
let total = 0;
impactData.value.forEach((item) => {
total += item.count || 0;
});
return total;
}); });
// //
const getBarHeight = (value) => { const getBarHeight = (value) => {
// 20%100% const actualValue = value.count || value.value;
const height = (value / maxValue) * 100; // 10%100%
return Math.min(100, Math.max(20, Math.round(height))); const height = (actualValue / totalValue.value) * 200;
// 5%
return Math.min(100, Math.max(10, Math.round(height)));
}; };
const handleAIClick = () => { const handleAIClick = () => {
emit("openAIResult"); emit("openAIResult");
@ -319,17 +517,22 @@ const tableHeight = computed(() => {
}); });
// //
const districtData = [ const districtData = ref([
{ name: "江北区", road: 1, bridge: 1, tunnel: 1, slope: 8, project: 11 }, { extension: "江北区", road: 1, bridge: 1, tunnel: 1, slope: 8, project: 11 },
{ name: "江北区", road: 1, bridge: 1, tunnel: 1, slope: 8, project: 11 }, { extension: "南岸区", road: 1, bridge: 2, tunnel: 2, slope: 6, project: 12 },
{ name: "南岸区", road: 1, bridge: 2, tunnel: 2, slope: 6, project: 12 }, {
{ name: "九龙坡区", road: 2, bridge: 1, tunnel: 1, slope: 9, project: 9 }, extension: "九龙坡区",
{ name: "九龙坡区", road: 2, bridge: 1, tunnel: 1, slope: 9, project: 9 }, road: 2,
{ name: "万州区", road: 1, bridge: 2, tunnel: 2, slope: 11, project: 7 }, bridge: 1,
]; tunnel: 1,
slope: 9,
project: 9,
},
{ extension: "万州区", road: 1, bridge: 2, tunnel: 2, slope: 11, project: 7 },
]);
// //
const responseStats = [ const responseStats = ref([
{ {
label: "叫应总数", label: "叫应总数",
value: "15", value: "15",
@ -366,14 +569,14 @@ const responseStats = [
iconClass: "icon-check", iconClass: "icon-check",
img: imgCheck, img: imgCheck,
}, },
]; ]);
// //
const dispatchList = [ const dispatchList = ref([
{ label: "国省道调度清单", value: "341" }, { label: "国省道调度", value: "341" },
{ label: "农村公路调度清单", value: "210" }, { label: "农村公路调度", value: "210" },
{ label: "建设工程调度清单", value: "120" }, { label: "建设工程调度", value: "120" },
]; ]);
const headerCellStyle = () => ({ const headerCellStyle = () => ({
background: "rgba(64, 169, 255, 0.1)", background: "rgba(64, 169, 255, 0.1)",
@ -393,10 +596,6 @@ const cellStyle = () => ({
textAlign: "center", textAlign: "center",
lineHeight: "1.2", lineHeight: "1.2",
}); });
onMounted(() => {
loadData();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -534,7 +733,7 @@ onMounted(() => {
} }
.card-label { .card-label {
font-size: vw(12); font-size: vw(16);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
} }
} }
@ -596,7 +795,7 @@ onMounted(() => {
position: absolute; position: absolute;
left: 0; left: 0;
top: vw(-10); top: vw(-10);
font-size: vw(11); font-size: vw(14);
color: rgba(255, 255, 255, 0.6); color: rgba(255, 255, 255, 0.6);
} }
@ -661,7 +860,7 @@ onMounted(() => {
} }
.bar-value { .bar-value {
font-size: vw(12); font-size: vw(14);
font-weight: bold; font-weight: bold;
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.9);
margin-bottom: vw(5); margin-bottom: vw(5);
@ -677,13 +876,13 @@ onMounted(() => {
rgba(64, 169, 255, 0.3) 100% rgba(64, 169, 255, 0.3) 100%
); );
border-radius: 2px 2px 0 0; border-radius: 2px 2px 0 0;
min-height: 20px; // min-height: 20px;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.bar-label { .bar-label {
bottom: vw(-20); bottom: vw(-20);
font-size: vw(10); font-size: vw(14);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
transition: all 0.3s ease; transition: all 0.3s ease;
position: absolute; position: absolute;
@ -695,7 +894,7 @@ onMounted(() => {
position: absolute; position: absolute;
right: vw(-15); right: vw(-15);
bottom: 0; bottom: 0;
font-size: vw(10); font-size: vw(14);
color: rgba(255, 255, 255, 0.6); color: rgba(255, 255, 255, 0.6);
} }
} }
@ -831,7 +1030,7 @@ onMounted(() => {
&::before { &::before {
content: "←"; content: "←";
color: #fff; color: #fff;
font-size: vw(12); font-size: vw(14);
} }
} }
} }
@ -911,7 +1110,7 @@ onMounted(() => {
} }
.stat-label { .stat-label {
font-size: vw(12); font-size: vw(14);
color: #ffffff; color: #ffffff;
} }
} }
@ -967,7 +1166,7 @@ onMounted(() => {
margin-bottom: vw(6); margin-bottom: vw(6);
.unit { .unit {
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
font-weight: normal; font-weight: normal;
margin-left: 2px; margin-left: 2px;
@ -975,7 +1174,7 @@ onMounted(() => {
} }
.card-label { .card-label {
font-size: vw(10); font-size: vw(14);
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
} }
} }

View File

@ -472,7 +472,7 @@ const majorEvent = "0";
align-items: center; align-items: center;
.resource-label { .resource-label {
width: 48%; width: 48%;
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
margin-bottom: vw(2); margin-bottom: vw(2);
} }
@ -483,7 +483,7 @@ const majorEvent = "0";
color: #18F2F9; color: #18F2F9;
.unit { .unit {
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
font-weight: normal; font-weight: normal;
margin-left: 2px; margin-left: 2px;
@ -516,8 +516,11 @@ const majorEvent = "0";
flex: 1; flex: 1;
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
background: rgba(62, 106, 172, 0.36); // background: rgba(62, 106, 172, 0.36);
box-shadow: inset 0px 0px 8px 0px #379bff; // box-shadow: inset 0px 0px 8px 0px #379bff;
background-image: url("../../assets/RiskWarning_img/路径 62@2x.png");
background-size: 100% 100%;
background-position: right;
gap: vw(8); gap: vw(8);
.control-item { .control-item {
@ -544,7 +547,7 @@ const majorEvent = "0";
} }
.control-label { .control-label {
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
} }
} }
@ -597,7 +600,7 @@ const majorEvent = "0";
} }
.patrol-label { .patrol-label {
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
} }
} }
@ -667,7 +670,7 @@ const majorEvent = "0";
} }
.rescue-label { .rescue-label {
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
} }
} }
@ -755,7 +758,7 @@ const majorEvent = "0";
} }
.block-label { .block-label {
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
} }
@ -768,7 +771,7 @@ const majorEvent = "0";
} }
.death-label { .death-label {
font-size: vw(10); font-size: vw(14);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
} }
} }
@ -805,14 +808,14 @@ const majorEvent = "0";
margin-bottom: vw(6); margin-bottom: vw(6);
.unit { .unit {
font-size: vw(12); font-size: vw(14);
font-weight: normal; font-weight: normal;
margin-left: 2px; margin-left: 2px;
} }
} }
.damage-label { .damage-label {
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
} }
@ -843,7 +846,7 @@ const majorEvent = "0";
background-position: left; background-position: left;
.event-title { .event-title {
font-size: vw(12); font-size: vw(14);
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.9);
} }

View File

@ -20,13 +20,28 @@
</template> </template>
<script setup> <script setup>
import { ref } from "vue"; import { ref, watch, inject } from "vue";
import { Calendar } from "@element-plus/icons-vue"; import { Calendar } from "@element-plus/icons-vue";
const emit = defineEmits(["openAIResult"]); const emit = defineEmits(["openAIResult"]);
//
const triggerRefreshLeftData = inject('triggerRefreshLeftData');
const dateRange = ref([]); const dateRange = ref([]);
// dateRange
watch(dateRange, (newVal, oldVal) => {
//
if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
console.log('dateRange 发生变化:', newVal);
//
if (triggerRefreshLeftData) {
triggerRefreshLeftData();
}
}
}, { deep: true });
const handleAIClick = () => { const handleAIClick = () => {
emit("openAIResult"); emit("openAIResult");
}; };
@ -88,8 +103,8 @@ const handleAIClick = () => {
height: 2.6em !important; height: 2.6em !important;
} }
:deep(.el-date-editor) { :deep(.el-date-editor) {
width: vw(200); width: 180px;
max-width: vw(200); max-width: vw(350);
background: #183c67; background: #183c67;
box-shadow: inset 0px 0px 8px 0px #4fecff; box-shadow: inset 0px 0px 8px 0px #4fecff;