diff --git a/packages/mobile/index.html b/packages/mobile/index.html index c408d1e..d0c1457 100644 --- a/packages/mobile/index.html +++ b/packages/mobile/index.html @@ -1,14 +1,24 @@ - - - - - - H5移动端 - - -
- - - + + + + + + + H5移动端 + + + + + +
+ + + + \ No newline at end of file diff --git a/packages/mobile/src/views/Material/MaterialManagement.vue b/packages/mobile/src/views/Material/MaterialManagement.vue index 4f4ab58..7caea4b 100644 --- a/packages/mobile/src/views/Material/MaterialManagement.vue +++ b/packages/mobile/src/views/Material/MaterialManagement.vue @@ -211,6 +211,7 @@ import { ref, onMounted, reactive, toRaw, watch } from "vue"; import { useRouter, useRoute } from "vue-router"; import { showToast, showLoadingToast } from "vant"; import { request } from "../../../../shared/utils/request"; +import { loadAMap } from "../../../../shared/utils/aMap"; const router = useRouter(); const route = useRoute(); @@ -218,7 +219,7 @@ const searchValue = ref(""); // 搜索框输入值 const showPopup = ref(false); // 控制弹出层显示隐藏 const yhzDetail = ref({}); // 养护站详情数据 const materialList = ref([]); // 物资列表数据 -const INIT_FORM = { +const getInitForm = () => ({ material: { jd: "", // 物资经度 wd: "", // 物资纬度 @@ -238,8 +239,8 @@ const INIT_FORM = { remark: "", // 备注 }, photos: [], -}; -const form = reactive({ ...INIT_FORM }); // 表单 +}); +const form = reactive(getInitForm()); // 根据养护站rid获取物资列表 const getMaterialList = async (wzmc) => { @@ -281,15 +282,6 @@ onMounted(() => { // 购置日期相关 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); @@ -338,7 +330,6 @@ const handleSubmit = async () => { message: "新增成功", }); onPopupClose(); - Object.assign(form, { ...INIT_FORM }); getMaterialList(searchValue.value); } else { throw new Error(res.message); @@ -423,37 +414,38 @@ const afterRead = async (file) => { }; // 获取经纬度 -const handleGetLocation = () => { - if (!navigator.geolocation) { - showToast("您的浏览器不支持地理位置获取"); - return; +const handleGetLocation = async () => { + // 确保AMap完成加载 + if (!window.AMap) { + await loadAMap(); } - - showLoadingToast({ - message: "定位中...", + const loadingToast = showLoadingToast({ + message: "正在获取位置", forbidClick: true, + duration: 0, // 设置为0表示不会自动关闭 + zIndex: 9999, }); - navigator.geolocation.getCurrentPosition( - (position) => { - form.material.jd = position.coords.longitude.toFixed(6); - form.material.wd = position.coords.latitude.toFixed(6); - }, - (error) => { - const errorMessage = - { - 1: "位置服务被拒绝", - 2: "暂时无法获取位置", - 3: "定位超时", - }[error.code] || "定位失败"; - showToast(errorMessage); - }, - { - enableHighAccuracy: true, // 高精度模式 - timeout: 5000, // 超时时间 - maximumAge: 0, // 不缓存位置 - } - ); + AMap.plugin("AMap.Geolocation", () => { + const geolocation = new AMap.Geolocation({ + enableHighAccuracy: true, + timeout: 5000, + showMarker: false, + zoomToAccuracy: true, + }); + + geolocation.getCurrentPosition((status, result) => { + if (status === "complete") { + form.material.jd = result.position.lng.toFixed(6); + form.material.wd = result.position.lat.toFixed(6); + loadingToast.close(); + } else { + loadingToast.close(); + showToast(result); + console.log("result", result); + } + }); + }); }; watch( @@ -505,6 +497,9 @@ const handleAdd = async () => { }; const onPopupClose = () => { + Object.assign(form, getInitForm()); + fileList.value = []; + [showDwPicker, showFzrPicker].forEach((v) => (v.value = false)); showPopup.value = false; }; diff --git a/packages/screen/package.json b/packages/screen/package.json index 00a2548..9091471 100644 --- a/packages/screen/package.json +++ b/packages/screen/package.json @@ -9,17 +9,18 @@ "preview": "vite preview" }, "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", - "echarts": "^6.0.0", - "vue-echarts": "^8.0.1", - "cesium": "^1.135.0", - "axios": "^1.13.2", + "@h5/shared": "workspace:*", + "@turf/turf": "^7.3.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": { "@vitejs/plugin-vue": "^6.0.1", diff --git a/packages/screen/src/router/index.js b/packages/screen/src/router/index.js index 28e7400..8b66e8f 100644 --- a/packages/screen/src/router/index.js +++ b/packages/screen/src/router/index.js @@ -10,6 +10,9 @@ const routes = [ { path: '/cockpit', name: 'Cockpit', + meta: { + screen: true + }, component: () => import('../views/cockpit/index.vue') }, { diff --git a/packages/screen/src/views/cockpit/api/commonHttp.js b/packages/screen/src/views/cockpit/api/commonHttp.js index a31e253..a436756 100644 --- a/packages/screen/src/views/cockpit/api/commonHttp.js +++ b/packages/screen/src/views/cockpit/api/commonHttp.js @@ -2,7 +2,6 @@ import { request } from '@shared/utils/request' // 获取业务底图 export function getBaseMap() { - // return [...ddt] return request({ url: '/snow-ops-platform/dataDirectory/queryCatalog', method: 'GET', @@ -14,7 +13,6 @@ export function getBaseMap() { // 获取业务图 export function getBusinessMap() { - // return [...si] return request({ url: '/snow-ops-platform/dataDirectory/queryCatalog', 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 + }) +} + + + diff --git a/packages/screen/src/views/cockpit/assets/img/header-bg.png b/packages/screen/src/views/cockpit/assets/img/header-bg.png index 3020925..b873c80 100644 Binary files a/packages/screen/src/views/cockpit/assets/img/header-bg.png and b/packages/screen/src/views/cockpit/assets/img/header-bg.png differ diff --git a/packages/screen/src/views/cockpit/components/CockpitLayout.vue b/packages/screen/src/views/cockpit/components/CockpitLayout.vue index 36beaf8..69834bd 100644 --- a/packages/screen/src/views/cockpit/components/CockpitLayout.vue +++ b/packages/screen/src/views/cockpit/components/CockpitLayout.vue @@ -1,6 +1,7 @@ @@ -60,9 +58,11 @@ import LegendToolbar from './LegendToolbar.vue' import EmergencyForceTooltip from './EmergencyForceTooltip.vue' import emergencyForceMarkerIcon from '../assets/legendTool/应急力量icon定位.png' -onMounted(()=>{ +onMounted(() => { // 加载地图业务底图 并 聚焦中心点 mapBase.loadBaseData() + + weatherWarningRef.value.loadData() }) // ==================== 常量定义 ==================== @@ -79,6 +79,9 @@ const EMERGENCY_FORCE_LAYER_ID = 'legend:emergencyForce' */ const EMERGENCY_FORCE_CACHE_TTL = 60 * 1000 +// ==================== 组件实例 ==================== +const weatherWarningRef = ref(null) + // ==================== 状态管理 ==================== const mapStore = useMapStore() @@ -101,7 +104,7 @@ const mapBase = useMapBase(mapStore) /** * 标记图hook,搭配lengendToolbar使用, 作用是点击某个图例项时,在地图上显示所有该图例的图 */ -const mapImageMark = useMapImageMark(mapStore) +const mapImageMarkHook = useMapImageMark(mapStore) /** * 应急力量数据加载状态 @@ -131,8 +134,21 @@ const emergencyForceAbortController = ref(null) /** * 工具图标列表通过key关联 * 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] - 标记图标 */ const handleLegendMarkerToggle = async ({ key, active, markerIcon }) => { + if (key === 'riskRoad') { + mapBase.toggleHighAltitudeRoadMap(active) + return + } + + // 只处理应急力量图例项 if (key !== 'emergencyForce') { + if (active == false) { + filterMark.value[key] = [] + } - mapImageMark.toggleMark({ key, active, markerIcon }) + mapImageMarkHook.toggleMark({ key, active, markerIcon }) return } @@ -552,6 +619,7 @@ onBeforeUnmount(() => { @supports (width: 1cqw) { --cq-inline-100: 100cqw; } + @supports (height: 1cqh) { --cq-block-100: 100cqh; } @@ -568,13 +636,15 @@ onBeforeUnmount(() => { width: 100%; height: 100%; 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-size: cover; display: flex; flex-direction: column; - overflow: auto; /* 当宿主高度 < 最小高度时允许滚动 */ + overflow: auto; + /* 当宿主高度 < 最小高度时允许滚动 */ } /* 窄容器嵌入的紧凑布局(<1100px 宽度)*/ @@ -586,8 +656,10 @@ onBeforeUnmount(() => { .cockpit-main { position: relative; flex: 1; - min-height: 0; /* 允许网格在 flex 上下文中收缩 */ - overflow: hidden; /* 防止内容溢出 */ + min-height: 0; + /* 允许网格在 flex 上下文中收缩 */ + overflow: hidden; + /* 防止内容溢出 */ } /* 地图底层 - 填满整个容器 */ @@ -603,7 +675,8 @@ onBeforeUnmount(() => { position: absolute; inset: 0; z-index: 1; - pointer-events: none; /* 不阻挡交互 */ + pointer-events: none; + /* 不阻挡交互 */ background: url(../assets/img/遮罩层.png) no-repeat center/cover; } @@ -617,7 +690,8 @@ onBeforeUnmount(() => { gap: var(--cockpit-gap); height: 100%; box-sizing: border-box; - pointer-events: none; /* 容器不拦截事件,让中间区域透明 */ + pointer-events: none; + /* 容器不拦截事件,让中间区域透明 */ } /* 中间占位区域 - 透明且不可交互,点击穿透到地图 */ @@ -630,10 +704,13 @@ onBeforeUnmount(() => { display: flex; flex-direction: column; gap: var(--cockpit-gap); - min-width: 0; /* 防止在窄容器中溢出 */ - min-height: 0; /* 允许 flex 子元素收缩并启用滚动 */ + min-width: 0; + /* 防止在窄容器中溢出 */ + min-height: 0; + /* 允许 flex 子元素收缩并启用滚动 */ padding: 1rem; - pointer-events: auto; /* 恢复面板的交互能力 */ + pointer-events: auto; + /* 恢复面板的交互能力 */ } /* 图例工具栏 - 居中显示在底部 */ @@ -643,6 +720,7 @@ onBeforeUnmount(() => { left: 50%; transform: translateX(-50%); z-index: 3; - pointer-events: auto; /* 确保图例可交互 */ + pointer-events: auto; + /* 确保图例可交互 */ } diff --git a/packages/screen/src/views/cockpit/components/EmergencyResources.vue b/packages/screen/src/views/cockpit/components/EmergencyResources.vue index f94967f..5a44a18 100644 --- a/packages/screen/src/views/cockpit/components/EmergencyResources.vue +++ b/packages/screen/src/views/cockpit/components/EmergencyResources.vue @@ -21,7 +21,7 @@ :class="{ 'row-alt': index % 2 === 0 }" >
{{ index + 1 }}
- {{ resource.qxmc }} + {{ resource.qxmc }} {{ resource.yhzCount }} {{ resource.wzCount }}
@@ -43,7 +43,9 @@ import { ref, onMounted } from 'vue' import { request } from '@shared/utils/request' -const resources = ref([ +const emit = defineEmits(['clickEmergencyResource']) + +const testData = [ { id: 1, name: '万州区', @@ -98,7 +100,9 @@ const resources = ref([ equipmentClass: 'red', hasAlert: false } -]) +] + +const resources = ref([]) // 请求后端接口 /district/statistics const getDistrictStatistics = async () => { @@ -106,7 +110,6 @@ const getDistrictStatistics = async () => { url: '/snow-ops-platform/district/statistics', method: 'GET' }) - console.log(res) if(res.code === '00000') { resources.value = res.data } else { @@ -240,6 +243,7 @@ onMounted(() => { .district-name { text-align: left; + cursor: pointer; } .count { diff --git a/packages/screen/src/views/cockpit/components/ImageMarkTooltip/ImageMarkTooltip.vue b/packages/screen/src/views/cockpit/components/ImageMarkTooltip/YHZTooltip.vue similarity index 78% rename from packages/screen/src/views/cockpit/components/ImageMarkTooltip/ImageMarkTooltip.vue rename to packages/screen/src/views/cockpit/components/ImageMarkTooltip/YHZTooltip.vue index 1b7dfd1..1626400 100644 --- a/packages/screen/src/views/cockpit/components/ImageMarkTooltip/ImageMarkTooltip.vue +++ b/packages/screen/src/views/cockpit/components/ImageMarkTooltip/YHZTooltip.vue @@ -11,8 +11,32 @@ -
- +
+ +
+ 名称: + {{ detail.mc }} +
+ +
+ 所属区县: + {{ detail.qxmc }} +
+ +
+ 应急设备: + {{ detail.sbsl }} +
+ +
+ 应急物资: + {{ detail.wzsl }} +
+ +
+ 应急人员: + {{ detail.rysl }} +
@@ -36,11 +60,7 @@ \ No newline at end of file diff --git a/packages/screen/src/views/cockpit/components/PageHeader.vue b/packages/screen/src/views/cockpit/components/PageHeader.vue index c8655d6..9c55989 100644 --- a/packages/screen/src/views/cockpit/components/PageHeader.vue +++ b/packages/screen/src/views/cockpit/components/PageHeader.vue @@ -1,8 +1,6 @@ @@ -14,7 +12,7 @@ @use '@/styles/mixins.scss' as *; .page-header { - height: vh(137); + height: vw(111); background: url(../assets/img/header-bg.png) no-repeat; background-size: 100% 100%; display: flex; @@ -23,15 +21,6 @@ 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 { color: rgba(255, 255, 255, 1); @@ -43,24 +32,4 @@ 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; - } -} diff --git a/packages/screen/src/views/cockpit/components/WeatherWarning.vue b/packages/screen/src/views/cockpit/components/WeatherWarning.vue index 75b4cd7..17b0457 100644 --- a/packages/screen/src/views/cockpit/components/WeatherWarning.vue +++ b/packages/screen/src/views/cockpit/components/WeatherWarning.vue @@ -66,6 +66,7 @@