feat: 优化卫星选择器并完善 Cesium 页面交互
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -17,6 +17,9 @@
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[less]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.preferences.autoImportFileExcludePatterns": ["vue-router/auto$"],
|
||||
"i18n-ally.localesPaths": ["src/locales"],
|
||||
|
@ -1,45 +1,209 @@
|
||||
<script setup lang="ts">
|
||||
import type { Viewer } from 'cesium';
|
||||
import type { Entity, Viewer } from 'cesium';
|
||||
|
||||
import { cesium_init } from './cesium-helper/00.cesium-init';
|
||||
import { TLE_LIST } from './cesium-helper/_TLE_DATA';
|
||||
import { demo_01_OrbitGeneration } from './cesium-helper/demo_01_OrbitGeneration';
|
||||
import { demo_02_Track } from './cesium-helper/demo_02_Track';
|
||||
import SatelliteEntity from './cesium-helper/SatelliteEntity';
|
||||
|
||||
let viewer: Viewer;
|
||||
onMounted(async () => {
|
||||
viewer = cesium_init(document.querySelector('#cesiumContainer')!);
|
||||
Object.assign(globalThis, { viewer });
|
||||
// 存储卫星实体和选中状态
|
||||
interface SatelliteItem {
|
||||
entity: Entity | null; // 存储添加到viewer的实体引用
|
||||
id: string;
|
||||
name: string;
|
||||
selected: boolean;
|
||||
tle: string;
|
||||
}
|
||||
|
||||
(({ run }) => (run ? demo_01_OrbitGeneration(viewer) : null))({ run: false });
|
||||
// 初始化卫星数据
|
||||
const satellites = ref<SatelliteItem[]>(
|
||||
TLE_LIST.map((tle) => {
|
||||
const name = tle.split('\n')[0].trim();
|
||||
return {
|
||||
entity: null,
|
||||
id: name,
|
||||
name,
|
||||
selected: false,
|
||||
tle,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
(({ run }) => (run ? demo_02_Track(viewer) : null))({ run: false });
|
||||
|
||||
// demo_01_OrbitGeneration(viewer);
|
||||
// demo_02_Track(viewer);
|
||||
|
||||
TLE_LIST.forEach((tle) => {
|
||||
const satellite = new SatelliteEntity(tle);
|
||||
const cesiumSateEntity = satellite.createSatelliteEntity();
|
||||
/* let result = */ viewer.entities.add(cesiumSateEntity);
|
||||
});
|
||||
const state = reactive({
|
||||
checkAll: false,
|
||||
indeterminate: false,
|
||||
searchText: '',
|
||||
});
|
||||
|
||||
// 计算属性
|
||||
const selectedCount = computed(() => satellites.value.filter((s) => s.selected).length);
|
||||
const totalCount = computed(() => satellites.value.length);
|
||||
|
||||
// 过滤后的卫星列表 - 添加记忆化以提高性能
|
||||
const filteredSatellites = computed(() => {
|
||||
if (!state.searchText) return satellites.value;
|
||||
|
||||
const searchLower = state.searchText.toLowerCase();
|
||||
return satellites.value.filter((s) => s.name.toLowerCase().includes(searchLower));
|
||||
});
|
||||
|
||||
// 监听选中状态变化,更新全选和半选状态
|
||||
watchEffect(() => {
|
||||
const count = selectedCount.value;
|
||||
const filteredCount = filteredSatellites.value.length;
|
||||
state.indeterminate = count > 0 && count < filteredCount;
|
||||
state.checkAll = count === filteredCount && filteredCount > 0;
|
||||
});
|
||||
|
||||
// Cesium相关
|
||||
let viewer: null | Viewer = null;
|
||||
|
||||
// 初始化Cesium
|
||||
onMounted(() => {
|
||||
const container = document.querySelector('#cesiumContainer');
|
||||
if (!container) return;
|
||||
|
||||
viewer = cesium_init(container);
|
||||
|
||||
// 开发调试用,生产环境可移除
|
||||
if (import.meta.env.DEV) {
|
||||
Object.assign(globalThis, { viewer });
|
||||
}
|
||||
});
|
||||
|
||||
// 清理资源
|
||||
onBeforeUnmount(() => {
|
||||
if (viewer) {
|
||||
// 移除所有实体
|
||||
satellites.value.forEach((satellite) => {
|
||||
if (satellite.entity) {
|
||||
viewer?.entities.remove(satellite.entity);
|
||||
}
|
||||
});
|
||||
|
||||
// 销毁viewer
|
||||
viewer.destroy();
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// 切换卫星选中状态
|
||||
const toggleSatellite = (satellite: SatelliteItem) => {
|
||||
satellite.selected = !satellite.selected;
|
||||
updateSatelliteEntity(satellite);
|
||||
};
|
||||
|
||||
// 更新卫星实体(添加或移除)
|
||||
const updateSatelliteEntity = (satellite: SatelliteItem) => {
|
||||
if (!viewer) return;
|
||||
|
||||
if (satellite.selected) {
|
||||
// 添加卫星到viewer
|
||||
try {
|
||||
const satelliteObj = new SatelliteEntity(satellite.tle);
|
||||
const cesiumSateEntity = satelliteObj.createSatelliteEntity();
|
||||
const result = viewer.entities.add(cesiumSateEntity);
|
||||
satellite.entity = result; // 保存实体引用以便后续移除
|
||||
} catch (error) {
|
||||
console.error(`添加卫星 ${satellite.name} 失败:`, error);
|
||||
}
|
||||
} else {
|
||||
// 从viewer中移除卫星
|
||||
if (satellite.entity) {
|
||||
viewer.entities.remove(satellite.entity);
|
||||
satellite.entity = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 全选/取消全选
|
||||
const onCheckAllChange = (e: { target: { checked: boolean } }) => {
|
||||
const checked = e.target.checked;
|
||||
|
||||
filteredSatellites.value.forEach((satellite) => {
|
||||
if (satellite.selected !== checked) {
|
||||
satellite.selected = checked;
|
||||
updateSatelliteEntity(satellite);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="cesiumContainer"></div>
|
||||
<div id="cesiumContainer" class="z-0">
|
||||
<div class="absolute top-0 left-0 p-4 z-10">
|
||||
<APopover placement="bottomLeft" title="选择卫星" trigger="click" :overlayStyle="{ width: '320px' }">
|
||||
<template #content>
|
||||
<div class="satellite-selector">
|
||||
<!-- 搜索框 -->
|
||||
<AInput class="mb-3" v-model:value="state.searchText" placeholder="搜索卫星" allowClear>
|
||||
<template #prefix><SearchOutlined /></template>
|
||||
</AInput>
|
||||
|
||||
<!-- 选择状态信息 -->
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<span class="text-xs text-gray-500">已选择: {{ selectedCount }}/{{ totalCount }}</span>
|
||||
</div>
|
||||
|
||||
<ADivider style="margin: 8px 0" />
|
||||
|
||||
<!-- 无结果提示 -->
|
||||
<div v-if="filteredSatellites.length === 0" class="text-center text-gray-500 py-4">没有找到匹配的卫星</div>
|
||||
|
||||
<!-- 全选选项 -->
|
||||
<ACheckbox
|
||||
v-if="filteredSatellites.length > 0"
|
||||
:checked="state.checkAll"
|
||||
:indeterminate="state.indeterminate"
|
||||
@change="onCheckAllChange"
|
||||
>
|
||||
全选
|
||||
</ACheckbox>
|
||||
|
||||
<!-- 卫星列表 -->
|
||||
<div class="satellite-list max-h-60 overflow-y-auto pr-1">
|
||||
<ACheckbox
|
||||
v-for="satellite in filteredSatellites"
|
||||
:key="satellite.id"
|
||||
:checked="satellite.selected"
|
||||
@change="() => toggleSatellite(satellite)"
|
||||
class="flex my-2 w-full"
|
||||
>
|
||||
{{ satellite.name }}
|
||||
</ACheckbox>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 触发按钮 -->
|
||||
<AButton>
|
||||
<template #icon><FilterOutlined /></template>
|
||||
<!-- <span>卫星筛选</span> -->
|
||||
<span v-if="selectedCount > 0" class="ml-1">({{ selectedCount }})</span>
|
||||
</AButton>
|
||||
</APopover>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
<style scoped>
|
||||
#cesiumContainer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
@apply absolute top-0 left-0 right-0 bottom-0;
|
||||
}
|
||||
.cesium-viewer-bottom {
|
||||
display: none;
|
||||
|
||||
.satellite-list {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.satellite-list::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.satellite-list::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.satellite-selector {
|
||||
max-height: 80vh;
|
||||
}
|
||||
</style>
|
||||
|
@ -36,9 +36,3 @@ const onConfirm: PopconfirmProps['onConfirm'] = async (e) => {
|
||||
<slot></slot>
|
||||
</APopconfirm>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
[class^='ant-'] .anticon svg {
|
||||
vertical-align: unset; /* baseline */
|
||||
}
|
||||
</style>
|
||||
|
@ -14,6 +14,9 @@ async function handleConfirmAsync(e: MouseEvent) {
|
||||
|
||||
<template>
|
||||
<ACard>
|
||||
<AButton>
|
||||
<template #icon> <FilterOutlined /> </template>
|
||||
</AButton>
|
||||
<ACard>
|
||||
<HAPopconfirm title="你确定吗? 🤔 " description="别担心,我们只是假装很严肃 🎭" @confirm="handleConfirmAsync">
|
||||
<AButton size="small" danger type="text">HAPopconfirm</AButton>
|
||||
|
@ -7,5 +7,6 @@ import './main.less';
|
||||
import 'primeicons/primeicons.css';
|
||||
|
||||
import './reset-primevue.css';
|
||||
import './reset-antdv.less';
|
||||
|
||||
import 'virtual:uno.css';
|
||||
|
3
src/styles/reset-antdv.less
Normal file
3
src/styles/reset-antdv.less
Normal file
@ -0,0 +1,3 @@
|
||||
.anticon svg {
|
||||
vertical-align: unset; /* baseline */
|
||||
}
|
Reference in New Issue
Block a user