diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/一级标题栏bg.png b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/一级标题栏bg.png new file mode 100644 index 0000000..0ca0281 Binary files /dev/null and b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/一级标题栏bg.png differ diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/事件icon.png b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/事件icon.png new file mode 100644 index 0000000..1b33697 Binary files /dev/null and b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/事件icon.png differ diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/信息面板bg.png b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/信息面板bg.png index 5effa9b..1ff4ae9 100644 Binary files a/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/信息面板bg.png and b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/信息面板bg.png differ diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/视频面板bg.png b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/视频面板bg.png index b526190..fe1234d 100644 Binary files a/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/视频面板bg.png and b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/视频面板bg.png differ diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/ForceDispatch.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/ForceDispatch.vue index 4c68cfc..18af93e 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/ForceDispatch.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/ForceDispatch.vue @@ -150,15 +150,16 @@ const handleStartDispatch = () => { .force-dispatch__top { display: grid; grid-template-columns: 1fr 1fr; - gap: vw(12); + gap: vw(20); } /* 响应等级卡片 */ .force-dispatch__level-card { display: flex; align-items: center; + justify-content: space-between; gap: vw(12); - padding: vh(10) vw(16); + padding: vh(5) vw(16); background: rgba(20, 53, 118, 0.6); border: 1px solid rgba(28, 161, 255, 0.3); border-radius: vw(4); @@ -166,13 +167,13 @@ const handleStartDispatch = () => { } .force-dispatch__level-label { - font-size: fs(13); + font-size: fs(18); font-family: SourceHanSansCN-Regular, sans-serif; color: rgba(255, 255, 255, 0.8); } .force-dispatch__level-value { - font-size: fs(14); + font-size: fs(18); font-family: SourceHanSansCN-Bold, sans-serif; font-weight: 600; color: rgba(255, 255, 255, 0.95); @@ -206,7 +207,7 @@ const handleStartDispatch = () => { } .force-dispatch__plan-text { - font-size: fs(13); + font-size: fs(18); font-family: SourceHanSansCN-Medium, sans-serif; font-weight: 500; color: rgba(255, 255, 255, 0.95); diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/ForcePreset.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/ForcePreset.vue index 8663c8a..ae7cfa4 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/ForcePreset.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/ForcePreset.vue @@ -1,22 +1,36 @@ @@ -92,36 +146,119 @@ const handleDistanceChange = (distance) => { padding: 0; margin-top: 0; - &__filter { + // 自定义下拉框 + .custom-dropdown { + position: relative; + margin-bottom: vh(8); + } + + // 触发器 + .dropdown-trigger { display: flex; align-items: center; - gap: vw(8); - padding: vh(10) vw(16); - background: rgba(20, 53, 118, 0.5); - border-radius: vw(4); - margin-bottom: vh(16); + justify-content: space-between; + padding: vh(8) vw(16); + background: rgba(28, 70, 130, 0.9); + border: 1px solid rgba(28, 161, 255, 0.3); + border-radius: vw(8); cursor: pointer; + transition: all 0.3s ease; - .filter-content { - display: flex; - align-items: center; - gap: vw(8); - width: 100%; + &:hover { + background: rgba(28, 70, 130, 1); + border-color: rgba(28, 161, 255, 0.5); } - .filter-text { + .trigger-text { flex: 1; color: var(--text-white); - font-size: fs(14); + font-size: fs(15); font-family: SourceHanSansCN-Medium, sans-serif; + font-weight: 500; } - .filter-icon { - width: vw(12); - height: vh(12); + .trigger-icon { + width: 16px; + height: 16px; + flex-shrink: 0; + transition: transform 0.3s ease; + + img { + width: 100%; + height: 100%; + object-fit: contain; + } + + &.is-open { + transform: rotate(180deg); + } } } + // 下拉面板 + .dropdown-panel { + position: absolute; + top: calc(100% + vh(4)); + left: 0; + right: 0; + background: rgba(15, 35, 75, 0.98); + border: 1px solid rgba(28, 161, 255, 0.3); + border-radius: vw(8); + overflow: hidden; + z-index: 100; + box-shadow: 0 vh(4) vh(16) rgba(0, 0, 0, 0.5); + } + + // 下拉项 + .dropdown-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: vh(12) vw(16); + cursor: pointer; + transition: background 0.2s ease; + + &:hover { + background: rgba(28, 161, 255, 0.1); + } + + &.is-active { + background: rgba(28, 161, 255, 0.15); + } + + .item-text { + flex: 1; + color: rgba(255, 255, 255, 0.9); + font-size: fs(15); + font-family: SourceHanSansCN-Medium, sans-serif; + font-weight: 400; + } + + .item-icon { + width: vw(20); + height: vh(20); + flex-shrink: 0; + color: var(--primary-color); + + svg { + width: 100%; + height: 100%; + } + } + } + + // 下拉动画 + .dropdown-slide-enter-active, + .dropdown-slide-leave-active { + transition: all 0.3s ease; + } + + .dropdown-slide-enter-from, + .dropdown-slide-leave-to { + opacity: 0; + transform: translateY(vh(-10)); + } + &__summary { position: relative; display: flex; @@ -205,7 +342,7 @@ const handleDistanceChange = (distance) => { display: flex; flex-direction: column; gap: vh(8); - max-height: 100px; + max-height: vw(120); overflow-y: auto; // 滚动条 @@ -259,29 +396,4 @@ const handleDistanceChange = (distance) => { } } } - -// Dropdown菜单样式覆盖 -:deep(.el-dropdown-menu) { - background: rgba(20, 53, 118, 0.95); - border: 1px solid rgba(255, 255, 255, 0.1); - padding: vh(4) 0; - - .el-dropdown-menu__item { - color: var(--text-white); - font-size: fs(14); - font-family: SourceHanSansCN-Medium, sans-serif; - padding: vh(8) vw(16); - - &:hover { - background: rgba(255, 255, 255, 0.1); - color: var(--text-white); - } - - &.is-active { - background: rgba(255, 255, 255, 0.15); - color: var(--el-color-primary); - font-weight: 600; - } - } -} diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/PageHeader.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/PageHeader.vue index f0d9975..6d360ca 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/PageHeader.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/PageHeader.vue @@ -44,7 +44,7 @@ const handleBack = () => { position: relative; width: 100%; height: vh(111); - background: url('../assets/images/b149e2d47f8744b5a916eb88fb4115cc_mergeImage.png') no-repeat; + background: url('../assets/images/一级标题栏bg.png') no-repeat; background-size: 100% 100%; display: flex; align-items: center; diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/DataField.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/DataField.vue index 14aaf08..8b8b28b 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/DataField.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/DataField.vue @@ -47,13 +47,13 @@ const valueClass = computed(() => { display: flex; align-items: center; gap: vw(8); - padding: vh(8) vw(10); + padding: vh(4) vw(10); background: url('../../assets/images/DataField/快速感知_bg.png') no-repeat center center; background-size: 100% 100%; &__icon { width: vw(24); - height: vh(30); + height: vh(24); flex-shrink: 0; } diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue index 253912d..79ed7b4 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue @@ -141,7 +141,7 @@ import { request } from "@shared/utils/request"; // 标记点图标 import emergencyCenterIcon from "./assets/images/应急中心.png"; -import dangerIcon from "./assets/images/danger.png"; +import eventIcon from "./assets/images/事件icon.png"; import soldierIcon from "./assets/images/SketchPngfbec927027ff9e49207749ebaafd229429315341fda199251b6dfb1723ff17fb.png"; import deviceIcon from "./assets/images/SketchPng860d54f2a31f5f441fc6a88081224f1e98534bf6d5ca1246e420983bdf690380.png"; import emergencyBaseIcon from "./assets/images/应急基地.png"; @@ -156,6 +156,11 @@ const handleDistanceChange = async (newDistance) => { // 更新搜索半径 disasterData.updateSearchRadius(newDistance); + // 更新范围圈 + if (mapStore.viewer) { + createOrUpdateRangeCircle(mapStore.viewer, newDistance); + } + // 重新加载应急资源数据并更新地图标记 await loadEmergencyResources(108.011506, 30.175827); }; @@ -188,6 +193,9 @@ const currentTooltipEntity = ref(null); // 加载动画状态 const showLoading = ref(false); +// 范围圈实体 +const rangeCircleEntity = ref(null); + // 3D Tiles加载功能 const { load3DTileset, waitForTilesetReady } = use3DTiles(); @@ -405,7 +413,7 @@ onMounted(() => { 0 ), billboard: { - image: dangerIcon, + image: eventIcon, width: 36, height: 36, verticalOrigin: Cesium.VerticalOrigin.BOTTOM, @@ -415,6 +423,53 @@ onMounted(() => { }); viewer.entities.add(defaultPoint); + // 在默认点附近添加10个模拟点位(应急人员和应急装备),分散在10km范围内 + // 1度纬度约等于111km,1度经度在30度纬度约等于96km + // 10km约等于0.09度纬度,0.104度经度 + const simulatedPoints = [ + // 应急人员 - 分散在不同方向 + { type: 'soldier', name: '张三', department: '应急救援队', lon: 108.051, lat: 30.205, distance: 4.2, icon: soldierIcon }, + { type: 'soldier', name: '李四', department: '消防队', lon: 107.975, lat: 30.195, distance: 5.8, icon: soldierIcon }, + { type: 'soldier', name: '王五', department: '医疗队', lon: 108.025, lat: 30.155, distance: 3.5, icon: soldierIcon }, + { type: 'soldier', name: '赵六', department: '应急救援队', lon: 108.085, lat: 30.168, distance: 7.2, icon: soldierIcon }, + { type: 'soldier', name: '刘七', department: '消防队', lon: 107.945, lat: 30.182, distance: 8.5, icon: soldierIcon }, + // 应急装备 - 分散在不同方向 + { type: 'device', name: '救援车辆A', deviceType: '消防车', lon: 108.065, lat: 30.185, distance: 6.3, icon: deviceIcon }, + { type: 'device', name: '救援车辆B', deviceType: '救护车', lon: 107.960, lat: 30.165, distance: 6.8, icon: deviceIcon }, + { type: 'device', name: '无人机A', deviceType: 'DJI', lon: 108.035, lat: 30.225, distance: 5.5, icon: deviceIcon }, + { type: 'device', name: '无人机B', deviceType: 'DJI', lon: 108.095, lat: 30.195, distance: 9.2, icon: deviceIcon }, + { type: 'device', name: '通讯设备', deviceType: '卫星电话', lon: 107.930, lat: 30.175, distance: 9.8, icon: deviceIcon } + ]; + + simulatedPoints.forEach(point => { + const entity = viewer.entities.add({ + position: Cesium.Cartesian3.fromDegrees(point.lon, point.lat, 0), + billboard: { + image: point.icon, + width: 36, + height: 40, + verticalOrigin: Cesium.VerticalOrigin.BOTTOM, + heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, + disableDepthTestDistance: Number.POSITIVE_INFINITY, + }, + properties: point.type === 'soldier' + ? { + type: 'soldier', + name: point.name, + department: point.department, + location: `目前为止距离现场${point.distance}公里` + } + : { + type: 'device', + name: point.name, + deviceType: point.deviceType, + location: `目前为止距离现场${point.distance}公里` + } + }); + }); + + console.log(`[index.vue] 已添加 ${simulatedPoints.length} 个模拟点位`); + // camera.setView({ // ...DEFAULT_CAMERA_VIEW, // }); @@ -514,6 +569,9 @@ onMounted(() => { // camera.setView(DEFAULT_CAMERA_VIEW) } } + + // 创建初始范围圈(使用当前搜索半径) + createOrUpdateRangeCircle(viewer, disasterData.forcePreset.value.searchRadius); }); }); @@ -678,6 +736,42 @@ const handleStartDispatch = (payload) => { }, 3000); }; +/** + * 创建或更新范围圈 + * @param {Cesium.Viewer} viewer - Cesium viewer 实例 + * @param {number} radiusKm - 半径(公里) + */ +const createOrUpdateRangeCircle = (viewer, radiusKm) => { + if (!viewer) return; + + const centerLon = 108.011506; + const centerLat = 30.175827; + const radiusMeters = radiusKm * 1000; + + // 如果已存在范围圈,先移除 + if (rangeCircleEntity.value) { + viewer.entities.remove(rangeCircleEntity.value); + rangeCircleEntity.value = null; + } + + // 创建新的范围圈 + rangeCircleEntity.value = viewer.entities.add({ + position: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 0), + ellipse: { + semiMinorAxis: radiusMeters, + semiMajorAxis: radiusMeters, + height: 0, + material: Cesium.Color.fromCssColorString('#1CA1FF').withAlpha(0.2), + outline: true, + outlineColor: Cesium.Color.fromCssColorString('#1CA1FF').withAlpha(0.8), + outlineWidth: 2, + heightReference: Cesium.HeightReference.CLAMP_TO_GROUND + } + }); + + console.log(`[index.vue] 已创建/更新范围圈: ${radiusKm}km`); +}; + /** * 在指定屏幕坐标显示地图 Tooltip *