Compare commits

..

No commits in common. "27ff5b5764dcf6359a36bc00955575f4e8d8aab3" and "695c4b75b092e726de6606814508797d93152b5e" have entirely different histories.

16 changed files with 183 additions and 269 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -107,47 +107,6 @@
}
}
// 自定义背景图按钮基类用于使用背景图片的 button 元素
.btn-custom-bg {
// 重置 button 默认样式
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
border: none;
outline: none;
background: transparent;
background-color: transparent;
padding: 0;
margin: 0;
// 基础交互样式
cursor: pointer;
transition: opacity 0.3s, transform 0.2s;
user-select: none;
// 焦点样式键盘导航时显示
&:focus-visible {
outline: 2px solid var(--primary-color);
outline-offset: 2px;
}
// 交互效果
&:hover {
opacity: 0.8;
}
&:active {
transform: scale(0.98);
}
// 禁用状态
&:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
}
// Flexbox 工具类
.flex {
display: flex;

View File

@ -160,11 +160,9 @@ const handleStartDispatch = () => {
justify-content: space-between;
gap: vw(12);
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);
background: url('../../assets/images/响应等级bg.png') no-repeat center center;
background-size: 100% 100%;
background: rgba(20, 53, 118, 0.6);
border: 1px solid rgba(28, 161, 255, 0.3);
border-radius: vw(4);
min-height: vh(40);
}
@ -188,11 +186,9 @@ const handleStartDispatch = () => {
justify-content: center;
gap: vw(8);
padding: vh(10) vw(16);
// background: linear-gradient(135deg, rgba(28, 161, 255, 0.3), rgba(28, 161, 255, 0.5));
// border: 1px solid rgba(28, 161, 255, 0.5);
// border-radius: vw(4);
background: url('../../assets/images/文本按钮bg.png') no-repeat center center;
background-size: 100% 100%;
background: linear-gradient(135deg, rgba(28, 161, 255, 0.3), rgba(28, 161, 255, 0.5));
border: 1px solid rgba(28, 161, 255, 0.5);
border-radius: vw(4);
cursor: pointer;
transition: all 0.3s ease;
min-height: vh(40);
@ -262,14 +258,14 @@ const handleStartDispatch = () => {
}
.force-dispatch__stat-value {
font-size: fs(18);
font-size: fs(16);
font-family: SourceHanSansCN-Bold, sans-serif;
font-weight: 700;
color: var(--text-white);
color: var(--primary-color);
}
.force-dispatch__stat--action .force-dispatch__stat-value {
color: var(--primary-color);
color: var(--warning-color);
}
.force-dispatch__stat-unit {
@ -350,14 +346,14 @@ const handleStartDispatch = () => {
}
.force-dispatch__circle-line1 {
font-size: fs(16);
font-size: fs(14);
font-family: SourceHanSansCN-Medium, sans-serif;
font-weight: 500;
color: var(--text-white);
}
.force-dispatch__circle-line2 {
font-size: fs(16);
font-size: fs(13);
font-family: SourceHanSansCN-Regular, sans-serif;
font-weight: 400;
color: var(--text-white);

View File

@ -4,14 +4,9 @@
<div class="custom-dropdown" v-click-outside="closeDropdown">
<!-- 触发器 -->
<div class="dropdown-trigger" @click="toggleDropdown">
<span class="trigger-text"
>距离灾害点{{ forcePreset.searchRadius }}km范围内</span
>
<span class="trigger-text">距离灾害点{{ forcePreset.searchRadius }}km范围内</span>
<div class="trigger-icon" :class="{ 'is-open': isDropdownOpen }">
<img
src="../../assets/images/SketchPng8063f445fba047c290a9620343b62ea51d767b8cdcd86769502b5b160998aacc.png"
alt="dropdown"
/>
<img src="../../assets/images/SketchPng8063f445fba047c290a9620343b62ea51d767b8cdcd86769502b5b160998aacc.png" alt="dropdown" />
</div>
</div>
@ -26,29 +21,10 @@
@click="selectOption(option.value)"
>
<span class="item-text">{{ option.label }}</span>
<div
v-if="forcePreset.searchRadius === option.value"
class="item-icon"
>
<svg
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="2"
/>
<path
d="M8 12L11 15L16 9"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<div v-if="forcePreset.searchRadius === option.value" class="item-icon">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
<path d="M8 12L11 15L16 9" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
@ -61,22 +37,6 @@
<div class="summary-stats">
<div class="stat-group">
<div class="stat-card stat-card--base">
<span class="stat-label flexBox"
>应急基地
<img
src="../../assets/images/SketchPng157040b93d0288f48b67022319d8789e5ccded19d94fa14634044ed62a722b1c.png"
alt="base"
class="stat-icon"
/>
</span>
<div class="stat-value-wrapper">
<span class="stat-value">{{ forcePreset.bases }}</span>
<span class="stat-unit"></span>
</div>
</div>
<div class="stat-card stat-card--equipment">
<span class="stat-label">应急装备</span>
<div class="stat-value-wrapper">
@ -85,6 +45,15 @@
</div>
</div>
<div class="stat-card stat-card--base">
<span class="stat-label">应急基地</span>
<div class="stat-value-wrapper">
<span class="stat-value">{{ forcePreset.bases }}</span>
<span class="stat-unit"></span>
</div>
<!-- <img src="../../assets/images/SketchPng157040b93d0288f48b67022319d8789e5ccded19d94fa14634044ed62a722b1c.png" alt="base" class="stat-icon" /> -->
</div>
<div class="stat-card stat-card--personnel">
<span class="stat-label">应急人员</span>
<div class="stat-value-wrapper">
@ -100,16 +69,10 @@
:key="station.id"
class="station-item"
>
<img
src="../../assets/images/路线icon.png"
alt="station"
class="station-icon"
/>
<img src="../../assets/images/路线icon.png" alt="station" class="station-icon" />
<div class="station-info">
<span class="station-name">{{ station.name }}</span>
<span class="station-distance"
>距离灾害点{{ station.distance }}公里</span
>
<span class="station-distance">距离灾害点{{ station.distance }}公里</span>
</div>
</div>
</div>
@ -119,34 +82,34 @@
</template>
<script setup>
import { ref, inject } from "vue";
import { ref, inject } from 'vue'
const { forcePreset } = inject("disasterData");
const onDistanceChange = inject("onDistanceChange");
const { forcePreset } = inject('disasterData')
const onDistanceChange = inject('onDistanceChange')
//
const isDropdownOpen = ref(false);
const isDropdownOpen = ref(false)
//
const distanceOptions = [
// { value: 10, label: "10km" },
{ value: 30, label: "距离灾害点30km范围内" },
{ value: 50, label: "距离灾害点50km范围内" },
];
{ value: 10, label: '距离灾害点10km范围内' },
{ value: 30, label: '距离灾害点30km范围内' },
{ value: 50, label: '距离灾害点50km范围内' }
]
/**
* 切换下拉框显示/隐藏
*/
const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value;
};
isDropdownOpen.value = !isDropdownOpen.value
}
/**
* 关闭下拉框
*/
const closeDropdown = () => {
isDropdownOpen.value = false;
};
isDropdownOpen.value = false
}
/**
* 选择选项
@ -154,30 +117,30 @@ const closeDropdown = () => {
*/
const selectOption = (value) => {
if (onDistanceChange) {
onDistanceChange(value);
onDistanceChange(value)
}
closeDropdown();
};
closeDropdown()
}
//
const vClickOutside = {
mounted(el, binding) {
el.clickOutsideEvent = (event) => {
if (!(el === event.target || el.contains(event.target))) {
binding.value();
binding.value()
}
};
document.addEventListener("click", el.clickOutsideEvent);
}
document.addEventListener('click', el.clickOutsideEvent)
},
unmounted(el) {
document.removeEventListener("click", el.clickOutsideEvent);
},
};
document.removeEventListener('click', el.clickOutsideEvent)
}
}
</script>
<style scoped lang="scss">
@use "@/styles/mixins.scss" as *;
@use "../../assets/styles/common.scss" as *;
@use '@/styles/mixins.scss' as *;
@use '../../assets/styles/common.scss' as *;
.force-preset {
padding: 0;
@ -323,7 +286,7 @@ const vClickOutside = {
.stat-group {
display: flex;
justify-content: center;
gap: vw(22);
gap: vw(32);
.stat-card {
// flex: 1;
@ -335,10 +298,9 @@ const vClickOutside = {
padding: vh(12) vw(12) vh(30);
border-radius: vw(6);
position: relative;
background: url("../../assets/images/完成里程.png") no-repeat center
bottom;
background: url('../../assets/images/完成里程.png') no-repeat center bottom;
// background-size: 100% 100%;
width: vw(104);
width: vw(84);
.stat-label {
color: var(--text-white);
@ -346,11 +308,6 @@ const vClickOutside = {
font-family: SourceHanSansCN-Medium, sans-serif;
}
.flexBox{
display: flex;
align-items: center;
}
.stat-value-wrapper {
display: flex;
align-items: baseline;
@ -371,12 +328,12 @@ const vClickOutside = {
}
.stat-icon {
// position: absolute;
// right: vw(8);
// bottom: vh(8);
width: vw(12);
height: vw(10);
// opacity: 0.5;
position: absolute;
right: vw(8);
bottom: vh(8);
width: vw(24);
height: vh(24);
opacity: 0.5;
}
}
}
@ -404,8 +361,7 @@ const vClickOutside = {
gap: vw(12);
padding: vh(0) vw(12);
// background: rgba(20, 53, 118, 0.3);
background: url("../../assets/images/文本线条框.png") no-repeat center
center;
background: url('../../assets/images/文本线条框.png') no-repeat center center;
background-size: 100% 100%;
border-radius: vw(6);

View File

@ -114,8 +114,7 @@ const getColumnName = (type) => {
grid-template-columns: vw(50) 1fr 1fr 1fr vw(80);
gap: vw(8);
padding: vh(12) vw(16);
// background: rgba(20, 53, 118, 0.5);
background: rgba(6, 38, 62, 0.8);
background: rgba(20, 53, 118, 0.5);
color: var(--text-white);
font-size: fs(13);
font-family: SourceHanSansCN-Medium, sans-serif;
@ -128,8 +127,8 @@ const getColumnName = (type) => {
}
.table-body {
min-height: vh(170);
max-height: vh(170);
min-height: vh(205);
max-height: vh(205);
overflow-y: auto;
&::-webkit-scrollbar {
@ -149,8 +148,7 @@ const getColumnName = (type) => {
display: grid;
grid-template-columns: vw(50) 1fr 1fr 1fr vw(80);
gap: vw(8);
padding: vh(6) vw(16);
background: linear-gradient(to right, #163b60, #133147);
padding: vh(12) vw(16);
color: var(--text-white);
font-size: fs(12);
font-family: SourceHanSansCN-Regular, sans-serif;
@ -158,7 +156,7 @@ const getColumnName = (type) => {
border-bottom: 1px solid rgba(28, 161, 255, 0.1);
&:hover {
background: rgba(28, 161, 255, 0.15);
background: rgba(28, 161, 255, 0.1);
}
.col-index {

View File

@ -14,10 +14,16 @@
<div v-show="!isCollapsed" class="video-monitor-item__content">
<div class="video-placeholder">
<!-- 视频播放器 -->
<video :src="monitor.videoSrc" autoplay loop muted playsinline />
<video
:src="monitor.videoSrc"
autoplay
loop
muted
playsinline
/>
<div class="video-time">{{ currentTime }}</div>
</div>
<!-- 控制条叠加在视频底部 -->
<div class="video-controls">
<div
@ -72,71 +78,69 @@
</div>
</div>
</div>
</div>
</transition>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import { ref, onMounted, onUnmounted } from 'vue'
const props = defineProps({
monitor: {
type: Object,
required: true,
},
});
required: true
}
})
defineEmits(["megaphone", "audio", "zoom"]);
defineEmits(['megaphone', 'audio', 'zoom'])
// /
const isCollapsed = ref(false);
const isCollapsed = ref(false)
/**
* 切换收起/展开状态
*/
const toggleCollapse = () => {
isCollapsed.value = !isCollapsed.value;
};
isCollapsed.value = !isCollapsed.value
}
const currentTime = ref("");
const currentTime = ref('')
const updateTime = () => {
const now = new Date();
currentTime.value = now
.toLocaleString("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
})
.replace(/\//g, "/")
.replace(",", "");
};
const now = new Date()
currentTime.value = now.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).replace(/\//g, '/').replace(',', '')
}
let timer = null;
let timer = null
onMounted(() => {
updateTime();
timer = setInterval(updateTime, 1000);
});
updateTime()
timer = setInterval(updateTime, 1000)
})
onUnmounted(() => {
if (timer) {
clearInterval(timer);
clearInterval(timer)
}
});
})
</script>
<style scoped lang="scss">
@use "@/styles/mixins.scss" as *;
@use "../../assets/styles/common.scss" as *;
@use '@/styles/mixins.scss' as *;
@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: url('../../assets/images/视频面板bg.png') no-repeat center center;
background-size: 100% 100%;
padding: 2px;
// border-radius: vw(8);
@ -154,8 +158,7 @@ onUnmounted(() => {
justify-content: space-between;
align-items: center;
padding: vh(10) vw(16);
// background: rgba(20, 53, 118, 0.5);
background: rgba(6, 38, 62, 0.8);
background: rgba(20, 53, 118, 0.5);
border-bottom: 1px solid var(--border-color);
transition: all 0.3s ease;
@ -227,8 +230,7 @@ onUnmounted(() => {
z-index: 2;
width: clamp(140px, 10.1vw, 194px);
height: clamp(28px, vh(32), 36px);
background: url("../../assets/images/video-time-bg.png") no-repeat
center center;
background: url('../../assets/images/video-time-bg.png') no-repeat center center;
background-size: 100% 100%;
color: var(--text-white);
font-size: clamp(10px, fs(12), 12px);
@ -240,10 +242,9 @@ onUnmounted(() => {
padding: 0 clamp(4px, 1vw, 8px);
box-sizing: border-box;
}
}
//
.video-controls {
// position: absolute;
position: absolute;
left: 0;
right: 0;
bottom: 0;
@ -270,8 +271,7 @@ onUnmounted(() => {
font-size: clamp(10px, fs(10), 11px);
// font-family: SourceHanSansCN-Regular, sans-serif;
cursor: pointer;
transition: transform 0.15s ease-out, background 0.2s ease,
border-color 0.2s ease;
transition: transform 0.15s ease-out, background 0.2s ease, border-color 0.2s ease;
transform-origin: center;
&__icon {
@ -304,6 +304,7 @@ onUnmounted(() => {
}
}
}
}
}
//

View File

@ -1,7 +1,7 @@
<template>
<div class="scene-label" :class="labelClass">
<div class="scene-label__content">
<!-- <img :src="iconSrc" alt="scene" class="scene-label__icon" /> -->
<img :src="iconSrc" alt="scene" class="scene-label__icon" />
<span class="scene-label__text">{{ text }}</span>
</div>
</div>
@ -46,8 +46,6 @@ const iconSrc = sceneIcon
top: calc(var(--sa-header-height));
z-index: 10;
pointer-events: none;
background: url('../assets/images/灾害现场实景.png') no-repeat center center;
background-size: 100% 100%;
// 线
&--center-left {
@ -65,10 +63,10 @@ const iconSrc = sceneIcon
display: flex;
align-items: center;
gap: vw(6);
padding: vw(8) vw(10);
// background: rgba(20, 53, 118, 1);
// border: 1px solid var(--border-color);
// border-radius: vw(8);
padding: vw(5) vw(10);
background: rgba(20, 53, 118, 1);
border: 1px solid var(--border-color);
border-radius: vw(8);
}
&__icon {
@ -82,7 +80,6 @@ const iconSrc = sceneIcon
font-family: SourceHanSansCN-Medium, sans-serif;
font-weight: 500;
white-space: nowrap;
margin-left: 16px;
}
}
</style>

View File

@ -48,16 +48,26 @@ const handleClick = () => {
@use '../../assets/styles/common.scss' as *;
.action-button {
@extend .btn-custom-bg; //
display: inline-flex;
align-items: center;
justify-content: center;
gap: vw(6);
border: none;
border-radius: vw(4);
cursor: pointer;
font-family: SourceHanSansCN-Medium, sans-serif;
font-weight: 500;
transition: opacity 0.3s, transform 0.2s;
white-space: nowrap;
&:hover {
opacity: 0.8;
}
&:active {
transform: scale(0.98);
}
&__icon {
width: vw(16);
height: vh(16);
@ -69,10 +79,7 @@ const handleClick = () => {
//
&--primary {
background-image: url('../../assets/images/文本按钮bg.png');
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center;
background: var(--primary-color);
color: var(--text-white);
}

View File

@ -23,7 +23,7 @@ export function useDisasterData() {
equipment: 23,
bases: 2,
personnel: 2124,
searchRadius: 30, // km
searchRadius: 10, // km
stations: [
{
id: 1,

View File

@ -20,8 +20,7 @@ export const BEFORE_3DTILES_CONFIG = {
// 3D Tiles 服务 URL
// TODO: 替换为实际的灾前模型 URL
// url: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/model/S107/terra_b3dms/tileset.json',
url: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/model/ylzg/zxyj1119/terra_b3dms/tileset.json',
url: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/model/S107/terra_b3dms/tileset.json',
// 默认可见性
visible: false,
@ -70,7 +69,8 @@ export const BEFORE_IMAGERY_CONFIG = {
// 影像服务URL
// 格式支持标准瓦片服务的URL模板{z}/{x}/{y} 为瓦片坐标占位符
// url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
// url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
url: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/model/ylzg/zxyj1119/terra_b3dms/tileset.json',
// 图层类型
type: 'UrlTemplate',