Compare commits

..

No commits in common. "f6e40b8775bcedfdc055f05ecb1936e504f33212" and "3f9fea0db40d2c3f3a9b152bdb3b9c6edf46474a" have entirely different histories.

30 changed files with 249 additions and 374 deletions

View File

@ -9,23 +9,6 @@
:root { :root {
--cq-inline-100: 100vw; --cq-inline-100: 100vw;
--cq-block-100: 100vh; --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);
} }
* { * {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -2,13 +2,27 @@
/** /**
* 3D态势感知公共样式 * 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 { .panel {
display: flex; display: flex;

View File

@ -46,7 +46,7 @@
<!-- 中央圆形元素 --> <!-- 中央圆形元素 -->
<div class="force-dispatch__center"> <div class="force-dispatch__center">
<div class="force-dispatch__circle"> <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-text">
<div class="force-dispatch__circle-line1">建议</div> <div class="force-dispatch__circle-line1">建议</div>
<div class="force-dispatch__circle-line2">调度力量</div> <div class="force-dispatch__circle-line2">调度力量</div>
@ -226,10 +226,9 @@ const handleStartDispatch = () => {
.force-dispatch__middle { .force-dispatch__middle {
display: grid; display: grid;
grid-template-columns: 1fr auto 1fr; grid-template-columns: 1fr auto 1fr;
gap: vw(18); gap: vw(16);
align-items: center; align-items: center;
padding: vh(12) 0; padding: vh(12) 0;
// background: url('../../assets/images/ForceDispatchBg.png') center top no-repeat;
} }
/* 左右列统一样式 */ /* 左右列统一样式 */
@ -246,8 +245,6 @@ const handleStartDispatch = () => {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: vh(8) 0; padding: vh(8) 0;
background: url('../../assets/images/文本线条框.png') no-repeat center center;
background-size: 100% 100%;
} }
.force-dispatch__stat-label { .force-dispatch__stat-label {
@ -281,13 +278,11 @@ const handleStartDispatch = () => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
position: relative; position: relative;
background: url('../../assets/images/中心.png') no-repeat center center;
background-size: contain;
} }
.force-dispatch__circle { .force-dispatch__circle {
position: relative; position: relative;
width: vw(140); width: vw(120);
height: vh(120); height: vh(120);
display: flex; display: flex;
align-items: center; align-items: center;
@ -374,17 +369,13 @@ const handleStartDispatch = () => {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
gap: vw(16); gap: vw(16);
// padding-top: vh(12); padding-top: vh(12);
} }
.force-dispatch__eta { .force-dispatch__eta {
display: flex; display: flex;
// flex-direction: column; flex-direction: column;
align-items: center;
gap: vh(2); 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 { .force-dispatch__eta-label {

View File

@ -42,7 +42,7 @@
:key="station.id" :key="station.id"
class="station-item" 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"> <div class="station-info">
<span class="station-name">{{ station.name }}</span> <span class="station-name">{{ station.name }}</span>
<span class="station-distance">距离灾害点{{ station.distance }}公里</span> <span class="station-distance">距离灾害点{{ station.distance }}公里</span>
@ -118,7 +118,7 @@ const { forcePreset } = inject('disasterData')
.stat-group { .stat-group {
display: flex; display: flex;
justify-content: center; justify-content: center;
gap: vw(32); gap: vw(12);
.stat-card { .stat-card {
// flex: 1; // flex: 1;
@ -127,11 +127,11 @@ const { forcePreset } = inject('disasterData')
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: vh(8); gap: vh(8);
padding: vh(12) vw(12) vh(30); padding: vh(12) vw(12);
border-radius: vw(6); border-radius: vw(6);
position: relative; position: relative;
background: url('../../assets/images/完成里程.png') no-repeat center bottom; background: url('../../assets/images/完成里程.png') no-repeat center center;
// background-size: 100% 100%; background-size: 100% 100%;
width: vw(84); width: vw(84);
.stat-label { .stat-label {
@ -180,8 +180,7 @@ const { forcePreset } = inject('disasterData')
align-items: center; align-items: center;
gap: vw(12); gap: vw(12);
padding: vh(10) vw(12); padding: vh(10) vw(12);
// background: rgba(20, 53, 118, 0.3); background: rgba(20, 53, 118, 0.3);
background: url('../../assets/images/文本线条框.png') no-repeat center center;
border-radius: vw(6); border-radius: vw(6);
.station-icon { .station-icon {

View File

@ -26,7 +26,7 @@ import ForceDispatch from './ForceDispatch.vue'
@use '../../assets/styles/common.scss' as *; @use '../../assets/styles/common.scss' as *;
.left-panel { .left-panel {
// width: vw(464); width: vw(464);
height: 100%; height: 100%;
background: url('../../assets/images/SketchPng7ba5c49d9f8f79e6b559d62cfb6b0b0c79616dd8b289f8b62b5cb8adc18c30e7.png') no-repeat; background: url('../../assets/images/SketchPng7ba5c49d9f8f79e6b559d62cfb6b0b0c79616dd8b289f8b62b5cb8adc18c30e7.png') no-repeat;
background-size: 100% 100%; background-size: 100% 100%;

View File

@ -42,7 +42,7 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import { MAP_TOOLS, DEVICE_WATCH } from "../../constants.js"; import { MAP_TOOLS, DEVICE_WATCH } from "../../constants";
// //
const isWatchingDevice = ref(false); const isWatchingDevice = ref(false);
@ -53,25 +53,16 @@ const activeTool = ref(null);
// //
const emit = defineEmits(["device-watch", "tool-change"]); const emit = defineEmits(["device-watch", "tool-change"]);
//
console.log('MapControls - MAP_TOOLS:', MAP_TOOLS);
console.log('MapControls - DEVICE_WATCH:', DEVICE_WATCH);
/** /**
* 获取设备图标 * 获取设备图标
*/ */
const getDeviceIcon = () => { const getDeviceIcon = () => {
const iconName = DEVICE_WATCH.icon; const iconName = DEVICE_WATCH.icon;
const state = isWatchingDevice.value ? "点亮" : ""; const state = isWatchingDevice.value ? "点亮" : "";
try {
return new URL( return new URL(
`../../assets/images/MapControls/${iconName}${state}.png`, `../../assets/images/MapControls/${iconName}${state}.png`,
import.meta.url import.meta.url
).href; ).href;
} catch (error) {
console.error('加载设备图标失败:', error);
return '';
}
}; };
/** /**
@ -80,15 +71,10 @@ const getDeviceIcon = () => {
const getToolIcon = (tool) => { const getToolIcon = (tool) => {
const iconName = tool.icon; const iconName = tool.icon;
const state = activeTool.value === tool.key ? "点亮" : ""; const state = activeTool.value === tool.key ? "点亮" : "";
try {
return new URL( return new URL(
`../../assets/images/MapControls/${iconName}${state}.png`, `../../assets/images/MapControls/${iconName}${state}.png`,
import.meta.url import.meta.url
).href; ).href;
} catch (error) {
console.error('加载工具图标失败:', error, tool);
return '';
}
}; };
/** /**
@ -123,7 +109,11 @@ const handleToolClick = (toolKey) => {
@use "../../assets/styles/common.scss" as *; @use "../../assets/styles/common.scss" as *;
.map-controls { .map-controls {
position: relative; // position: absolute;
bottom: vh(30);
left: 50%;
transform: translateX(-50%);
z-index: 10;
display: flex; display: flex;
gap: vw(14); gap: vw(14);
align-items: flex-end; align-items: flex-end;
@ -246,7 +236,7 @@ const handleToolClick = (toolKey) => {
&__icon { &__icon {
width: vw(32); width: vw(32);
height: vw(32); height: vh(28);
flex-shrink: 0; flex-shrink: 0;
transition: all 0.3s ease; transition: all 0.3s ease;
} }

View File

@ -1,30 +1,43 @@
<template> <template>
<div class="map-viewer"> <div class="map-viewer">
<!-- Cesium 地图容器 --> <div class="map-viewer__container">
<MapViewport /> <!-- 这里放置实际的3D地图组件 Cesium, Mapbox GL JS -->
<div class="map-placeholder">
</div>
<!-- 地图控制工具 - 使用 Teleport 传送到更高层级 --> <!-- 地图控制工具 -->
<!-- 延迟渲染确保目标元素已存在 -->
<Teleport to="#sa-controls" v-if="isMounted">
<MapControls /> <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> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from 'vue'
import { MapViewport } from '@/map'
import MapControls from './MapControls.vue' import MapControls from './MapControls.vue'
// Teleport const emit = defineEmits(['marker-click'])
const isMounted = ref(false)
onMounted(() => { const handleMarkerClick = (type) => {
// 使 nextTick DOM console.log('点击标记:', type)
setTimeout(() => { emit('marker-click', type)
isMounted.value = true }
}, 0)
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -32,11 +45,83 @@ onMounted(() => {
@use '../../assets/styles/common.scss' as *; @use '../../assets/styles/common.scss' as *;
.map-viewer { .map-viewer {
flex: 1;
height: 100%;
position: relative; position: relative;
&__container {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative;
background: url(../../assets/images/SketchPng6e145958ea0dbf76e6562cc7965debbb95226caff3271c366ac9b254cbe6e796.png) center/cover no-repeat;
overflow: hidden;
// MapViewport .map-placeholder {
// MapViewport 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> </style>

View File

@ -6,23 +6,23 @@
<span class="back-text">返回驾驶舱</span> <span class="back-text">返回驾驶舱</span>
</button> </button>
</div>
<div class="page-header__center">
<div class="logo-section"> <div class="logo-section">
<img src="../assets/images/3ad857a9ed044c12b0e3b4345af6be59_mergeImage.png" alt="logo" class="logo-image" /> <img src="../assets/images/3ad857a9ed044c12b0e3b4345af6be59_mergeImage.png" alt="logo" class="logo-image" />
</div> </div>
</div>
<div class="page-header__center">
<h1 class="page-title">渝路智管-公路安全畅通运行管理</h1> <h1 class="page-title">渝路智管-公路安全畅通运行管理</h1>
</div> </div>
<!-- <div class="page-header__right"> <div class="page-header__right">
<button class="scene-btn"> <button class="scene-btn">
<img src="../assets/images/SketchPng08621fb3b35614299e29352b8d67ad9c2c7dccf7b9c17d042492671e3bbe19f8.png" alt="scene" class="scene-icon" /> <img src="../assets/images/SketchPng08621fb3b35614299e29352b8d67ad9c2c7dccf7b9c17d042492671e3bbe19f8.png" alt="scene" class="scene-icon" />
<span class="scene-text">灾后现场实景</span> <span class="scene-text">灾后现场实景</span>
</button> </button>
<img src="../assets/images/SketchPng0c172674e37bf751242a160c7adba8ee18f6f445e351e0cdb28dce03f8ee833e.png" alt="settings" class="settings-icon" /> <img src="../assets/images/SketchPng0c172674e37bf751242a160c7adba8ee18f6f445e351e0cdb28dce03f8ee833e.png" alt="settings" class="settings-icon" />
</div> --> </div>
</header> </header>
</template> </template>
@ -61,10 +61,9 @@ const handleBack = () => {
display: flex; display: flex;
align-items: center; align-items: center;
gap: vw(10); gap: vw(10);
padding: vh(10) vw(24); padding: vh(12) vw(24);
margin-top: vh(20); background: url('../assets/images/SketchPnge54852f2dbf23aeabe7bfd58d2b0fad279041cbf01ce1a8908b70d93846e0a4c.png') 0 -1px no-repeat;
background: url('../assets/images/返回按钮.png') 0 -1px no-repeat; background-size: vw(136) vh(46);
background-size: 100% 100%;
border: none; border: none;
cursor: pointer; cursor: pointer;
transition: opacity 0.3s; transition: opacity 0.3s;
@ -100,16 +99,7 @@ const handleBack = () => {
left: 50%; left: 50%;
top: 50%; top: 50%;
transform: translate(-50%, -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 { .page-title {
background-image: linear-gradient( background-image: linear-gradient(
180deg, 180deg,
@ -138,7 +128,7 @@ const handleBack = () => {
display: flex; display: flex;
align-items: center; align-items: center;
gap: vw(6); gap: vw(6);
padding: vw(5); padding: vh(10) vw(16);
background: rgba(20, 53, 118, 1); background: rgba(20, 53, 118, 1);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: vw(8); border-radius: vw(8);
@ -151,8 +141,8 @@ const handleBack = () => {
} }
.scene-icon { .scene-icon {
width: vw(32); width: vw(17);
height: vw(32); height: vh(22);
} }
.scene-text { .scene-text {

View File

@ -1,11 +1,7 @@
<template> <template>
<div class="collaboration-info"> <div class="collaboration-info">
<div class="collaboration-info__header"> <div class="collaboration-info__header">
<img <img src="../../assets/images/SketchPng5d7d0c9a19ebbe31859bb19ed24fd41e757f04c7980ce640abb9c2c693b54728.png" alt="info" class="header-icon" />
src="../../assets/images/SketchPng5d7d0c9a19ebbe31859bb19ed24fd41e757f04c7980ce640abb9c2c693b54728.png"
alt="info"
class="header-icon"
/>
</div> </div>
<div class="collaboration-info__list"> <div class="collaboration-info__list">
@ -14,11 +10,8 @@
:key="info.id" :key="info.id"
class="info-item" class="info-item"
> >
<span <div class="info-source">[{{ info.source }}]</div>
class="info-source" <div class="info-content">{{ info.content }}</div>
:style="{ color: getSourceColor(info.source) }"
>[{{ info.source }}]</span>
<span class="info-content">{{ info.content }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -28,22 +21,6 @@
import { inject } from 'vue' import { inject } from 'vue'
const { collaborationInfo } = inject('disasterData') 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> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -51,21 +28,19 @@ const getSourceColor = (source) => {
@use '../../assets/styles/common.scss' as *; @use '../../assets/styles/common.scss' as *;
.collaboration-info { .collaboration-info {
position: relative; padding: vh(16) vw(20);
margin-top: vh(12); margin-top: 0;
background: url('../../assets/images/信息面板bg.png') no-repeat center center; background: rgba(20, 53, 118, 0.1);
background-size: 100% 100%; border-radius: vw(8);
padding: 2px;
&__header { &__header {
position: absolute; display: flex;
top: vh(16); justify-content: flex-end;
right: vw(5); margin-bottom: vh(12);
z-index: 1;
.header-icon { .header-icon {
width: vw(20); width: vw(24);
height: vh(20); height: vh(24);
} }
} }
@ -73,9 +48,9 @@ const getSourceColor = (source) => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: vh(12); gap: vh(12);
padding: vw(8) vw(16);
max-height: vh(200); max-height: vh(200);
overflow-y: auto; overflow-y: auto;
padding-right: vw(8);
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: vw(4); width: vw(4);
@ -95,22 +70,25 @@ const getSourceColor = (source) => {
} }
.info-item { .info-item {
// padding: vh(12) vw(16); padding: vh(12) vw(16);
// background: rgba(20, 53, 118, 0.3); background: rgba(20, 53, 118, 0.3);
// border-radius: vw(4); border-left: vw(3) solid var(--primary-color);
// line-height: 1.8; border-radius: vw(4);
line-height: 1.6;
.info-source { .info-source {
color: var(--warning-color);
font-size: fs(13); font-size: fs(13);
font-family: SourceHanSansCN-Bold, sans-serif; font-family: SourceHanSansCN-Bold, sans-serif;
font-weight: 700; font-weight: 700;
margin-right: vw(4); // margin-bottom: vh(6);
} }
.info-content { .info-content {
color: var(--text-white); color: var(--text-white);
font-size: fs(12); font-size: fs(12);
font-family: SourceHanSansCN-Regular, sans-serif; font-family: SourceHanSansCN-Regular, sans-serif;
line-height: 1.8;
} }
} }
} }

View File

@ -75,7 +75,7 @@ const getColumnName = (type) => {
.tab-item { .tab-item {
flex: 1; flex: 1;
padding: vh(5) vw(16); padding: vh(10) vw(16);
// background: rgba(20, 53, 118, 0.3); // background: rgba(20, 53, 118, 0.3);
background: url('../../assets/images/tab未选中.png') no-repeat center center; background: url('../../assets/images/tab未选中.png') no-repeat center center;
background-size: 100% 100%; background-size: 100% 100%;
@ -94,6 +94,7 @@ const getColumnName = (type) => {
// background: var(--primary-light); // background: var(--primary-light);
background: url('../../assets/images/tab选中.png') no-repeat center center; background: url('../../assets/images/tab选中.png') no-repeat center center;
background-size: 100% 100%; background-size: 100% 100%;
border-bottom: vw(2) solid var(--primary-color);
} }
&:hover:not(&--active) { &:hover:not(&--active) {

View File

@ -40,6 +40,6 @@ const handleZoom = (monitorId) => {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: vw(12); gap: vw(12);
// padding: vh(16) vw(16); padding: vh(16) vw(16);
} }
</style> </style>

View File

@ -3,7 +3,7 @@
<div class="video-monitor-item__header"> <div class="video-monitor-item__header">
<span class="video-title">{{ monitor.title }}</span> <span class="video-title">{{ monitor.title }}</span>
<img <img
src="../../assets/images/展开icon.png" src="../../assets/images/SketchPng753a456c1847586cb7f369e3b90a8459432a27811a579827ba86f9bb427841b2.png"
alt="collapse" alt="collapse"
class="collapse-icon" class="collapse-icon"
/> />
@ -93,10 +93,8 @@ onUnmounted(() => {
@use '../../assets/styles/common.scss' as *; @use '../../assets/styles/common.scss' as *;
.video-monitor-item { .video-monitor-item {
// background: rgba(20, 53, 118, 0.3); background: rgba(20, 53, 118, 0.3);
background: url('../../assets/images/视频面板bg.png') no-repeat center center; border-radius: vw(8);
background-size: cover;
// border-radius: vw(8);
overflow: hidden; overflow: hidden;
&__header { &__header {
@ -115,14 +113,14 @@ onUnmounted(() => {
} }
.collapse-icon { .collapse-icon {
width: vw(26); width: vw(16);
height: vw(26); height: vh(16);
cursor: pointer; cursor: pointer;
transition: transform 0.3s; transition: transform 0.3s;
// &:hover { &:hover {
// transform: rotate(180deg); transform: rotate(180deg);
// } }
} }
} }

View File

@ -4,9 +4,9 @@
<DispatchCommand /> <DispatchCommand />
</CollapsiblePanel> </CollapsiblePanel>
<!-- <CollapsiblePanel title="现场监控" subtitle="「实时视频」"> <CollapsiblePanel title="现场监控" subtitle="「实时视频」">
</CollapsiblePanel> -->
<VideoMonitorGrid /> <VideoMonitorGrid />
</CollapsiblePanel>
<!-- 如需启用调度建议面板可取消下面注释 --> <!-- 如需启用调度建议面板可取消下面注释 -->
<!-- <!--
@ -15,9 +15,9 @@
</CollapsiblePanel> </CollapsiblePanel>
--> -->
<!-- <CollapsiblePanel title="协同信息" subtitle="「多部门联动」"> <CollapsiblePanel title="协同信息" subtitle="「多部门联动」">
</CollapsiblePanel> -->
<CollaborationInfo /> <CollaborationInfo />
</CollapsiblePanel>
</div> </div>
</template> </template>
@ -34,7 +34,7 @@ import CollaborationInfo from './CollaborationInfo.vue'
@use '../../assets/styles/common.scss' as *; @use '../../assets/styles/common.scss' as *;
.right-panel { .right-panel {
// width: vw(486); width: vw(486);
height: 100%; height: 100%;
background: url('../../assets/images/SketchPngab2bc23b7e477ddbee76b880e28c1c97d6afb9261784dec29ed08c4e0a34d5b3.png') no-repeat; background: url('../../assets/images/SketchPngab2bc23b7e477ddbee76b880e28c1c97d6afb9261784dec29ed08c4e0a34d5b3.png') no-repeat;
background-size: 100% 100%; background-size: 100% 100%;

View File

@ -207,31 +207,12 @@ function onAfterLeave(el) {
} }
&__body { &__body {
position: relative;
overflow: hidden; overflow: hidden;
transition: height 0.3s ease; 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 { &__content {
position: relative; padding: vh(12) vw(5) vh(20);
padding: vh(5) vw(20) vh(15); //
z-index: 1;
// //
&.scrollable { &.scrollable {
@ -245,7 +226,6 @@ function onAfterLeave(el) {
&--collapsed { &--collapsed {
.collapsible-panel__body { .collapsible-panel__body {
height: 0; height: 0;
border-width: 0; // border
} }
} }
} }

View File

@ -5,29 +5,14 @@
<!-- 主内容区域 --> <!-- 主内容区域 -->
<div class="situational-awareness__main"> <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 /> <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"> <MapViewer @marker-click="handleMarkerClick" />
<div id="sa-controls" class="situational-awareness__controls"></div>
</div> <!-- 右侧面板 -->
<RightPanel />
</div> </div>
<!-- 弹窗组件 --> <!-- 弹窗组件 -->
@ -47,7 +32,7 @@
</template> </template>
<script setup> <script setup>
import { ref, provide, onMounted } from 'vue' import { ref, provide } from 'vue'
import PageHeader from './components/PageHeader.vue' import PageHeader from './components/PageHeader.vue'
import LeftPanel from './components/LeftPanel/index.vue' import LeftPanel from './components/LeftPanel/index.vue'
import MapViewer from './components/MapViewer/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 PersonnelDetail from './components/Popups/PersonnelDetail.vue'
import EmergencyCenterDetail from './components/Popups/EmergencyCenterDetail.vue' import EmergencyCenterDetail from './components/Popups/EmergencyCenterDetail.vue'
import { useDisasterData } from './composables/useDisasterData' import { useDisasterData } from './composables/useDisasterData'
import { useMapStore } from '@/map'
// 使 // 使
const disasterData = useDisasterData() const disasterData = useDisasterData()
@ -63,23 +47,6 @@ const disasterData = useDisasterData()
// 使 // 使
provide('disasterData', disasterData) 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 showPersonnelDetail = ref(false)
const showCenterDetail = ref(false) const showCenterDetail = ref(false)
@ -108,30 +75,21 @@ const handleBack = () => {
// router.push('/cockpit') // router.push('/cockpit')
} }
//
const handleMarkerClick = (type) => {
if (type === 'personnel') {
showPersonnelDetail.value = true
} else if (type === 'center') {
showCenterDetail.value = true
}
}
// //
const handlePersonnelLink = (personnel) => { const handlePersonnelLink = (personnel) => {
console.log('联动人员:', personnel) console.log('联动人员:', personnel)
// 使 mapStore camera service //
// const { camera } = mapStore.services()
// camera.flyTo({ destination: [lon, lat, height] })
showPersonnelDetail.value = false 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> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -139,133 +97,41 @@ const handlePersonnelLink = (personnel) => {
@use './assets/styles/common.scss' as *; @use './assets/styles/common.scss' as *;
.situational-awareness { .situational-awareness {
// width: 100vw;
container-name: situational-awareness; height: 100vh;
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);
background-color: var(--bg-dark); background-color: var(--bg-dark);
overflow: auto; // 宿 < overflow: hidden;
display: flex;
flex-direction: column;
// PageHeader &__main {
> :first-child { 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; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
z-index: 10; bottom: 0;
} background: url(../3DSituationalAwarenessCopy/assets/img/SketchPng6e145958ea0dbf76e6562cc7965debbb95226caff3271c366ac9b254cbe6e796.png)
center/cover no-repeat;
&__main { opacity: 0.3;
position: absolute; pointer-events: none;
inset: 0; //
background: url(./assets/images/main-bg.png) center/cover no-repeat;
overflow: hidden;
}
// -
&__map-layer {
position: absolute;
inset: 0;
z-index: 0; 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; position: relative;
// z-index: 1;
min-height: 56px; // MapControls }
} }
}
// <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> </style>