Merge branch 'dev' of http://222.212.85.86:8222/bdzl2/bxztApp into dev
This commit is contained in:
commit
27ff5b5764
@ -1,14 +1,24 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="viewport"
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover" />
|
||||||
<title>H5移动端</title>
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
</head>
|
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||||
<body>
|
<title>H5移动端</title>
|
||||||
<div id="app"></div>
|
<script>
|
||||||
<script type="module" src="/src/main.js"></script>
|
window._AMapSecurityConfig = {
|
||||||
</body>
|
securityJsCode: "08c037da44c78afd7338203268c2d2a5"
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script src="https://webapi.amap.com/loader.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -211,6 +211,7 @@ import { ref, onMounted, reactive, toRaw, watch } from "vue";
|
|||||||
import { useRouter, useRoute } from "vue-router";
|
import { useRouter, useRoute } from "vue-router";
|
||||||
import { showToast, showLoadingToast } from "vant";
|
import { showToast, showLoadingToast } from "vant";
|
||||||
import { request } from "../../../../shared/utils/request";
|
import { request } from "../../../../shared/utils/request";
|
||||||
|
import { loadAMap } from "../../../../shared/utils/aMap";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@ -218,7 +219,7 @@ const searchValue = ref(""); // 搜索框输入值
|
|||||||
const showPopup = ref(false); // 控制弹出层显示隐藏
|
const showPopup = ref(false); // 控制弹出层显示隐藏
|
||||||
const yhzDetail = ref({}); // 养护站详情数据
|
const yhzDetail = ref({}); // 养护站详情数据
|
||||||
const materialList = ref([]); // 物资列表数据
|
const materialList = ref([]); // 物资列表数据
|
||||||
const INIT_FORM = {
|
const getInitForm = () => ({
|
||||||
material: {
|
material: {
|
||||||
jd: "", // 物资经度
|
jd: "", // 物资经度
|
||||||
wd: "", // 物资纬度
|
wd: "", // 物资纬度
|
||||||
@ -238,8 +239,8 @@ const INIT_FORM = {
|
|||||||
remark: "", // 备注
|
remark: "", // 备注
|
||||||
},
|
},
|
||||||
photos: [],
|
photos: [],
|
||||||
};
|
});
|
||||||
const form = reactive({ ...INIT_FORM }); // 表单
|
const form = reactive(getInitForm());
|
||||||
|
|
||||||
// 根据养护站rid获取物资列表
|
// 根据养护站rid获取物资列表
|
||||||
const getMaterialList = async (wzmc) => {
|
const getMaterialList = async (wzmc) => {
|
||||||
@ -281,15 +282,6 @@ onMounted(() => {
|
|||||||
|
|
||||||
// 购置日期相关
|
// 购置日期相关
|
||||||
const showTimePicker = ref(false);
|
const showTimePicker = ref(false);
|
||||||
const currentDate = ref([
|
|
||||||
new Date().getFullYear(),
|
|
||||||
new Date().getMonth() + 1,
|
|
||||||
new Date().getDate(),
|
|
||||||
]);
|
|
||||||
const onDateConfirm = ({ selectedValues }) => {
|
|
||||||
form.rkrq = selectedValues.join("-");
|
|
||||||
showTimePicker.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 选择单位相关
|
// 选择单位相关
|
||||||
const dwField = ref(null);
|
const dwField = ref(null);
|
||||||
@ -338,7 +330,6 @@ const handleSubmit = async () => {
|
|||||||
message: "新增成功",
|
message: "新增成功",
|
||||||
});
|
});
|
||||||
onPopupClose();
|
onPopupClose();
|
||||||
Object.assign(form, { ...INIT_FORM });
|
|
||||||
getMaterialList(searchValue.value);
|
getMaterialList(searchValue.value);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(res.message);
|
throw new Error(res.message);
|
||||||
@ -423,37 +414,38 @@ const afterRead = async (file) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 获取经纬度
|
// 获取经纬度
|
||||||
const handleGetLocation = () => {
|
const handleGetLocation = async () => {
|
||||||
if (!navigator.geolocation) {
|
// 确保AMap完成加载
|
||||||
showToast("您的浏览器不支持地理位置获取");
|
if (!window.AMap) {
|
||||||
return;
|
await loadAMap();
|
||||||
}
|
}
|
||||||
|
const loadingToast = showLoadingToast({
|
||||||
showLoadingToast({
|
message: "正在获取位置",
|
||||||
message: "定位中...",
|
|
||||||
forbidClick: true,
|
forbidClick: true,
|
||||||
|
duration: 0, // 设置为0表示不会自动关闭
|
||||||
|
zIndex: 9999,
|
||||||
});
|
});
|
||||||
|
|
||||||
navigator.geolocation.getCurrentPosition(
|
AMap.plugin("AMap.Geolocation", () => {
|
||||||
(position) => {
|
const geolocation = new AMap.Geolocation({
|
||||||
form.material.jd = position.coords.longitude.toFixed(6);
|
enableHighAccuracy: true,
|
||||||
form.material.wd = position.coords.latitude.toFixed(6);
|
timeout: 5000,
|
||||||
},
|
showMarker: false,
|
||||||
(error) => {
|
zoomToAccuracy: true,
|
||||||
const errorMessage =
|
});
|
||||||
{
|
|
||||||
1: "位置服务被拒绝",
|
geolocation.getCurrentPosition((status, result) => {
|
||||||
2: "暂时无法获取位置",
|
if (status === "complete") {
|
||||||
3: "定位超时",
|
form.material.jd = result.position.lng.toFixed(6);
|
||||||
}[error.code] || "定位失败";
|
form.material.wd = result.position.lat.toFixed(6);
|
||||||
showToast(errorMessage);
|
loadingToast.close();
|
||||||
},
|
} else {
|
||||||
{
|
loadingToast.close();
|
||||||
enableHighAccuracy: true, // 高精度模式
|
showToast(result);
|
||||||
timeout: 5000, // 超时时间
|
console.log("result", result);
|
||||||
maximumAge: 0, // 不缓存位置
|
}
|
||||||
}
|
});
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@ -505,6 +497,9 @@ const handleAdd = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onPopupClose = () => {
|
const onPopupClose = () => {
|
||||||
|
Object.assign(form, getInitForm());
|
||||||
|
fileList.value = [];
|
||||||
|
[showDwPicker, showFzrPicker].forEach((v) => (v.value = false));
|
||||||
showPopup.value = false;
|
showPopup.value = false;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -9,17 +9,18 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.5.18",
|
|
||||||
"vue-router": "^4.6.3",
|
|
||||||
"pinia": "^3.0.3",
|
|
||||||
"element-plus": "^2.11.5",
|
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
"echarts": "^6.0.0",
|
"@h5/shared": "workspace:*",
|
||||||
"vue-echarts": "^8.0.1",
|
"@turf/turf": "^7.3.0",
|
||||||
"cesium": "^1.135.0",
|
|
||||||
"axios": "^1.13.2",
|
|
||||||
"@vueuse/core": "^14.0.0",
|
"@vueuse/core": "^14.0.0",
|
||||||
"@h5/shared": "workspace:*"
|
"axios": "^1.13.2",
|
||||||
|
"cesium": "^1.135.0",
|
||||||
|
"echarts": "^6.0.0",
|
||||||
|
"element-plus": "^2.11.5",
|
||||||
|
"pinia": "^3.0.3",
|
||||||
|
"vue": "^3.5.18",
|
||||||
|
"vue-echarts": "^8.0.1",
|
||||||
|
"vue-router": "^4.6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
|
|||||||
@ -10,6 +10,9 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/cockpit',
|
path: '/cockpit',
|
||||||
name: 'Cockpit',
|
name: 'Cockpit',
|
||||||
|
meta: {
|
||||||
|
screen: true
|
||||||
|
},
|
||||||
component: () => import('../views/cockpit/index.vue')
|
component: () => import('../views/cockpit/index.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { request } from '@shared/utils/request'
|
|||||||
|
|
||||||
// 获取业务底图
|
// 获取业务底图
|
||||||
export function getBaseMap() {
|
export function getBaseMap() {
|
||||||
// return [...ddt]
|
|
||||||
return request({
|
return request({
|
||||||
url: '/snow-ops-platform/dataDirectory/queryCatalog',
|
url: '/snow-ops-platform/dataDirectory/queryCatalog',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@ -14,7 +13,6 @@ export function getBaseMap() {
|
|||||||
|
|
||||||
// 获取业务图
|
// 获取业务图
|
||||||
export function getBusinessMap() {
|
export function getBusinessMap() {
|
||||||
// return [...si]
|
|
||||||
return request({
|
return request({
|
||||||
url: '/snow-ops-platform/dataDirectory/queryCatalog',
|
url: '/snow-ops-platform/dataDirectory/queryCatalog',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@ -24,6 +22,48 @@ export function getBusinessMap() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取所有的养护站
|
||||||
|
export function getAllYHZList() {
|
||||||
|
return request({
|
||||||
|
url: '/snow-ops-platform/yhz/listByDistrict',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
qxmc: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function getYHZDetail(params) {
|
||||||
|
return request({
|
||||||
|
url: '/snow-ops-platform/yhz/getById',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得高海拔路段图 high-altitude road
|
||||||
|
export function getHighAltitudeRoadMap () {
|
||||||
|
return request({
|
||||||
|
url: '/snow-ops-platform/dataDirectory/queryCatalog',
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
pcatalog: 'GHBMAP'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取天气预警统计
|
||||||
|
export function getWeatherWarningStatistics (params) {
|
||||||
|
return request({
|
||||||
|
url: '/snow-ops-platform/weatherWarning/statistics',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 183 KiB |
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cockpit-layout">
|
<div class="cockpit-layout">
|
||||||
<!-- <PageHeader /> -->
|
<!-- <PageHeader /> -->
|
||||||
|
<PageHeader />
|
||||||
<div class="cockpit-main">
|
<div class="cockpit-main">
|
||||||
<!-- 地图底层 -->
|
<!-- 地图底层 -->
|
||||||
<div class="map-layer">
|
<div class="map-layer">
|
||||||
@ -13,8 +14,8 @@
|
|||||||
<!-- 浮动面板层 -->
|
<!-- 浮动面板层 -->
|
||||||
<div class="panels-layer">
|
<div class="panels-layer">
|
||||||
<div class="panel-column left-panel">
|
<div class="panel-column left-panel">
|
||||||
<WeatherWarning />
|
<WeatherWarning ref="weatherWarningRef" />
|
||||||
<EmergencyResources />
|
<EmergencyResources @clickEmergencyResource="handleEmergencyResourceClick" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="center-spacer" aria-hidden="true"></div>
|
<div class="center-spacer" aria-hidden="true"></div>
|
||||||
@ -26,17 +27,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 图例工具栏 -->
|
<!-- 图例工具栏 -->
|
||||||
<LegendToolbar class="legend-toolbar" :legendKeys="legendKeys" @marker-toggle="handleLegendMarkerToggle" />
|
<LegendToolbar class="legend-toolbar" v-model="legendToolActiveItem" :legendKeys="legendKeys"
|
||||||
|
@marker-toggle="handleLegendMarkerToggle" />
|
||||||
|
|
||||||
<!-- 应急力量详情提示框 -->
|
<!-- 应急力量详情提示框 -->
|
||||||
<EmergencyForceTooltip
|
<EmergencyForceTooltip :visible="emergencyForceInteraction.tooltipVisible.value"
|
||||||
:visible="emergencyForceInteraction.tooltipVisible.value"
|
:position="emergencyForceInteraction.tooltipPosition.value" :data="emergencyForceInteraction.tooltipData.value"
|
||||||
:position="emergencyForceInteraction.tooltipPosition.value"
|
:loading="emergencyForceInteraction.loading.value" :error="emergencyForceInteraction.error.value"
|
||||||
:data="emergencyForceInteraction.tooltipData.value"
|
@close="emergencyForceInteraction.hideTooltip" />
|
||||||
:loading="emergencyForceInteraction.loading.value"
|
|
||||||
:error="emergencyForceInteraction.error.value"
|
|
||||||
@close="emergencyForceInteraction.hideTooltip"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -60,9 +58,11 @@ import LegendToolbar from './LegendToolbar.vue'
|
|||||||
import EmergencyForceTooltip from './EmergencyForceTooltip.vue'
|
import EmergencyForceTooltip from './EmergencyForceTooltip.vue'
|
||||||
import emergencyForceMarkerIcon from '../assets/legendTool/应急力量icon定位.png'
|
import emergencyForceMarkerIcon from '../assets/legendTool/应急力量icon定位.png'
|
||||||
|
|
||||||
onMounted(()=>{
|
onMounted(() => {
|
||||||
// 加载地图业务底图 并 聚焦中心点
|
// 加载地图业务底图 并 聚焦中心点
|
||||||
mapBase.loadBaseData()
|
mapBase.loadBaseData()
|
||||||
|
|
||||||
|
weatherWarningRef.value.loadData()
|
||||||
})
|
})
|
||||||
|
|
||||||
// ==================== 常量定义 ====================
|
// ==================== 常量定义 ====================
|
||||||
@ -79,6 +79,9 @@ const EMERGENCY_FORCE_LAYER_ID = 'legend:emergencyForce'
|
|||||||
*/
|
*/
|
||||||
const EMERGENCY_FORCE_CACHE_TTL = 60 * 1000
|
const EMERGENCY_FORCE_CACHE_TTL = 60 * 1000
|
||||||
|
|
||||||
|
// ==================== 组件实例 ====================
|
||||||
|
const weatherWarningRef = ref(null)
|
||||||
|
|
||||||
// ==================== 状态管理 ====================
|
// ==================== 状态管理 ====================
|
||||||
|
|
||||||
const mapStore = useMapStore()
|
const mapStore = useMapStore()
|
||||||
@ -101,7 +104,7 @@ const mapBase = useMapBase(mapStore)
|
|||||||
/**
|
/**
|
||||||
* 标记图hook,搭配lengendToolbar使用, 作用是点击某个图例项时,在地图上显示所有该图例的图
|
* 标记图hook,搭配lengendToolbar使用, 作用是点击某个图例项时,在地图上显示所有该图例的图
|
||||||
*/
|
*/
|
||||||
const mapImageMark = useMapImageMark(mapStore)
|
const mapImageMarkHook = useMapImageMark(mapStore)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应急力量数据加载状态
|
* 应急力量数据加载状态
|
||||||
@ -131,8 +134,21 @@ const emergencyForceAbortController = ref(null)
|
|||||||
/**
|
/**
|
||||||
* 工具图标列表通过key关联
|
* 工具图标列表通过key关联
|
||||||
* key具体有哪些,请根据LegendToolbar.vue中定义的defaultLegendItems
|
* key具体有哪些,请根据LegendToolbar.vue中定义的defaultLegendItems
|
||||||
|
* 后续的ImageMarkData中也需要与这里的key值对应
|
||||||
*/
|
*/
|
||||||
const legendKeys = ref(['serviceFacility','riskRoad','blockEvent','weatherAlert','emergencyForce'])
|
const legendKeys = ref(['serviceFacility', 'riskRoad', 'blockEvent', 'weatherAlert', 'emergencyForce'])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤标记对象,存放当前需要过滤的关键字
|
||||||
|
*/
|
||||||
|
const filterMark = ref({
|
||||||
|
serviceFacility: []
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图例项
|
||||||
|
*/
|
||||||
|
const legendToolActiveItem = ref([])
|
||||||
|
|
||||||
// ==================== 工具函数 ====================
|
// ==================== 工具函数 ====================
|
||||||
|
|
||||||
@ -413,6 +429,48 @@ const renderEmergencyForcePoints = async (entityService, points, markerIcon) =>
|
|||||||
|
|
||||||
// ==================== 事件处理 ====================
|
// ==================== 事件处理 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应急资源表格行被点击时触发
|
||||||
|
*/
|
||||||
|
const handleEmergencyResourceClick = (resource) => {
|
||||||
|
let focusKey = null
|
||||||
|
|
||||||
|
// 根据区县名称,过滤养护站
|
||||||
|
const filterList = filterMark.value.serviceFacility
|
||||||
|
const index = filterList.findIndex((item) => item == resource.qxmc)
|
||||||
|
// 实现第一次点击表示选中,第二次点击表示取消选中
|
||||||
|
if (index > -1) {
|
||||||
|
filterList.splice(index, 1)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filterList.push(resource.qxmc)
|
||||||
|
focusKey = resource.qxmc
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当点击了某个区县,需要将养护站的legend也点亮
|
||||||
|
const legendIndex = legendToolActiveItem.value.indexOf('serviceFacility')
|
||||||
|
// 如果过滤列表存在数据,则需要点亮legend
|
||||||
|
if (filterList.length && legendIndex == -1) {
|
||||||
|
legendToolActiveItem.value.push('serviceFacility')
|
||||||
|
}
|
||||||
|
// 如果过滤列表不存在,则需要关闭legend
|
||||||
|
if (!filterList.length && legendIndex > -1) {
|
||||||
|
legendToolActiveItem.value.splice(legendIndex, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用图例hook来进行数据的请求与图例的绘制,第一个回调函数是过滤点位,第二个是聚焦点位
|
||||||
|
mapImageMarkHook.filterYHZMark(
|
||||||
|
"serviceFacility",
|
||||||
|
(item) => {
|
||||||
|
return filterList.find((filterItem) => filterItem == item.qxmc)
|
||||||
|
},
|
||||||
|
(item) => {
|
||||||
|
return item.qxmc == focusKey
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理图例工具栏的标记切换事件
|
* 处理图例工具栏的标记切换事件
|
||||||
*
|
*
|
||||||
@ -422,10 +480,19 @@ const renderEmergencyForcePoints = async (entityService, points, markerIcon) =>
|
|||||||
* @param {string} [payload.markerIcon] - 标记图标
|
* @param {string} [payload.markerIcon] - 标记图标
|
||||||
*/
|
*/
|
||||||
const handleLegendMarkerToggle = async ({ key, active, markerIcon }) => {
|
const handleLegendMarkerToggle = async ({ key, active, markerIcon }) => {
|
||||||
|
if (key === 'riskRoad') {
|
||||||
|
mapBase.toggleHighAltitudeRoadMap(active)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 只处理应急力量图例项
|
// 只处理应急力量图例项
|
||||||
if (key !== 'emergencyForce') {
|
if (key !== 'emergencyForce') {
|
||||||
|
if (active == false) {
|
||||||
|
filterMark.value[key] = []
|
||||||
|
}
|
||||||
|
|
||||||
mapImageMark.toggleMark({ key, active, markerIcon })
|
mapImageMarkHook.toggleMark({ key, active, markerIcon })
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -552,6 +619,7 @@ onBeforeUnmount(() => {
|
|||||||
@supports (width: 1cqw) {
|
@supports (width: 1cqw) {
|
||||||
--cq-inline-100: 100cqw;
|
--cq-inline-100: 100cqw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@supports (height: 1cqh) {
|
@supports (height: 1cqh) {
|
||||||
--cq-block-100: 100cqh;
|
--cq-block-100: 100cqh;
|
||||||
}
|
}
|
||||||
@ -568,13 +636,15 @@ onBeforeUnmount(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: var(--cockpit-min-height);
|
min-height: var(--cockpit-min-height);
|
||||||
min-width: 0; /* 允许 flex 子元素收缩 */
|
min-width: 0;
|
||||||
|
/* 允许 flex 子元素收缩 */
|
||||||
|
|
||||||
background: url(../assets/img/cockpit-main-bg.png) no-repeat;
|
background: url(../assets/img/cockpit-main-bg.png) no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: auto; /* 当宿主高度 < 最小高度时允许滚动 */
|
overflow: auto;
|
||||||
|
/* 当宿主高度 < 最小高度时允许滚动 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 窄容器嵌入的紧凑布局(<1100px 宽度)*/
|
/* 窄容器嵌入的紧凑布局(<1100px 宽度)*/
|
||||||
@ -586,8 +656,10 @@ onBeforeUnmount(() => {
|
|||||||
.cockpit-main {
|
.cockpit-main {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0; /* 允许网格在 flex 上下文中收缩 */
|
min-height: 0;
|
||||||
overflow: hidden; /* 防止内容溢出 */
|
/* 允许网格在 flex 上下文中收缩 */
|
||||||
|
overflow: hidden;
|
||||||
|
/* 防止内容溢出 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 地图底层 - 填满整个容器 */
|
/* 地图底层 - 填满整个容器 */
|
||||||
@ -603,7 +675,8 @@ onBeforeUnmount(() => {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
pointer-events: none; /* 不阻挡交互 */
|
pointer-events: none;
|
||||||
|
/* 不阻挡交互 */
|
||||||
background: url(../assets/img/遮罩层.png) no-repeat center/cover;
|
background: url(../assets/img/遮罩层.png) no-repeat center/cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,7 +690,8 @@ onBeforeUnmount(() => {
|
|||||||
gap: var(--cockpit-gap);
|
gap: var(--cockpit-gap);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
pointer-events: none; /* 容器不拦截事件,让中间区域透明 */
|
pointer-events: none;
|
||||||
|
/* 容器不拦截事件,让中间区域透明 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 中间占位区域 - 透明且不可交互,点击穿透到地图 */
|
/* 中间占位区域 - 透明且不可交互,点击穿透到地图 */
|
||||||
@ -630,10 +704,13 @@ onBeforeUnmount(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--cockpit-gap);
|
gap: var(--cockpit-gap);
|
||||||
min-width: 0; /* 防止在窄容器中溢出 */
|
min-width: 0;
|
||||||
min-height: 0; /* 允许 flex 子元素收缩并启用滚动 */
|
/* 防止在窄容器中溢出 */
|
||||||
|
min-height: 0;
|
||||||
|
/* 允许 flex 子元素收缩并启用滚动 */
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
pointer-events: auto; /* 恢复面板的交互能力 */
|
pointer-events: auto;
|
||||||
|
/* 恢复面板的交互能力 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 图例工具栏 - 居中显示在底部 */
|
/* 图例工具栏 - 居中显示在底部 */
|
||||||
@ -643,6 +720,7 @@ onBeforeUnmount(() => {
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
pointer-events: auto; /* 确保图例可交互 */
|
pointer-events: auto;
|
||||||
|
/* 确保图例可交互 */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
:class="{ 'row-alt': index % 2 === 0 }"
|
:class="{ 'row-alt': index % 2 === 0 }"
|
||||||
>
|
>
|
||||||
<div class="row-number">{{ index + 1 }}</div>
|
<div class="row-number">{{ index + 1 }}</div>
|
||||||
<span class="district-name">{{ resource.qxmc }}</span>
|
<span class="district-name" @click="emit('clickEmergencyResource' , resource)">{{ resource.qxmc }}</span>
|
||||||
<span class="count green">{{ resource.yhzCount }}</span>
|
<span class="count green">{{ resource.yhzCount }}</span>
|
||||||
<span class="count orange">{{ resource.wzCount }}</span>
|
<span class="count orange">{{ resource.wzCount }}</span>
|
||||||
<div class="equipment-cell">
|
<div class="equipment-cell">
|
||||||
@ -43,7 +43,9 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { request } from '@shared/utils/request'
|
import { request } from '@shared/utils/request'
|
||||||
|
|
||||||
const resources = ref([
|
const emit = defineEmits(['clickEmergencyResource'])
|
||||||
|
|
||||||
|
const testData = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: '万州区',
|
name: '万州区',
|
||||||
@ -98,7 +100,9 @@ const resources = ref([
|
|||||||
equipmentClass: 'red',
|
equipmentClass: 'red',
|
||||||
hasAlert: false
|
hasAlert: false
|
||||||
}
|
}
|
||||||
])
|
]
|
||||||
|
|
||||||
|
const resources = ref([])
|
||||||
|
|
||||||
// 请求后端接口 /district/statistics
|
// 请求后端接口 /district/statistics
|
||||||
const getDistrictStatistics = async () => {
|
const getDistrictStatistics = async () => {
|
||||||
@ -106,7 +110,6 @@ const getDistrictStatistics = async () => {
|
|||||||
url: '/snow-ops-platform/district/statistics',
|
url: '/snow-ops-platform/district/statistics',
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
})
|
})
|
||||||
console.log(res)
|
|
||||||
if(res.code === '00000') {
|
if(res.code === '00000') {
|
||||||
resources.value = res.data
|
resources.value = res.data
|
||||||
} else {
|
} else {
|
||||||
@ -240,6 +243,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
.district-name {
|
.district-name {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.count {
|
.count {
|
||||||
|
|||||||
@ -11,8 +11,32 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<div class="tooltip-content">
|
<div class="tooltip-content" v-if="!loading && detail">
|
||||||
<component v-if="hasData" :is="contentMap[data.mapData.layerId]" :data="data" />
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">名称:</span>
|
||||||
|
<span class="value">{{ detail.mc }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">所属区县:</span>
|
||||||
|
<span class="value">{{ detail.qxmc }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">应急设备:</span>
|
||||||
|
<span class="value">{{ detail.sbsl }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">应急物资:</span>
|
||||||
|
<span class="value">{{ detail.wzsl }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">应急人员:</span>
|
||||||
|
<span class="value">{{ detail.rysl }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 如果没有数据 -->
|
<!-- 如果没有数据 -->
|
||||||
<div v-if="!hasData" class="no-data">
|
<div v-if="!hasData" class="no-data">
|
||||||
@ -36,11 +60,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import serviceFacility from './serviceFacility.vue'
|
import { getYHZDetail } from '@/views/cockpit/api/commonHttp.js'
|
||||||
import riskRoad from './riskRoad.vue'
|
|
||||||
import weatherAlert from './weatherAlert.vue'
|
|
||||||
import blockEvent from './blockEvent.vue'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应急力量详情提示框组件
|
* 应急力量详情提示框组件
|
||||||
* 使用 HTML Overlay 方式显示在地图标记点上方
|
* 使用 HTML Overlay 方式显示在地图标记点上方
|
||||||
@ -75,14 +95,6 @@ const props = defineProps({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载状态
|
|
||||||
*/
|
|
||||||
loading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 错误信息
|
* 错误信息
|
||||||
*/
|
*/
|
||||||
@ -93,16 +105,10 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// ==================== State ====================
|
// ==================== State ====================
|
||||||
const contentMap = ref({
|
|
||||||
// 养护站内容
|
const title = ref('养护站')
|
||||||
serviceFacility,
|
const detail = ref(null)
|
||||||
// 高海拔道路
|
const loading = ref(true)
|
||||||
riskRoad,
|
|
||||||
// 阻断事件
|
|
||||||
blockEvent,
|
|
||||||
// 气象预警
|
|
||||||
weatherAlert,
|
|
||||||
})
|
|
||||||
|
|
||||||
// ==================== Emits ====================
|
// ==================== Emits ====================
|
||||||
|
|
||||||
@ -117,22 +123,22 @@ const hasData = computed(() => {
|
|||||||
return !!props.data
|
return !!props.data
|
||||||
})
|
})
|
||||||
|
|
||||||
const title = computed(() => {
|
|
||||||
if (props.data.mapData.layerId === 'serviceFacility') {
|
|
||||||
return '养护站'
|
|
||||||
}
|
|
||||||
if (props.data.mapData.layerId === 'riskRoad') {
|
|
||||||
return '高海拔路段'
|
|
||||||
}
|
|
||||||
if (props.data.mapData.layerId === 'blockEvent') {
|
|
||||||
return '阻断事件'
|
|
||||||
}
|
|
||||||
if (props.data.mapData.layerId === 'weatherAlert') {
|
|
||||||
return '气象预警'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// ==================== Methods ====================
|
// ==================== Methods ====================
|
||||||
|
// 这个方法会在index.js中被执行
|
||||||
|
const init = () => {
|
||||||
|
getDetail()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDetail = async () => {
|
||||||
|
loading.value = true
|
||||||
|
const res = await getYHZDetail({
|
||||||
|
id: props.data.id
|
||||||
|
})
|
||||||
|
if (res.success) {
|
||||||
|
detail.value = res.data
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理关闭按钮点击
|
* 处理关闭按钮点击
|
||||||
@ -140,11 +146,22 @@ const title = computed(() => {
|
|||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
init
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@use '@/styles/mixins.scss' as *;
|
@use '@/styles/mixins.scss' as *;
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.common-tooltip {
|
.common-tooltip {
|
||||||
// CSS 变量:控制背景图片尺寸和内边距
|
// CSS 变量:控制背景图片尺寸和内边距
|
||||||
// 注意:修改图片资源时,需要同步更新这些高度值以匹配实际 PNG 尺寸
|
// 注意:修改图片资源时,需要同步更新这些高度值以匹配实际 PNG 尺寸
|
||||||
@ -161,7 +178,7 @@ const handleClose = () => {
|
|||||||
transform: translate(-50%, calc(-100% - 20px));
|
transform: translate(-50%, calc(-100% - 20px));
|
||||||
|
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
max-width: 300px;
|
width: 300px;
|
||||||
|
|
||||||
// 上下 padding 需要容纳顶部和底部图片 + 内容间距
|
// 上下 padding 需要容纳顶部和底部图片 + 内容间距
|
||||||
// 左右 padding 保持一致
|
// 左右 padding 保持一致
|
||||||
@ -262,25 +279,43 @@ const handleClose = () => {
|
|||||||
|
|
||||||
// 内容区域
|
// 内容区域
|
||||||
.tooltip-content {
|
.tooltip-content {
|
||||||
padding-right: 1.5rem; // 为关闭按钮留出空间
|
display: grid;
|
||||||
|
grid-template-columns: 1.5fr 1fr;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.info-item:first-child {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// padding-right: 1.5rem; // 为关闭按钮留出空间
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb {
|
||||||
|
margin-bottom: 0.5rem
|
||||||
}
|
}
|
||||||
|
|
||||||
// 信息项
|
// 信息项
|
||||||
.info-item {
|
.info-item {
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
|
white-space: nowrap;
|
||||||
color: rgba(255, 255, 255, 0.7);
|
color: rgba(255, 255, 255, 0.7);
|
||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
color: #fff;
|
font-family: SourceHanSansCN, SourceHanSansCN;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #14FFF6;
|
||||||
|
line-height: 21px;
|
||||||
|
text-align: left;
|
||||||
|
font-style: normal;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,8 +1,13 @@
|
|||||||
import { createVNode, render } from 'vue'
|
import { createVNode, render } from 'vue'
|
||||||
import ImageMarkTooltip from './ImageMarkTooltip.vue'
|
import serviceFacility from './YHZTooltip.vue'
|
||||||
|
|
||||||
class ImageMarkTooltipUI {
|
const UIMap = {
|
||||||
constructor() {
|
serviceFacility
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageMarkTooltip {
|
||||||
|
constructor({ key }) {
|
||||||
|
this.key = key
|
||||||
this.instance = null
|
this.instance = null
|
||||||
this.container = null
|
this.container = null
|
||||||
this.entity = null
|
this.entity = null
|
||||||
@ -22,8 +27,10 @@ class ImageMarkTooltipUI {
|
|||||||
|
|
||||||
this.entity = options.entity
|
this.entity = options.entity
|
||||||
|
|
||||||
|
const UIInstance = UIMap[this.key]
|
||||||
|
|
||||||
// 创建 VNode
|
// 创建 VNode
|
||||||
const vnode = createVNode(ImageMarkTooltip, {
|
const vnode = createVNode(UIInstance, {
|
||||||
visible: true,
|
visible: true,
|
||||||
position: options.position || { x: 0, y: 0 },
|
position: options.position || { x: 0, y: 0 },
|
||||||
data: options.data || {},
|
data: options.data || {},
|
||||||
@ -37,6 +44,9 @@ class ImageMarkTooltipUI {
|
|||||||
// 渲染到容器
|
// 渲染到容器
|
||||||
render(vnode, this.container)
|
render(vnode, this.container)
|
||||||
this.instance = vnode.component
|
this.instance = vnode.component
|
||||||
|
if(this.instance.exposed?.init) {
|
||||||
|
this.instance.exposed.init()
|
||||||
|
}
|
||||||
return this.instance
|
return this.instance
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,11 +78,7 @@ class ImageMarkTooltipUI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建单例实例
|
|
||||||
const instance = new ImageMarkTooltipUI()
|
|
||||||
|
|
||||||
export const CommonTooltip = instance
|
export const newImageMarkTooltip = ({ key }) => {
|
||||||
|
return new ImageMarkTooltip({ key })
|
||||||
export const newImageMarkTooltip = () => {
|
|
||||||
return new ImageMarkTooltipUI()
|
|
||||||
}
|
}
|
||||||
@ -1,5 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="tool-tip-content">
|
<div class="tool-tip-content">
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">名称:</span>
|
||||||
|
<span class="value">{{ data.mc }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex">
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">所属区县:</span>
|
||||||
|
<span class="value">{{ data.yjllpz }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">应急设备:</span>
|
||||||
|
<span class="value">{{ data.wz }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|||||||
@ -1,51 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="content" v-if="data">
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="label">名称:</span>
|
|
||||||
<span class="value">{{ data.mc }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex">
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="label">所属区县:</span>
|
|
||||||
<span class="value">{{ data.yjllpz }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="label">应急物资:</span>
|
|
||||||
<span class="value">{{ data.wz }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
import { onMounted, ref } from 'vue';
|
|
||||||
const props = defineProps({
|
|
||||||
data: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
@use '@/styles/mixins.scss' as *;
|
|
||||||
|
|
||||||
// 信息项
|
|
||||||
.info-item {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
color: rgba(255, 255, 255, 0.7);
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
color: #fff;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,8 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<header class="page-header">
|
<header class="page-header">
|
||||||
<div class="header-bg">
|
<div class="header-bg">
|
||||||
<h1 class="title">安全保通服务</h1>
|
|
||||||
<button class="app-button">应用</button>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
@ -14,7 +12,7 @@
|
|||||||
@use '@/styles/mixins.scss' as *;
|
@use '@/styles/mixins.scss' as *;
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
height: vh(137);
|
height: vw(111);
|
||||||
background: url(../assets/img/header-bg.png) no-repeat;
|
background: url(../assets/img/header-bg.png) no-repeat;
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -23,15 +21,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-bg {
|
|
||||||
background-image: url(../assets/img/header-title-bg.png);
|
|
||||||
width: 100%;
|
|
||||||
height: vh(107);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
color: rgba(255, 255, 255, 1);
|
color: rgba(255, 255, 255, 1);
|
||||||
@ -43,24 +32,4 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-button {
|
|
||||||
position: absolute;
|
|
||||||
right: vw(40);
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
width: vw(165);
|
|
||||||
height: vh(44);
|
|
||||||
background: url(../assets/img/header-btn-app-bg.png) no-repeat;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
color: rgba(255, 255, 255, 1);
|
|
||||||
font-size: fs(16);
|
|
||||||
font-family: SourceHanSansCN-Regular, sans-serif;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: opacity 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -66,6 +66,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import { getWeatherWarningStatistics } from '@/views/cockpit/api/commonHttp.js'
|
||||||
|
|
||||||
// 导入图片
|
// 导入图片
|
||||||
import glowTopIcon from '../assets/img/weather-badge-glow-top.png'
|
import glowTopIcon from '../assets/img/weather-badge-glow-top.png'
|
||||||
@ -79,7 +80,8 @@ const warningLevels = ref([
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: '蓝色预警',
|
name: '蓝色预警',
|
||||||
count: 320,
|
key: 'blueWarningCount',
|
||||||
|
count: 0,
|
||||||
color: 'rgba(132, 199, 255, 1)',
|
color: 'rgba(132, 199, 255, 1)',
|
||||||
glowTopIcon: glowTopIcon,
|
glowTopIcon: glowTopIcon,
|
||||||
glowBottomIcon: glowBottomIcon,
|
glowBottomIcon: glowBottomIcon,
|
||||||
@ -88,7 +90,8 @@ const warningLevels = ref([
|
|||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: '黄色预警',
|
name: '黄色预警',
|
||||||
count: 36,
|
key: 'yellowWarningCount',
|
||||||
|
count: 0,
|
||||||
color: 'rgba(215, 209, 38, 1)',
|
color: 'rgba(215, 209, 38, 1)',
|
||||||
glowTopIcon: glowTopIcon,
|
glowTopIcon: glowTopIcon,
|
||||||
glowBottomIcon: glowBottomIcon,
|
glowBottomIcon: glowBottomIcon,
|
||||||
@ -97,7 +100,8 @@ const warningLevels = ref([
|
|||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: '橙色预警',
|
name: '橙色预警',
|
||||||
count: 2,
|
key: 'orangeWarningCount',
|
||||||
|
count: 0,
|
||||||
color: 'rgba(255, 114, 0, 1)',
|
color: 'rgba(255, 114, 0, 1)',
|
||||||
glowTopIcon: glowTopIcon,
|
glowTopIcon: glowTopIcon,
|
||||||
glowBottomIcon: glowBottomIcon,
|
glowBottomIcon: glowBottomIcon,
|
||||||
@ -106,7 +110,8 @@ const warningLevels = ref([
|
|||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
name: '红色预警',
|
name: '红色预警',
|
||||||
count: 1,
|
key: 'redWarningCount',
|
||||||
|
count: 0,
|
||||||
color: 'rgba(255, 32, 0, 1)',
|
color: 'rgba(255, 32, 0, 1)',
|
||||||
glowTopIcon: glowTopIcon,
|
glowTopIcon: glowTopIcon,
|
||||||
glowBottomIcon: glowBottomIcon,
|
glowBottomIcon: glowBottomIcon,
|
||||||
@ -121,6 +126,24 @@ const districts = ref([
|
|||||||
{ id: 4, name: '涪陵区', km: 35, warning: '蓝色预警', warningColor: 'rgba(47, 156, 246, 1)' },
|
{ id: 4, name: '涪陵区', km: 35, warning: '蓝色预警', warningColor: 'rgba(47, 156, 246, 1)' },
|
||||||
{ id: 5, name: '合川区', km: 46, warning: '蓝色预警', warningColor: 'rgba(47, 156, 246, 1)' }
|
{ id: 5, name: '合川区', km: 46, warning: '蓝色预警', warningColor: 'rgba(47, 156, 246, 1)' }
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const loadData = async (areaCode) => {
|
||||||
|
const param = {
|
||||||
|
areaCode: areaCode || ''
|
||||||
|
}
|
||||||
|
const res = await getWeatherWarningStatistics(param)
|
||||||
|
if(res.success) {
|
||||||
|
for(const item of warningLevels.value) {
|
||||||
|
item.count = res.data[item.key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
loadData
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@ -11,16 +11,19 @@ export default class ImageMarkData {
|
|||||||
cacheData = null
|
cacheData = null
|
||||||
expiresAt = null
|
expiresAt = null
|
||||||
abortController = null
|
abortController = null
|
||||||
|
loading = false
|
||||||
/**
|
/**
|
||||||
* 60秒内重复点击使用缓存数据,减少服务器压力
|
* 60秒内重复点击使用缓存数据,减少服务器压力
|
||||||
*/
|
*/
|
||||||
cacheTime = 60 * 1000
|
cacheTime = 60 * 1000
|
||||||
|
|
||||||
constructor({ api, tooltip, onResponse }) {
|
constructor({ api, tooltip, markerIcon, onResponse, key }) {
|
||||||
this.api = api
|
this.api = api
|
||||||
|
this.key = key
|
||||||
this.response = onResponse
|
this.response = onResponse
|
||||||
|
this.markerIcon = markerIcon
|
||||||
// 初始化提示框
|
// 初始化提示框
|
||||||
this.tooltip = tooltip || newImageMarkTooltip()
|
this.tooltip = tooltip || newImageMarkTooltip({key})
|
||||||
}
|
}
|
||||||
|
|
||||||
getCache = () => {
|
getCache = () => {
|
||||||
@ -37,7 +40,7 @@ export default class ImageMarkData {
|
|||||||
// 首先查看缓存是否可用
|
// 首先查看缓存是否可用
|
||||||
const cacheData = this.getCache()
|
const cacheData = this.getCache()
|
||||||
if (cacheData) return cacheData
|
if (cacheData) return cacheData
|
||||||
|
this.loading = true
|
||||||
const controller = this.createAbortController()
|
const controller = this.createAbortController()
|
||||||
const config = {}
|
const config = {}
|
||||||
if (controller) config.signal = controller.signal
|
if (controller) config.signal = controller.signal
|
||||||
@ -47,6 +50,7 @@ export default class ImageMarkData {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('请求失败: ', error)
|
console.error('请求失败: ', error)
|
||||||
} finally {
|
} finally {
|
||||||
|
this.loading = false
|
||||||
let data = null
|
let data = null
|
||||||
if (this.response) data = this.response(res)
|
if (this.response) data = this.response(res)
|
||||||
else data = this.commonOnResponse(res)
|
else data = this.commonOnResponse(res)
|
||||||
@ -81,7 +85,6 @@ export default class ImageMarkData {
|
|||||||
// 通用的响应处理
|
// 通用的响应处理
|
||||||
commonOnResponse = (res) => {
|
commonOnResponse = (res) => {
|
||||||
if (res?.success) {
|
if (res?.success) {
|
||||||
res.data = res.data.slice(0, 2)
|
|
||||||
const dataList = res.data.map((item) => {
|
const dataList = res.data.map((item) => {
|
||||||
item.mapData = {
|
item.mapData = {
|
||||||
id: this.key + '-' + item.rid,
|
id: this.key + '-' + item.rid,
|
||||||
@ -91,7 +94,6 @@ export default class ImageMarkData {
|
|||||||
}
|
}
|
||||||
return item
|
return item
|
||||||
})
|
})
|
||||||
this.data = dataList
|
|
||||||
return dataList
|
return dataList
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { getBaseMap, getBusinessMap } from '@/views/cockpit/api/commonHttp.js'
|
import { getBaseMap, getBusinessMap, getHighAltitudeRoadMap } from '@/views/cockpit/api/commonHttp.js'
|
||||||
import * as Cesium from 'cesium'
|
import * as Cesium from 'cesium'
|
||||||
|
|
||||||
|
|
||||||
@ -6,6 +6,8 @@ import * as Cesium from 'cesium'
|
|||||||
// 主要是加载地图底图
|
// 主要是加载地图底图
|
||||||
export const useMapBase = (mapStore) => {
|
export const useMapBase = (mapStore) => {
|
||||||
|
|
||||||
|
let hightAltitudeRoadMapId
|
||||||
|
|
||||||
// 加载当前业务的底图, 类似于天地图,但是没有使用天地图作为底图,有大的地块的地形纹理,但是缩小范围很小,属于比较粗的图
|
// 加载当前业务的底图, 类似于天地图,但是没有使用天地图作为底图,有大的地块的地形纹理,但是缩小范围很小,属于比较粗的图
|
||||||
const loadBaseMap = async () => {
|
const loadBaseMap = async () => {
|
||||||
const layerService = mapStore.services().layer
|
const layerService = mapStore.services().layer
|
||||||
@ -38,22 +40,22 @@ export const useMapBase = (mapStore) => {
|
|||||||
// 处理启动加载的图层
|
// 处理启动加载的图层
|
||||||
const collectBootLoadLayers = (nodes, layers = [], parent = null) => {
|
const collectBootLoadLayers = (nodes, layers = [], parent = null) => {
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
if (node.Attribute?.servicePath && node.Attribute.bootLoad === 1) {
|
if (node.Attribute?.servicePath && node.Attribute.bootLoad === 1) {
|
||||||
// 确保 bootLoad 图层包含正确的 parentSortIndex
|
// 确保 bootLoad 图层包含正确的 parentSortIndex
|
||||||
const layerWithParentSort = {
|
const layerWithParentSort = {
|
||||||
...node,
|
...node,
|
||||||
parentSortIndex: parent?.Attribute?.sortValue
|
parentSortIndex: parent?.Attribute?.sortValue
|
||||||
};
|
};
|
||||||
layers.push(layerWithParentSort);
|
layers.push(layerWithParentSort);
|
||||||
}
|
}
|
||||||
if (node.Children) {
|
if (node.Children) {
|
||||||
collectBootLoadLayers(node.Children, layers, node);
|
collectBootLoadLayers(node.Children, layers, node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return layers;
|
return layers;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载业务地图,业务地图主要是高亮当前业务下的地区的区县,边界都会有高亮线条
|
// 加载业务地图,业务地图主要是高亮当前业务下的地区的区县与管线图,边界都会有高亮线条
|
||||||
const loadBusinessMap = async () => {
|
const loadBusinessMap = async () => {
|
||||||
const layerService = mapStore.services().layer
|
const layerService = mapStore.services().layer
|
||||||
const res = await getBusinessMap()
|
const res = await getBusinessMap()
|
||||||
@ -75,6 +77,34 @@ export const useMapBase = (mapStore) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载高海拔底图
|
||||||
|
const toggleHighAltitudeRoadMap = async (flag) => {
|
||||||
|
const layerService = mapStore.services().layer
|
||||||
|
|
||||||
|
if(!flag && hightAltitudeRoadMapId) {
|
||||||
|
layerService.removeLayer(hightAltitudeRoadMapId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const res = await getHighAltitudeRoadMap()
|
||||||
|
const resData = res.data
|
||||||
|
resData[0].Children = resData
|
||||||
|
mapStore.baseMapGroups = resData
|
||||||
|
for (const item of resData) {
|
||||||
|
const layers = mapStore.getBaseMapLayersForGroup(item.Attribute?.rid || item.Rid)
|
||||||
|
for (const layerConfig of layers) {
|
||||||
|
const layer = {
|
||||||
|
id: layerConfig.id,
|
||||||
|
type: layerConfig.type,
|
||||||
|
url: layerConfig.url,
|
||||||
|
meta: layerConfig.meta,
|
||||||
|
}
|
||||||
|
hightAltitudeRoadMapId = await layerService.addLayer(layer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loadBaseData = () => {
|
const loadBaseData = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loadBaseMap()
|
loadBaseMap()
|
||||||
@ -83,6 +113,7 @@ export const useMapBase = (mapStore) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loadBaseData
|
loadBaseData,
|
||||||
|
toggleHighAltitudeRoadMap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,18 @@
|
|||||||
import { fetchEmergencyForceList } from '@/views/cockpit/api/emergencyForce'
|
import { fetchEmergencyForceList } from '@/views/cockpit/api/emergencyForce'
|
||||||
|
import { getAllYHZList } from '@/views/cockpit/api/commonHttp'
|
||||||
|
|
||||||
import ImageMarkData from './ImageMarkData'
|
import ImageMarkData from './ImageMarkData'
|
||||||
import * as Cesium from 'cesium'
|
import * as Cesium from 'cesium'
|
||||||
|
import * as turf from '@turf/turf'
|
||||||
|
|
||||||
|
|
||||||
|
import serviceFacilityMarkerIcon from '../assets/legendTool/服务设施icon定位.png'
|
||||||
|
import riskRoadMarkerIcon from '../assets/legendTool/风险路段icon定位.png'
|
||||||
|
import blockEventMarkerIcon from '../assets/legendTool/阻断事件icon定位.png'
|
||||||
|
import emergencyForceMarkerIcon from '../assets/legendTool/应急力量icon定位.png'
|
||||||
|
import weatherAlertMarkerIcon from '../assets/legendTool/气象预警icon定位.png'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前业务下的地图服务
|
* 当前业务下的地图服务
|
||||||
@ -15,16 +26,41 @@ export const useMapImageMark = (mapStore) => {
|
|||||||
|
|
||||||
// 接口服务映射
|
// 接口服务映射
|
||||||
const imageMarkMap = {
|
const imageMarkMap = {
|
||||||
|
// 养护站 legend
|
||||||
serviceFacility: new ImageMarkData({
|
serviceFacility: new ImageMarkData({
|
||||||
api: fetchEmergencyForceList,
|
key: 'serviceFacility',
|
||||||
}),
|
api: getAllYHZList,
|
||||||
riskRoad: new ImageMarkData({
|
markerIcon: serviceFacilityMarkerIcon,
|
||||||
api: fetchEmergencyForceList,
|
onResponse: function (res) {
|
||||||
|
if (res?.success) {
|
||||||
|
res.data = res.data.filter((item) => {
|
||||||
|
if (isNaN(item.jd) || isNaN(item.wd)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
const dataList = res.data.map((item) => {
|
||||||
|
item.mapData = {
|
||||||
|
id: this.key + '-' + item.id,
|
||||||
|
layerId: this.key,
|
||||||
|
position: [Number(item.jd), Number(item.wd), 0],
|
||||||
|
image: this.markerIcon
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
return dataList
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
blockEvent: new ImageMarkData({
|
blockEvent: new ImageMarkData({
|
||||||
|
key: 'blockEvent',
|
||||||
|
markerIcon: blockEventMarkerIcon,
|
||||||
api: fetchEmergencyForceList,
|
api: fetchEmergencyForceList,
|
||||||
}),
|
}),
|
||||||
weatherAlert: new ImageMarkData({
|
weatherAlert: new ImageMarkData({
|
||||||
|
key: 'weatherAlert',
|
||||||
|
markerIcon: weatherAlertMarkerIcon,
|
||||||
api: fetchEmergencyForceList,
|
api: fetchEmergencyForceList,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -59,10 +95,10 @@ export const useMapImageMark = (mapStore) => {
|
|||||||
// 帧渲染函数,也就是显示器的每一帧都会执行
|
// 帧渲染函数,也就是显示器的每一帧都会执行
|
||||||
// 地图拖动与放大的监控,需要更新各个tooltips的位置, 通过消息来通知
|
// 地图拖动与放大的监控,需要更新各个tooltips的位置, 通过消息来通知
|
||||||
viewer.scene.postRender.addEventListener(() => {
|
viewer.scene.postRender.addEventListener(() => {
|
||||||
for(const key in imageMarkMap) {
|
for (const key in imageMarkMap) {
|
||||||
const imageMarkData = imageMarkMap[key]
|
const imageMarkData = imageMarkMap[key]
|
||||||
const entity = imageMarkData.tooltip.entity
|
const entity = imageMarkData.tooltip.entity
|
||||||
if(!entity) continue
|
if (!entity) continue
|
||||||
imageMarkData.tooltip.updatePosition(getScreenPosition(entity))
|
imageMarkData.tooltip.updatePosition(getScreenPosition(entity))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -99,23 +135,45 @@ export const useMapImageMark = (mapStore) => {
|
|||||||
imageMarkData.tooltip.close()
|
imageMarkData.tooltip.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 聚焦到图片实例的中心点
|
||||||
|
*/
|
||||||
|
const focusOnMarkers = (dataList) => {
|
||||||
|
if (!dataList || dataList.length === 0) return
|
||||||
|
const positions = dataList.map((item) => {
|
||||||
|
return [item.mapData.position[0], item.mapData.position[1]]
|
||||||
|
})
|
||||||
|
const features = turf.points(positions)
|
||||||
|
const center = turf.center(features)
|
||||||
|
const position = center.geometry.coordinates
|
||||||
|
mapStore.viewer.camera.setView({
|
||||||
|
destination: Cesium.Cartesian3.fromDegrees(position[0], position[1], 160000),
|
||||||
|
orientation: {
|
||||||
|
heading: Cesium.Math.toRadians(0), // 方向角
|
||||||
|
pitch: Cesium.Math.toRadians(-90), // 俯仰角(-90 为垂直向下看)
|
||||||
|
roll: 0 // 翻滚角
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据图标名称请求后台服务,动态加载图标列表
|
* 根据图标名称请求后台服务,动态加载图标列表
|
||||||
*/
|
*/
|
||||||
const loadDynamicMark = async ({ key, markerIcon, params }) => {
|
const loadDynamicMark = async ({ key, params }) => {
|
||||||
const imageMarkData = imageMarkMap[key]
|
const imageMarkData = imageMarkMap[key]
|
||||||
if (!imageMarkData) return
|
if (!imageMarkData) return
|
||||||
imageMarkData.markerIcon = markerIcon
|
// 取消上一次请求
|
||||||
imageMarkData.key = key
|
imageMarkData.cancelRequest()
|
||||||
const dataList = await imageMarkData.request(params)
|
const dataList = await imageMarkData.request(params)
|
||||||
|
clearLayerEntity(key)
|
||||||
drawImageEntities(dataList)
|
drawImageEntities(dataList)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 显示/隐藏地图标
|
* 显示/隐藏地图标
|
||||||
*/
|
*/
|
||||||
const toggleMark = async ({ key, active, markerIcon, params }) => {
|
const toggleMark = async ({ key, active, params }) => {
|
||||||
const imageMarkData = imageMarkMap[key]
|
const imageMarkData = imageMarkMap[key]
|
||||||
|
|
||||||
if (!active) {
|
if (!active) {
|
||||||
@ -124,7 +182,31 @@ export const useMapImageMark = (mapStore) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDynamicMark({ key, markerIcon, params })
|
loadDynamicMark({ key, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤养护站图标
|
||||||
|
*/
|
||||||
|
const filterYHZMark = async (key, filterFn, focusFn) => {
|
||||||
|
const imageMarkData = imageMarkMap[key]
|
||||||
|
if (!imageMarkData) return
|
||||||
|
// 取消上一次请求
|
||||||
|
imageMarkData.cancelRequest()
|
||||||
|
let dataList = await imageMarkData.request()
|
||||||
|
dataList = dataList.filter((item) => {
|
||||||
|
return filterFn(item)
|
||||||
|
})
|
||||||
|
clearLayerEntity(key)
|
||||||
|
// 绘制过滤出来的点
|
||||||
|
drawImageEntities(dataList)
|
||||||
|
// 聚焦聚焦函数过滤出来的点
|
||||||
|
const focusDataList = dataList.filter((item)=>{
|
||||||
|
return focusFn(item)
|
||||||
|
})
|
||||||
|
if(focusDataList) {
|
||||||
|
focusOnMarkers(focusDataList)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,7 +253,8 @@ export const useMapImageMark = (mapStore) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
toggleMark
|
toggleMark,
|
||||||
|
filterYHZMark
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
24
packages/shared/utils/aMap.js
Normal file
24
packages/shared/utils/aMap.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
export function loadAMap() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (window.AMap) return resolve();
|
||||||
|
|
||||||
|
// 确保AMapLoader存在
|
||||||
|
if (!window.AMapLoader) {
|
||||||
|
return reject(new Error("AMapLoader未正确加载"));
|
||||||
|
}
|
||||||
|
|
||||||
|
window.AMapLoader.load({
|
||||||
|
key: "848ab05db2a57a7782c153119f50dcef",
|
||||||
|
version: "2.0",
|
||||||
|
})
|
||||||
|
.then((AMap) => {
|
||||||
|
window.AMap = AMap;
|
||||||
|
console.log("AMap初始化完成", AMap);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("AMap加载失败:", err);
|
||||||
|
reject(new Error("地图加载失败,请检查网络连接"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user