Compare commits
No commits in common. "f6e40b8775bcedfdc055f05ecb1936e504f33212" and "3f9fea0db40d2c3f3a9b152bdb3b9c6edf46474a" have entirely different histories.
f6e40b8775
...
3f9fea0db4
@ -9,23 +9,6 @@
|
||||
:root {
|
||||
--cq-inline-100: 100vw;
|
||||
--cq-block-100: 100vh;
|
||||
|
||||
/* 3D 态势感知颜色变量 */
|
||||
--primary-color: rgba(28, 161, 255, 1);
|
||||
--primary-light: rgba(28, 161, 255, 0.44);
|
||||
--primary-lighter: rgba(28, 161, 255, 0.2);
|
||||
|
||||
--bg-dark: rgba(9, 22, 40, 1);
|
||||
--bg-panel: rgba(20, 53, 118, 1);
|
||||
|
||||
--text-white: rgba(255, 255, 255, 1);
|
||||
--text-gray: rgba(179, 204, 226, 1);
|
||||
|
||||
--success-color: rgba(17, 187, 119, 1);
|
||||
--warning-color: rgba(255, 128, 11, 1);
|
||||
--danger-color: rgba(255, 6, 36, 1);
|
||||
|
||||
--border-color: rgba(28, 161, 255, 0.3);
|
||||
}
|
||||
|
||||
* {
|
||||
|
||||
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 876 B After Width: | Height: | Size: 876 B |
|
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 466 B |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 544 B |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@ -2,13 +2,27 @@
|
||||
|
||||
/**
|
||||
* 3D态势感知公共样式
|
||||
*
|
||||
* 注意:CSS 变量(--primary-color, --text-white 等)已定义在全局样式中
|
||||
* 文件位置:/src/styles/index.scss
|
||||
*
|
||||
* 这样可以避免 Vue scoped 样式导致的 :root 变量不生效问题
|
||||
*/
|
||||
|
||||
// 公共颜色变量
|
||||
:root {
|
||||
--primary-color: rgba(28, 161, 255, 1);
|
||||
--primary-light: rgba(28, 161, 255, 0.44);
|
||||
--primary-lighter: rgba(28, 161, 255, 0.2);
|
||||
|
||||
--bg-dark: rgba(9, 22, 40, 1);
|
||||
--bg-panel: rgba(20, 53, 118, 1);
|
||||
|
||||
--text-white: rgba(255, 255, 255, 1);
|
||||
--text-gray: rgba(179, 204, 226, 1);
|
||||
|
||||
--success-color: rgba(17, 187, 119, 1);
|
||||
--warning-color: rgba(255, 128, 11, 1);
|
||||
--danger-color: rgba(255, 6, 36, 1);
|
||||
|
||||
--border-color: rgba(28, 161, 255, 0.3);
|
||||
}
|
||||
|
||||
// 公共面板样式
|
||||
.panel {
|
||||
display: flex;
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
<!-- 中央圆形元素 -->
|
||||
<div class="force-dispatch__center">
|
||||
<div class="force-dispatch__circle">
|
||||
<!-- <div class="force-dispatch__circle-ring"></div> -->
|
||||
<div class="force-dispatch__circle-ring"></div>
|
||||
<div class="force-dispatch__circle-text">
|
||||
<div class="force-dispatch__circle-line1">建议</div>
|
||||
<div class="force-dispatch__circle-line2">调度力量</div>
|
||||
@ -226,10 +226,9 @@ const handleStartDispatch = () => {
|
||||
.force-dispatch__middle {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
gap: vw(18);
|
||||
gap: vw(16);
|
||||
align-items: center;
|
||||
padding: vh(12) 0;
|
||||
// background: url('../../assets/images/ForceDispatchBg.png') center top no-repeat;
|
||||
}
|
||||
|
||||
/* 左右列统一样式 */
|
||||
@ -246,8 +245,6 @@ const handleStartDispatch = () => {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: vh(8) 0;
|
||||
background: url('../../assets/images/文本线条框.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.force-dispatch__stat-label {
|
||||
@ -281,13 +278,11 @@ const handleStartDispatch = () => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
background: url('../../assets/images/中心.png') no-repeat center center;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.force-dispatch__circle {
|
||||
position: relative;
|
||||
width: vw(140);
|
||||
width: vw(120);
|
||||
height: vh(120);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -374,17 +369,13 @@ const handleStartDispatch = () => {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: vw(16);
|
||||
// padding-top: vh(12);
|
||||
padding-top: vh(12);
|
||||
}
|
||||
|
||||
.force-dispatch__eta {
|
||||
display: flex;
|
||||
// flex-direction: column;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: vh(2);
|
||||
background: url('../../assets/images/文本线条框.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
padding: vh(8) vw(12);
|
||||
}
|
||||
|
||||
.force-dispatch__eta-label {
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
:key="station.id"
|
||||
class="station-item"
|
||||
>
|
||||
<img src="../../assets/images/路线icon.png" alt="station" class="station-icon" />
|
||||
<img src="../../assets/images/路线icon(1).png" alt="station" class="station-icon" />
|
||||
<div class="station-info">
|
||||
<span class="station-name">{{ station.name }}</span>
|
||||
<span class="station-distance">距离灾害点{{ station.distance }}公里</span>
|
||||
@ -118,7 +118,7 @@ const { forcePreset } = inject('disasterData')
|
||||
.stat-group {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: vw(32);
|
||||
gap: vw(12);
|
||||
|
||||
.stat-card {
|
||||
// flex: 1;
|
||||
@ -127,11 +127,11 @@ const { forcePreset } = inject('disasterData')
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: vh(8);
|
||||
padding: vh(12) vw(12) vh(30);
|
||||
padding: vh(12) vw(12);
|
||||
border-radius: vw(6);
|
||||
position: relative;
|
||||
background: url('../../assets/images/完成里程.png') no-repeat center bottom;
|
||||
// background-size: 100% 100%;
|
||||
background: url('../../assets/images/完成里程.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
width: vw(84);
|
||||
|
||||
.stat-label {
|
||||
@ -180,8 +180,7 @@ const { forcePreset } = inject('disasterData')
|
||||
align-items: center;
|
||||
gap: vw(12);
|
||||
padding: vh(10) vw(12);
|
||||
// background: rgba(20, 53, 118, 0.3);
|
||||
background: url('../../assets/images/文本线条框.png') no-repeat center center;
|
||||
background: rgba(20, 53, 118, 0.3);
|
||||
border-radius: vw(6);
|
||||
|
||||
.station-icon {
|
||||
|
||||
@ -26,7 +26,7 @@ import ForceDispatch from './ForceDispatch.vue'
|
||||
@use '../../assets/styles/common.scss' as *;
|
||||
|
||||
.left-panel {
|
||||
// width: vw(464);
|
||||
width: vw(464);
|
||||
height: 100%;
|
||||
background: url('../../assets/images/SketchPng7ba5c49d9f8f79e6b559d62cfb6b0b0c79616dd8b289f8b62b5cb8adc18c30e7.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { MAP_TOOLS, DEVICE_WATCH } from "../../constants.js";
|
||||
import { MAP_TOOLS, DEVICE_WATCH } from "../../constants";
|
||||
|
||||
// 卫星设备观看状态
|
||||
const isWatchingDevice = ref(false);
|
||||
@ -53,25 +53,16 @@ const activeTool = ref(null);
|
||||
// 定义对外事件
|
||||
const emit = defineEmits(["device-watch", "tool-change"]);
|
||||
|
||||
// 调试:检查常量是否正确加载
|
||||
console.log('MapControls - MAP_TOOLS:', MAP_TOOLS);
|
||||
console.log('MapControls - DEVICE_WATCH:', DEVICE_WATCH);
|
||||
|
||||
/**
|
||||
* 获取设备图标
|
||||
*/
|
||||
const getDeviceIcon = () => {
|
||||
const iconName = DEVICE_WATCH.icon;
|
||||
const state = isWatchingDevice.value ? "点亮" : "";
|
||||
try {
|
||||
return new URL(
|
||||
`../../assets/images/MapControls/${iconName}${state}.png`,
|
||||
import.meta.url
|
||||
).href;
|
||||
} catch (error) {
|
||||
console.error('加载设备图标失败:', error);
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -80,15 +71,10 @@ const getDeviceIcon = () => {
|
||||
const getToolIcon = (tool) => {
|
||||
const iconName = tool.icon;
|
||||
const state = activeTool.value === tool.key ? "点亮" : "";
|
||||
try {
|
||||
return new URL(
|
||||
`../../assets/images/MapControls/${iconName}${state}.png`,
|
||||
import.meta.url
|
||||
).href;
|
||||
} catch (error) {
|
||||
console.error('加载工具图标失败:', error, tool);
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -123,7 +109,11 @@ const handleToolClick = (toolKey) => {
|
||||
@use "../../assets/styles/common.scss" as *;
|
||||
|
||||
.map-controls {
|
||||
position: relative; // 改为相对定位,由父容器控制位置
|
||||
position: absolute;
|
||||
bottom: vh(30);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
gap: vw(14);
|
||||
align-items: flex-end;
|
||||
@ -246,7 +236,7 @@ const handleToolClick = (toolKey) => {
|
||||
|
||||
&__icon {
|
||||
width: vw(32);
|
||||
height: vw(32);
|
||||
height: vh(28);
|
||||
flex-shrink: 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
@ -1,30 +1,43 @@
|
||||
<template>
|
||||
<div class="map-viewer">
|
||||
<!-- Cesium 地图容器 -->
|
||||
<MapViewport />
|
||||
<div class="map-viewer__container">
|
||||
<!-- 这里放置实际的3D地图组件(如 Cesium, Mapbox GL JS 等) -->
|
||||
<div class="map-placeholder">
|
||||
</div>
|
||||
|
||||
<!-- 地图控制工具 - 使用 Teleport 传送到更高层级 -->
|
||||
<!-- 延迟渲染确保目标元素已存在 -->
|
||||
<Teleport to="#sa-controls" v-if="isMounted">
|
||||
<!-- 地图控制工具 -->
|
||||
<MapControls />
|
||||
</Teleport>
|
||||
|
||||
<!-- 地图标记点(应急人员、应急中心等) -->
|
||||
<div class="map-markers">
|
||||
<img
|
||||
src="../../assets/images/SketchPng9eb481bdb1aa555bcf1e817c3db9af492e273f88d5808c989826a8c382c5cb9f.png"
|
||||
alt="marker"
|
||||
class="map-marker"
|
||||
style="top: 40%; left: 45%"
|
||||
@click="handleMarkerClick('personnel')"
|
||||
/>
|
||||
<img
|
||||
src="../../assets/images/SketchPng3992df008169f438b4eab0a5f08b6d39b14f1387a18c08564067b7845d11b124.png"
|
||||
alt="center"
|
||||
class="map-marker"
|
||||
style="top: 60%; left: 55%"
|
||||
@click="handleMarkerClick('center')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { MapViewport } from '@/map'
|
||||
import MapControls from './MapControls.vue'
|
||||
|
||||
// 延迟标志,确保 Teleport 目标元素已存在
|
||||
const isMounted = ref(false)
|
||||
const emit = defineEmits(['marker-click'])
|
||||
|
||||
onMounted(() => {
|
||||
// 使用 nextTick 确保 DOM 完全渲染
|
||||
setTimeout(() => {
|
||||
isMounted.value = true
|
||||
}, 0)
|
||||
})
|
||||
const handleMarkerClick = (type) => {
|
||||
console.log('点击标记:', type)
|
||||
emit('marker-click', type)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -32,11 +45,83 @@ onMounted(() => {
|
||||
@use '../../assets/styles/common.scss' as *;
|
||||
|
||||
.map-viewer {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
&__container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: url(../../assets/images/SketchPng6e145958ea0dbf76e6562cc7965debbb95226caff3271c366ac9b254cbe6e796.png) center/cover no-repeat;
|
||||
overflow: hidden;
|
||||
|
||||
// MapViewport 会自动填满父容器
|
||||
// 地图相关的样式由 MapViewport 组件内部处理
|
||||
.map-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, rgba(9, 22, 40, 0.9) 0%, rgba(20, 53, 118, 0.7) 100%);
|
||||
position: relative;
|
||||
|
||||
// 实际使用时,这里会被3D地图组件替换
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: url(../../assets/images/SketchPng6e145958ea0dbf76e6562cc7965debbb95226caff3271c366ac9b254cbe6e796.png) center/cover;
|
||||
opacity: 0.1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.map-watermark {
|
||||
position: absolute;
|
||||
top: vh(20);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: vh(8) vw(20);
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: vw(6);
|
||||
color: var(--text-white);
|
||||
font-size: fs(14);
|
||||
font-family: SourceHanSansCN-Medium, sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.map-markers {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
|
||||
.map-marker {
|
||||
position: absolute;
|
||||
width: vw(32);
|
||||
height: vh(36);
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
transition: transform 0.3s;
|
||||
animation: markerBounce 2s ease-in-out infinite;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes markerBounce {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(vh(-10));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -6,23 +6,23 @@
|
||||
<span class="back-text">返回驾驶舱</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="page-header__center">
|
||||
<div class="logo-section">
|
||||
<img src="../assets/images/3ad857a9ed044c12b0e3b4345af6be59_mergeImage.png" alt="logo" class="logo-image" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-header__center">
|
||||
<h1 class="page-title">渝路智管-公路安全畅通运行管理</h1>
|
||||
</div>
|
||||
|
||||
<!-- <div class="page-header__right">
|
||||
<div class="page-header__right">
|
||||
<button class="scene-btn">
|
||||
<img src="../assets/images/SketchPng08621fb3b35614299e29352b8d67ad9c2c7dccf7b9c17d042492671e3bbe19f8.png" alt="scene" class="scene-icon" />
|
||||
<span class="scene-text">灾后现场实景</span>
|
||||
</button>
|
||||
|
||||
<img src="../assets/images/SketchPng0c172674e37bf751242a160c7adba8ee18f6f445e351e0cdb28dce03f8ee833e.png" alt="settings" class="settings-icon" />
|
||||
</div> -->
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
@ -61,10 +61,9 @@ const handleBack = () => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: vw(10);
|
||||
padding: vh(10) vw(24);
|
||||
margin-top: vh(20);
|
||||
background: url('../assets/images/返回按钮.png') 0 -1px no-repeat;
|
||||
background-size: 100% 100%;
|
||||
padding: vh(12) vw(24);
|
||||
background: url('../assets/images/SketchPnge54852f2dbf23aeabe7bfd58d2b0fad279041cbf01ce1a8908b70d93846e0a4c.png') 0 -1px no-repeat;
|
||||
background-size: vw(136) vh(46);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s;
|
||||
@ -100,16 +99,7 @@ const handleBack = () => {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.logo-section {
|
||||
.logo-image {
|
||||
width: vw(42);
|
||||
height: vh(30);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.page-title {
|
||||
background-image: linear-gradient(
|
||||
180deg,
|
||||
@ -138,7 +128,7 @@ const handleBack = () => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: vw(6);
|
||||
padding: vw(5);
|
||||
padding: vh(10) vw(16);
|
||||
background: rgba(20, 53, 118, 1);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: vw(8);
|
||||
@ -151,8 +141,8 @@ const handleBack = () => {
|
||||
}
|
||||
|
||||
.scene-icon {
|
||||
width: vw(32);
|
||||
height: vw(32);
|
||||
width: vw(17);
|
||||
height: vh(22);
|
||||
}
|
||||
|
||||
.scene-text {
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
<template>
|
||||
<div class="collaboration-info">
|
||||
<div class="collaboration-info__header">
|
||||
<img
|
||||
src="../../assets/images/SketchPng5d7d0c9a19ebbe31859bb19ed24fd41e757f04c7980ce640abb9c2c693b54728.png"
|
||||
alt="info"
|
||||
class="header-icon"
|
||||
/>
|
||||
<img src="../../assets/images/SketchPng5d7d0c9a19ebbe31859bb19ed24fd41e757f04c7980ce640abb9c2c693b54728.png" alt="info" class="header-icon" />
|
||||
</div>
|
||||
|
||||
<div class="collaboration-info__list">
|
||||
@ -14,11 +10,8 @@
|
||||
:key="info.id"
|
||||
class="info-item"
|
||||
>
|
||||
<span
|
||||
class="info-source"
|
||||
:style="{ color: getSourceColor(info.source) }"
|
||||
>[{{ info.source }}]</span>
|
||||
<span class="info-content">{{ info.content }}</span>
|
||||
<div class="info-source">[{{ info.source }}]</div>
|
||||
<div class="info-content">{{ info.content }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -28,22 +21,6 @@
|
||||
import { inject } from 'vue'
|
||||
|
||||
const { collaborationInfo } = inject('disasterData')
|
||||
|
||||
// 根据信息源返回对应的颜色
|
||||
const getSourceColor = (source) => {
|
||||
const colorMap = {
|
||||
'气象预警': '#4A9EFF', // 蓝色
|
||||
'气象部门': '#4A9EFF', // 蓝色
|
||||
'公安部门': '#FF5555', // 红色
|
||||
'交警部门': '#FF5555', // 红色
|
||||
'应急管理': '#FF5555', // 红色
|
||||
'融媒体中心': '#00D68F', // 绿色
|
||||
'新闻中心': '#00D68F', // 绿色
|
||||
'宣传部门': '#00D68F' // 绿色
|
||||
}
|
||||
|
||||
return colorMap[source] || '#4A9EFF' // <EFBFBD><EFBFBD><EFBFBD>认蓝色
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -51,21 +28,19 @@ const getSourceColor = (source) => {
|
||||
@use '../../assets/styles/common.scss' as *;
|
||||
|
||||
.collaboration-info {
|
||||
position: relative;
|
||||
margin-top: vh(12);
|
||||
background: url('../../assets/images/信息面板bg.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
padding: 2px;
|
||||
padding: vh(16) vw(20);
|
||||
margin-top: 0;
|
||||
background: rgba(20, 53, 118, 0.1);
|
||||
border-radius: vw(8);
|
||||
|
||||
&__header {
|
||||
position: absolute;
|
||||
top: vh(16);
|
||||
right: vw(5);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: vh(12);
|
||||
|
||||
.header-icon {
|
||||
width: vw(20);
|
||||
height: vh(20);
|
||||
width: vw(24);
|
||||
height: vh(24);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,9 +48,9 @@ const getSourceColor = (source) => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: vh(12);
|
||||
padding: vw(8) vw(16);
|
||||
max-height: vh(200);
|
||||
overflow-y: auto;
|
||||
padding-right: vw(8);
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: vw(4);
|
||||
@ -95,22 +70,25 @@ const getSourceColor = (source) => {
|
||||
}
|
||||
|
||||
.info-item {
|
||||
// padding: vh(12) vw(16);
|
||||
// background: rgba(20, 53, 118, 0.3);
|
||||
// border-radius: vw(4);
|
||||
// line-height: 1.8;
|
||||
padding: vh(12) vw(16);
|
||||
background: rgba(20, 53, 118, 0.3);
|
||||
border-left: vw(3) solid var(--primary-color);
|
||||
border-radius: vw(4);
|
||||
line-height: 1.6;
|
||||
|
||||
.info-source {
|
||||
color: var(--warning-color);
|
||||
font-size: fs(13);
|
||||
font-family: SourceHanSansCN-Bold, sans-serif;
|
||||
font-weight: 700;
|
||||
margin-right: vw(4); // 信息源和内容之间的小间距
|
||||
margin-bottom: vh(6);
|
||||
}
|
||||
|
||||
.info-content {
|
||||
color: var(--text-white);
|
||||
font-size: fs(12);
|
||||
font-family: SourceHanSansCN-Regular, sans-serif;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ const getColumnName = (type) => {
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
padding: vh(5) vw(16);
|
||||
padding: vh(10) vw(16);
|
||||
// background: rgba(20, 53, 118, 0.3);
|
||||
background: url('../../assets/images/tab未选中.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
@ -94,6 +94,7 @@ const getColumnName = (type) => {
|
||||
// background: var(--primary-light);
|
||||
background: url('../../assets/images/tab选中.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
border-bottom: vw(2) solid var(--primary-color);
|
||||
}
|
||||
|
||||
&:hover:not(&--active) {
|
||||
|
||||
@ -40,6 +40,6 @@ const handleZoom = (monitorId) => {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: vw(12);
|
||||
// padding: vh(16) vw(16);
|
||||
padding: vh(16) vw(16);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<div class="video-monitor-item__header">
|
||||
<span class="video-title">{{ monitor.title }}</span>
|
||||
<img
|
||||
src="../../assets/images/展开icon.png"
|
||||
src="../../assets/images/SketchPng753a456c1847586cb7f369e3b90a8459432a27811a579827ba86f9bb427841b2.png"
|
||||
alt="collapse"
|
||||
class="collapse-icon"
|
||||
/>
|
||||
@ -93,10 +93,8 @@ onUnmounted(() => {
|
||||
@use '../../assets/styles/common.scss' as *;
|
||||
|
||||
.video-monitor-item {
|
||||
// background: rgba(20, 53, 118, 0.3);
|
||||
background: url('../../assets/images/视频面板bg.png') no-repeat center center;
|
||||
background-size: cover;
|
||||
// border-radius: vw(8);
|
||||
background: rgba(20, 53, 118, 0.3);
|
||||
border-radius: vw(8);
|
||||
overflow: hidden;
|
||||
|
||||
&__header {
|
||||
@ -115,14 +113,14 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.collapse-icon {
|
||||
width: vw(26);
|
||||
height: vw(26);
|
||||
width: vw(16);
|
||||
height: vh(16);
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s;
|
||||
|
||||
// &:hover {
|
||||
// transform: rotate(180deg);
|
||||
// }
|
||||
&:hover {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
<DispatchCommand />
|
||||
</CollapsiblePanel>
|
||||
|
||||
<!-- <CollapsiblePanel title="现场监控" subtitle="「实时视频」">
|
||||
</CollapsiblePanel> -->
|
||||
<CollapsiblePanel title="现场监控" subtitle="「实时视频」">
|
||||
<VideoMonitorGrid />
|
||||
</CollapsiblePanel>
|
||||
|
||||
<!-- 如需启用调度建议面板,可取消下面注释 -->
|
||||
<!--
|
||||
@ -15,9 +15,9 @@
|
||||
</CollapsiblePanel>
|
||||
-->
|
||||
|
||||
<!-- <CollapsiblePanel title="协同信息" subtitle="「多部门联动」">
|
||||
</CollapsiblePanel> -->
|
||||
<CollapsiblePanel title="协同信息" subtitle="「多部门联动」">
|
||||
<CollaborationInfo />
|
||||
</CollapsiblePanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -34,7 +34,7 @@ import CollaborationInfo from './CollaborationInfo.vue'
|
||||
@use '../../assets/styles/common.scss' as *;
|
||||
|
||||
.right-panel {
|
||||
// width: vw(486);
|
||||
width: vw(486);
|
||||
height: 100%;
|
||||
background: url('../../assets/images/SketchPngab2bc23b7e477ddbee76b880e28c1c97d6afb9261784dec29ed08c4e0a34d5b3.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
@ -207,31 +207,12 @@ function onAfterLeave(el) {
|
||||
}
|
||||
|
||||
&__body {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: height 0.3s ease;
|
||||
|
||||
// 使用伪元素作为背景层,九宫格实现
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-style: solid;
|
||||
border-width: vh(25) vw(30);
|
||||
border-image-source: url('../../assets/images/面板bg.png');
|
||||
border-image-slice: 25 30 25 30 fill;
|
||||
border-image-width: vh(25) vw(30);
|
||||
border-image-repeat: stretch;
|
||||
box-sizing: border-box;
|
||||
pointer-events: none; // 不阻挡鼠标事件
|
||||
z-index: -1; // 放在内容下方
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
position: relative;
|
||||
padding: vh(5) vw(20) vh(15); // 留出空间避免被圆角遮挡
|
||||
z-index: 1;
|
||||
padding: vh(12) vw(5) vh(20);
|
||||
|
||||
// 如果内容需要滚动
|
||||
&.scrollable {
|
||||
@ -245,7 +226,6 @@ function onAfterLeave(el) {
|
||||
&--collapsed {
|
||||
.collapsible-panel__body {
|
||||
height: 0;
|
||||
border-width: 0; // 折叠时移除 border
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,29 +5,14 @@
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<div class="situational-awareness__main">
|
||||
<!-- 地图底层 -->
|
||||
<div class="situational-awareness__map-layer">
|
||||
<MapViewer />
|
||||
</div>
|
||||
|
||||
<!-- 地图遮罩层 -->
|
||||
<!-- <div class="situational-awareness__map-mask" aria-hidden="true"></div> -->
|
||||
|
||||
<!-- 浮动面板层 -->
|
||||
<div class="situational-awareness__panels-layer">
|
||||
<div class="situational-awareness__panel-column situational-awareness__panel-column--left">
|
||||
<!-- 左侧面板 -->
|
||||
<LeftPanel />
|
||||
</div>
|
||||
<div class="situational-awareness__center-spacer" aria-hidden="true"></div>
|
||||
<div class="situational-awareness__panel-column situational-awareness__panel-column--right">
|
||||
<RightPanel />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 地图控件层 - 高于遮罩和面板 -->
|
||||
<div class="situational-awareness__controls-layer">
|
||||
<div id="sa-controls" class="situational-awareness__controls"></div>
|
||||
</div>
|
||||
<!-- 中央地图区域 -->
|
||||
<MapViewer @marker-click="handleMarkerClick" />
|
||||
|
||||
<!-- 右侧面板 -->
|
||||
<RightPanel />
|
||||
</div>
|
||||
|
||||
<!-- 弹窗组件 -->
|
||||
@ -47,7 +32,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, provide, onMounted } from 'vue'
|
||||
import { ref, provide } from 'vue'
|
||||
import PageHeader from './components/PageHeader.vue'
|
||||
import LeftPanel from './components/LeftPanel/index.vue'
|
||||
import MapViewer from './components/MapViewer/index.vue'
|
||||
@ -55,7 +40,6 @@ import RightPanel from './components/RightPanel/index.vue'
|
||||
import PersonnelDetail from './components/Popups/PersonnelDetail.vue'
|
||||
import EmergencyCenterDetail from './components/Popups/EmergencyCenterDetail.vue'
|
||||
import { useDisasterData } from './composables/useDisasterData'
|
||||
import { useMapStore } from '@/map'
|
||||
|
||||
// 使用灾害数据
|
||||
const disasterData = useDisasterData()
|
||||
@ -63,23 +47,6 @@ const disasterData = useDisasterData()
|
||||
// 提供给子组件使用
|
||||
provide('disasterData', disasterData)
|
||||
|
||||
// 地图 store
|
||||
const mapStore = useMapStore()
|
||||
|
||||
// 初始化地图
|
||||
onMounted(() => {
|
||||
// 等待地图就绪后配置初始视图
|
||||
mapStore.onReady(() => {
|
||||
const { camera } = mapStore.services()
|
||||
|
||||
// 设置初始相机位置(以重庆忠县为例,可根据实际需求调整)
|
||||
// 经度: 108.0, 纬度: 30.3, 高度: 50000 米
|
||||
camera.setCenter(108.0, 30.3, 50000)
|
||||
|
||||
console.log('3D态势感知地图已就绪')
|
||||
})
|
||||
})
|
||||
|
||||
// 弹窗状态
|
||||
const showPersonnelDetail = ref(false)
|
||||
const showCenterDetail = ref(false)
|
||||
@ -108,30 +75,21 @@ const handleBack = () => {
|
||||
// router.push('/cockpit')
|
||||
}
|
||||
|
||||
// 处理地图标记点击
|
||||
const handleMarkerClick = (type) => {
|
||||
if (type === 'personnel') {
|
||||
showPersonnelDetail.value = true
|
||||
} else if (type === 'center') {
|
||||
showCenterDetail.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// 处理人员联动
|
||||
const handlePersonnelLink = (personnel) => {
|
||||
console.log('联动人员:', personnel)
|
||||
// 实际实现:使用 mapStore 的 camera service 飞行到人员位置
|
||||
// const { camera } = mapStore.services()
|
||||
// camera.flyTo({ destination: [lon, lat, height] })
|
||||
// 实际实现:在地图上定位该人员,调用通信功能等
|
||||
showPersonnelDetail.value = false
|
||||
}
|
||||
|
||||
// TODO: 实现地图实体点击事件监听
|
||||
// 当用户点击地图上的标记点时,显示对应的详情弹窗
|
||||
// mapStore.onReady(() => {
|
||||
// const { query } = mapStore.services()
|
||||
// // 监听实体点击事件
|
||||
// query.onEntityClick((entity) => {
|
||||
// if (entity.type === 'personnel') {
|
||||
// selectedPersonnel.value = entity.properties
|
||||
// showPersonnelDetail.value = true
|
||||
// } else if (entity.type === 'center') {
|
||||
// selectedCenter.value = entity.properties
|
||||
// showCenterDetail.value = true
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -139,133 +97,41 @@ const handlePersonnelLink = (personnel) => {
|
||||
@use './assets/styles/common.scss' as *;
|
||||
|
||||
.situational-awareness {
|
||||
// 容器查询设置,用于嵌入场景的自适应缩放
|
||||
container-name: situational-awareness;
|
||||
container-type: size;
|
||||
|
||||
// 为旧版浏览器提供视口单位回退
|
||||
--cq-inline-100: 100vw;
|
||||
--cq-block-100: 100vh;
|
||||
|
||||
// 当支持容器单位时覆盖为容器单位
|
||||
@supports (width: 1cqw) {
|
||||
--cq-inline-100: 100cqw;
|
||||
}
|
||||
@supports (height: 1cqh) {
|
||||
--cq-block-100: 100cqh;
|
||||
}
|
||||
|
||||
// 可配置的布局变量(使用 calc 直接计算,避免函数嵌套)
|
||||
--sa-left-width: calc(464 / 1920 * var(--cq-inline-100, 100vw));
|
||||
--sa-right-width: calc(486 / 1920 * var(--cq-inline-100, 100vw));
|
||||
--sa-gap: calc(16 / 1920 * var(--cq-inline-100, 100vw));
|
||||
--sa-padding: calc(16 / 1920 * var(--cq-inline-100, 100vw));
|
||||
--sa-header-height: calc(121 / 1080 * var(--cq-block-100, 100vh)); // Header 高度
|
||||
--sa-min-width: 1280px;
|
||||
--sa-min-height: 720px;
|
||||
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: var(--sa-min-width);
|
||||
min-height: var(--sa-min-height);
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: var(--bg-dark);
|
||||
overflow: auto; // 当宿主尺寸 < 最小尺寸时允许滚动
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
// PageHeader 浮在顶部
|
||||
> :first-child {
|
||||
&__main {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: vw(564) 1fr vw(526);
|
||||
gap: 0;
|
||||
min-height: 0;
|
||||
background: url(./assets/images/main-bg.png) center/cover no-repeat;
|
||||
position: relative;
|
||||
|
||||
// 背景地图图片
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&__main {
|
||||
position: absolute;
|
||||
inset: 0; // 铺满整个容器
|
||||
background: url(./assets/images/main-bg.png) center/cover no-repeat;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 地图底层 - 填满整个容器
|
||||
&__map-layer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
bottom: 0;
|
||||
background: url(../3DSituationalAwarenessCopy/assets/img/SketchPng6e145958ea0dbf76e6562cc7965debbb95226caff3271c366ac9b254cbe6e796.png)
|
||||
center/cover no-repeat;
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
// 地图遮罩层 - 覆盖在地图之上,增强视觉效果
|
||||
&__map-mask {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none; // 不阻挡交互
|
||||
// 使用 cockpit 的遮罩层图片,保持视觉一致性
|
||||
background: url(@/views/cockpit/assets/img/遮罩层.png) center/cover no-repeat;
|
||||
}
|
||||
|
||||
// 浮动面板层 - grid 与 pointer-events 结合保证中间透明
|
||||
&__panels-layer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 2;
|
||||
display: grid;
|
||||
grid-template-columns: var(--sa-left-width) 1fr var(--sa-right-width);
|
||||
grid-auto-rows: 1fr;
|
||||
gap: var(--sa-gap); // 列之间的间距
|
||||
height: 100%;
|
||||
padding-top: var(--sa-header-height); // 预留 Header 高度
|
||||
pointer-events: none; // 容器不拦截事件,让中间区域透明
|
||||
}
|
||||
|
||||
// 左右面板列 - 浮动卡片样式
|
||||
&__panel-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sa-gap); // 列内子面板之间的间距
|
||||
min-width: 0; // 防止在窄容器中溢出
|
||||
min-height: 0; // 允许 flex 子元素收缩并启用滚动
|
||||
pointer-events: auto; // 恢复面板的交互能力
|
||||
}
|
||||
|
||||
// 中间占位区域 - 透明且不可交互,点击穿透到地图
|
||||
&__center-spacer {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// 地图控件层 - 高于遮罩和面板,用于放置地图控制工具
|
||||
&__controls-layer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 3;
|
||||
pointer-events: none; // 容器不拦截事件
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
padding-bottom: 30px; // 临时使用固定值,确保控件显示
|
||||
}
|
||||
|
||||
// 控件容器 - 恢复交互能力
|
||||
&__controls {
|
||||
pointer-events: auto;
|
||||
> * {
|
||||
position: relative;
|
||||
// 调试:确保控件容器可见
|
||||
min-height: 56px; // MapControls 的高度
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 窄容器嵌入的紧凑布局(<1100px 宽度)
|
||||
.situational-awareness.is-compact {
|
||||
--sa-left-width: calc(380 / 1920 * var(--cq-inline-100, 100vw));
|
||||
--sa-right-width: calc(400 / 1920 * var(--cq-inline-100, 100vw));
|
||||
--sa-gap: calc(12 / 1920 * var(--cq-inline-100, 100vw));
|
||||
--sa-padding: calc(12 / 1920 * var(--cq-inline-100, 100vw));
|
||||
}
|
||||
|
||||
// 嵌入模式 - 使用更保守的最小尺寸
|
||||
.situational-awareness.is-embedded {
|
||||
--sa-min-width: 1024px;
|
||||
--sa-min-height: 600px;
|
||||
}
|
||||
</style>
|
||||
|
||||