| | |
| | | <template> |
| | | <div class="listCard"> |
| | | <div class="top"> |
| | | <span>方案详情</span> |
| | | <!-- <el-button class="clickable-text" @click="handleBack">结束模拟</el-button> --> |
| | | <div class="top"><span>方案详情</span> |
| | | <div @click="togglePick" :class="['pick-button', { active: isPickingActive }]"> |
| | | {{ isPickingActive ? '进行计算' : '开始拾取' }} |
| | | </div> |
| | | </div> |
| | | <div class="details"> |
| | | <div v-if="formattedData.length > 0"> |
| | | <div class="input-group"> |
| | | <div v-for="(item, index) in formattedData" :key="index" class="input-item"> |
| | | <label>{{ item.name }}</label> |
| | | <span>{{ item.value }}</span> |
| | | </div> |
| | | <div v-if="formattedData.length" class="input-group"> |
| | | <div v-for="(item, index) in formattedData" :key="index" class="input-item"> |
| | | <label>{{ item.name }}</label> |
| | | <span>{{ item.value }}</span> |
| | | </div> |
| | | </div> |
| | | <div v-else> |
| | | <p style="text-align: center">暂无方案信息</p> |
| | | </div> |
| | | </div> |
| | | <div> |
| | | |
| | | </div> |
| | | </div> |
| | | <Message |
| | | @close="close" |
| | | class="mess" |
| | | v-show="messageShow" |
| | | :mesData="mesData" |
| | | /> |
| | | <Message @close="close" class="mess" v-show="messageShow" :mesData="mesData" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { defineProps, defineEmits, inject ,ref,watch} from "vue"; |
| | | import { defineProps, defineEmits, inject, ref, watch } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { ElMessage } from "element-plus"; |
| | | |
| | | // 定义 emit 方法 |
| | | // 公共依赖 |
| | | const props = defineProps({ selectedScheme: { type: Object, default: null } }); |
| | | const emit = defineEmits(["back"]); |
| | | const { startSimulate, endSimulate } = inject("simulateActions"); |
| | | const { endSimulate } = inject("simulateActions"); |
| | | |
| | | // 返回按钮点击事件 |
| | | function handleBack() { |
| | | ElMessage({ |
| | | message: "模拟进程正在关闭中...", |
| | | type: "success", |
| | | }); |
| | | emit("back", false); // 向父组件传递 false 值 |
| | | endSimulate(); |
| | | // 状态管理 |
| | | const formattedData = ref([]); |
| | | const pickedPoints = ref([]); |
| | | const handler = ref(null); |
| | | const isPickingActive = ref(false); |
| | | |
| | | // 映射表 |
| | | const areaTypeMap = { |
| | | 0: "自定义区域仿真", |
| | | 1: "行政区划仿真", |
| | | 2: "重点区域仿真", |
| | | 3: "重点沟仿真" |
| | | }; |
| | | |
| | | const statusMap = { |
| | | 0: "创建仿真", |
| | | 1: "预处理", |
| | | 2: "分析中", |
| | | 10: "完成", |
| | | 20: "出错" |
| | | }; |
| | | |
| | | const typeMap = { |
| | | 1: "预测模拟", |
| | | 2: "实时模拟", |
| | | 3: "历史模拟" |
| | | }; |
| | | |
| | | // 跳过字段列表 |
| | | const skipKeys = ["geom", "id", "serviceName", "updateTime", "updateUser", "createUser", "bak"]; |
| | | |
| | | // 处理 data 字段解析 |
| | | function parseDataField(dataStr) { |
| | | if (typeof dataStr !== "string") return []; |
| | | |
| | | try { |
| | | const parsed = JSON.parse(dataStr); |
| | | const fields = { |
| | | total: "降雨总量(mm):", |
| | | duration: "降雨时长(分钟):", |
| | | intensity: "降雨强度(mm/小时):", |
| | | prediction: "降雨场次:", |
| | | model: "降雨模式:", |
| | | history: "历史降雨:", |
| | | gauges: "雨量计列表:" |
| | | }; |
| | | |
| | | return Object.entries(parsed) |
| | | .filter(([k]) => fields[k]) |
| | | .map(([k, v]) => ({ |
| | | name: fields[k], |
| | | value: Array.isArray(v) ? v.join(", ") : v || "无" |
| | | })); |
| | | } catch (e) { |
| | | return [{ name: "数据:", value: dataStr || "无" }]; |
| | | } |
| | | } |
| | | |
| | | // 接收父组件传递的 props |
| | | const props = defineProps({ |
| | | selectedScheme: { |
| | | type: Object, |
| | | default: null, |
| | | }, |
| | | }); |
| | | // 格式化后的数据 |
| | | const formattedData = ref([]); |
| | | |
| | | // 监听 selectedScheme 的变化 |
| | | watch( |
| | | () => props.selectedScheme, |
| | | (newScheme) => { |
| | | // console.log(newScheme, "弹窗的数据"); |
| | | |
| | | // 检查数据有效性 |
| | | if (!newScheme || typeof newScheme !== "object") { |
| | | console.warn("Invalid selectedScheme received:", newScheme); |
| | | formattedData.value = []; |
| | | return; |
| | | } |
| | | |
| | | // 将所有字段转换为列表形式 |
| | | const data = []; |
| | | |
| | | // 提前解析 areaType,确保其存在性 |
| | | const areaType = newScheme.areaType !== undefined ? newScheme.areaType : null; |
| | | |
| | | for (const [key, value] of Object.entries(newScheme)) { |
| | | // 跳过不需要的字段 |
| | | if (["geom", "id", "serviceName", "updateTime", "updateUser", "createUser", "bak"].includes(key)) continue; |
| | | |
| | | // 特殊处理 createTime 字段 |
| | | if (key === "createTime" && typeof value === "number") { |
| | | data.push({ |
| | | name: "创建时间:", |
| | | value: formatDate(value), |
| | | }); |
| | | continue; |
| | | } |
| | | |
| | | // 处理 areaType 字段 |
| | | if (key === "areaType") { |
| | | const areaTypeMap = { |
| | | 0: "自定义区域仿真", |
| | | 1: "行政区划仿真", |
| | | 2: "重点区域仿真", |
| | | 3: "重点沟仿真", |
| | | }; |
| | | data.push({ |
| | | name: "区域类别:", |
| | | value: areaTypeMap[value] || "未知", |
| | | }); |
| | | continue; |
| | | } |
| | | |
| | | // 处理 status 字段 |
| | | if (key === "status") { |
| | | const statusMap = { |
| | | 0: "创建仿真", |
| | | 1: "预处理", |
| | | 2: "分析中", |
| | | 10: "完成", |
| | | 20: "出错", |
| | | }; |
| | | data.push({ |
| | | name: "仿真状态:", |
| | | value: statusMap[value] || "未知", |
| | | }); |
| | | continue; |
| | | } |
| | | |
| | | // 处理 type 字段(仅在 areaType 不为 1 或 2 时显示) |
| | | if (key === "type") { |
| | | if (![1, 2].includes(areaType)) { |
| | | const typeMap = { |
| | | 1: "预测模拟", |
| | | 2: "实时模拟", |
| | | 3: "历史模拟", |
| | | }; |
| | | data.push({ |
| | | name: "模拟类别:", |
| | | value: typeMap[value] || "未知", |
| | | }); |
| | | } |
| | | continue; |
| | | } |
| | | |
| | | // 处理 areaName 字段 |
| | | if (key === "areaName") { |
| | | data.push({ |
| | | name: "区域名称:", |
| | | value: value || "无", |
| | | }); |
| | | continue; |
| | | } |
| | | |
| | | // 处理 result 字段 |
| | | if (key === "result") { |
| | | data.push({ |
| | | name: "仿真结果:", |
| | | value: value || "无", |
| | | }); |
| | | continue; |
| | | } |
| | | |
| | | // 处理 name 字段 |
| | | if (key === "name") { |
| | | data.push({ |
| | | name: "仿真方案:", |
| | | value: value || "无", |
| | | }); |
| | | continue; |
| | | } |
| | | |
| | | // 处理 data 字段 |
| | | if (key === "data" && typeof value === "string") { |
| | | try { |
| | | const parsedData = JSON.parse(value); |
| | | |
| | | // 处理 data.total 字段 |
| | | if (parsedData.total !== undefined) { |
| | | data.push({ |
| | | name: "降雨总量(mm):", |
| | | value: parsedData.total || "无", |
| | | }); |
| | | } |
| | | |
| | | // 处理 data.duration 字段 |
| | | if (parsedData.duration !== undefined) { |
| | | data.push({ |
| | | name: "降雨时长(分钟):", |
| | | value: parsedData.duration || "无", |
| | | }); |
| | | } |
| | | |
| | | // 处理 data.intensity 字段 |
| | | if (parsedData.intensity !== undefined) { |
| | | data.push({ |
| | | name: "降雨强度(mm/小时):", |
| | | value: parsedData.intensity || "无", |
| | | }); |
| | | } |
| | | |
| | | // 处理 data.prediction 字段 |
| | | if (parsedData.prediction !== undefined) { |
| | | data.push({ |
| | | name: "降雨场次:", |
| | | value: parsedData.prediction || "无", |
| | | }); |
| | | } |
| | | |
| | | // 处理 data.model 字段 |
| | | if (parsedData.model !== undefined) { |
| | | data.push({ |
| | | name: "降雨模式:", |
| | | value: parsedData.model || "无", |
| | | }); |
| | | } |
| | | |
| | | // 处理 data.history 字段 |
| | | if (parsedData.history !== undefined) { |
| | | data.push({ |
| | | name: "历史降雨:", |
| | | value: parsedData.history || "无", |
| | | }); |
| | | } |
| | | |
| | | // 处理 data.gauges 字段 |
| | | if (parsedData.gauges !== undefined) { |
| | | data.push({ |
| | | name: "雨量计列表:", |
| | | value: Array.isArray(parsedData.gauges) ? parsedData.gauges.join(", ") : "无", |
| | | }); |
| | | } |
| | | } catch (e) { |
| | | data.push({ |
| | | name: "数据:", |
| | | value: value || "无", |
| | | }); |
| | | } |
| | | continue; |
| | | } |
| | | |
| | | // 其他字段直接展示 |
| | | data.push({ |
| | | name: `${key}:`, |
| | | value: value || "无", |
| | | }); |
| | | } |
| | | |
| | | // 更新 formattedData |
| | | formattedData.value = data; |
| | | }, |
| | | { immediate: true } // 立即执行一次 |
| | | ); |
| | | |
| | | // 格式化时间戳为日期 |
| | | // 格式化时间戳 |
| | | function formatDate(timestamp) { |
| | | return dayjs(timestamp).format("YYYY-MM-DD HH:mm:ss"); |
| | | } |
| | | |
| | | // 定义任务状态的文本映射 |
| | | const statusText = { |
| | | 0: "未开始", |
| | | 1: "进行中", |
| | | 2: "已完成", |
| | | }; |
| | | // 格式化 selectedScheme 数据 |
| | | watch( |
| | | () => props.selectedScheme, |
| | | (newScheme) => { |
| | | if (!newScheme || typeof newScheme !== "object") { |
| | | formattedData.value = []; |
| | | return; |
| | | } |
| | | |
| | | const entries = Object.entries(newScheme); |
| | | const areaType = newScheme.areaType; |
| | | |
| | | const result = entries.reduce((acc, [key, value]) => { |
| | | if (skipKeys.includes(key)) return acc; |
| | | |
| | | switch (key) { |
| | | case "createTime": |
| | | acc.push({ name: "创建时间:", value: formatDate(value) }); |
| | | break; |
| | | case "areaType": |
| | | acc.push({ name: "区域类别:", value: areaTypeMap[value] || "未知" }); |
| | | break; |
| | | case "status": |
| | | acc.push({ name: "仿真状态:", value: statusMap[value] || "未知" }); |
| | | break; |
| | | case "type": |
| | | if (![1, 2].includes(areaType)) { |
| | | acc.push({ name: "模拟类别:", value: typeMap[value] || "未知" }); |
| | | } |
| | | break; |
| | | case "areaName": |
| | | acc.push({ name: "区域名称:", value: value || "无" }); |
| | | break; |
| | | case "result": |
| | | acc.push({ name: "仿真结果:", value: value || "无" }); |
| | | break; |
| | | case "name": |
| | | acc.push({ name: "仿真方案:", value: value || "无" }); |
| | | break; |
| | | case "data": |
| | | acc.push(...parseDataField(value)); |
| | | break; |
| | | default: |
| | | acc.push({ name: `${key}:`, value: value || "无" }); |
| | | } |
| | | |
| | | return acc; |
| | | }, []); |
| | | |
| | | formattedData.value = result; |
| | | }, |
| | | { immediate: true } |
| | | ); |
| | | |
| | | // 拾取相关逻辑 |
| | | const viewer = window.viewer; |
| | | |
| | | function getPickPosition(windowPosition) { |
| | | if (!viewer) return null; |
| | | viewer.scene.globe.depthTestAgainstTerrain = true; |
| | | const cartesian = viewer.scene.pickPosition(windowPosition); |
| | | if (!cartesian) return null; |
| | | |
| | | const cartographic = Cesium.Cartographic.fromCartesian(cartesian); |
| | | |
| | | // 在原有高度上增加300米 |
| | | cartographic.height += 80.0; |
| | | |
| | | return { |
| | | cartesian: Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, cartographic.height), |
| | | longitude: Cesium.Math.toDegrees(cartographic.longitude), |
| | | latitude: Cesium.Math.toDegrees(cartographic.latitude), |
| | | height: cartographic.height |
| | | }; |
| | | } |
| | | |
| | | function addPointToViewer(point, index) { |
| | | const entity = viewer.entities.add({ |
| | | position: point.cartesian, |
| | | billboard: { |
| | | // image: '../path/to/your/icon.png', // 替换为你的图标路径 |
| | | width: 32, // 图标宽度 |
| | | height: 32, // 图标高度 |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM |
| | | }, |
| | | label: { |
| | | text: `Point ${index + 1}\n经度: ${point.longitude.toFixed(6)}\n纬度: ${point.latitude.toFixed(6)}`, |
| | | font: '14pt monospace', |
| | | style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
| | | outlineWidth: 2, |
| | | verticalOrigin: Cesium.VerticalOrigin.TOP, |
| | | pixelOffset: new Cesium.Cartesian2(0, -40), // 调整标签相对于图标的偏移量 |
| | | fillColor: Cesium.Color.WHITE, |
| | | outlineColor: Cesium.Color.BLACK |
| | | } |
| | | }); |
| | | |
| | | // 可选:存储实体引用以便后续操作 |
| | | pickedPoints.value.push(entity); |
| | | } |
| | | |
| | | function initPickHandler() { |
| | | if (!viewer?.scene?.canvas) return; |
| | | handler.value = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); |
| | | |
| | | handler.value.setInputAction((movement) => { |
| | | const position = getPickPosition(movement.position); |
| | | if (position) { |
| | | const index = pickedPoints.value.length; |
| | | pickedPoints.value.push(position); |
| | | addPointToViewer(position, index); |
| | | } |
| | | }, Cesium.ScreenSpaceEventType.LEFT_CLICK); |
| | | } |
| | | |
| | | function togglePick() { |
| | | isPickingActive.value ? stopPicking() : startPicking(); |
| | | } |
| | | |
| | | function startPicking() { |
| | | pickedPoints.value = []; |
| | | viewer.entities.removeAll(); |
| | | !handler.value && initPickHandler(); |
| | | isPickingActive.value = true; |
| | | } |
| | | |
| | | function stopPicking() { |
| | | if (handler.value) { |
| | | handler.value.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK); |
| | | handler.value.destroy(); |
| | | handler.value = null; |
| | | } |
| | | |
| | | isPickingActive.value = false; |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .mess { |
| | | position: absolute; |
| | | top: 10%; |
| | | left: 100%; |
| | | z-index: 5000; |
| | | } |
| | | |
| | | .top { |
| | | display: flex; |
| | | width: 100%; |
| | | height: 41px; |
| | | color: white; |