feat: 优化卫星页面加载和状态管理,引入加载动画和响应式数据处理
This commit is contained in:
@ -11,45 +11,44 @@ const { tleList, viewer } = defineProps<{
|
|||||||
viewer: null | Viewer;
|
viewer: null | Viewer;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// 存储卫星实体和选中状态
|
// 提取卫星名称的函数
|
||||||
interface SatelliteItem {
|
const getSatelliteName = (tle: string) => {
|
||||||
entity: Entity | null; // 存储添加到viewer的实体引用
|
return (tle.split('\n') as [string, string, string])[0].trim();
|
||||||
name: string;
|
};
|
||||||
selected: boolean;
|
|
||||||
tle: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化卫星数据
|
// 将satellites改为计算属性,响应tleList的变化
|
||||||
const satellites = ref<SatelliteItem[]>(
|
const satellites = computed(() => Array.from(new Set(tleList)));
|
||||||
tleList.map((tle) => {
|
|
||||||
const name = (tle.split('\n') as [string, string, string])[0].trim();
|
// 创建Map存储卫星实体
|
||||||
return {
|
const satelliteEntities = ref<Map<string, Entity>>(new Map());
|
||||||
entity: null,
|
|
||||||
name,
|
// 创建Set存储已选中的卫星TLE
|
||||||
selected: false,
|
const selectedSatellites = ref<Set<string>>(new Set());
|
||||||
tle,
|
|
||||||
};
|
const searchText = ref('');
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
checkAll: false,
|
checkAll: false,
|
||||||
indeterminate: false,
|
indeterminate: false,
|
||||||
searchText: '',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
const selectedCount = computed(() => satellites.value.filter((s) => s.selected).length);
|
const selectedCount = computed(() => selectedSatellites.value.size);
|
||||||
const totalCount = computed(() => satellites.value.length);
|
const totalCount = computed(() => satellites.value.length);
|
||||||
|
|
||||||
// 过滤后的卫星列表 - 添加记忆化以提高性能
|
// 过滤后的卫星列表 - 添加记忆化以提高性能
|
||||||
const filteredSatellites = computed(() => {
|
const filteredSatellites = computed(() => {
|
||||||
if (!state.searchText) return satellites.value;
|
if (!searchText.value) return satellites.value;
|
||||||
|
|
||||||
const searchLower = state.searchText.toLowerCase();
|
const searchLower = searchText.value.toLowerCase();
|
||||||
return satellites.value.filter((s) => s.name.toLowerCase().includes(searchLower));
|
return satellites.value.filter((tle) => getSatelliteName(tle).toLowerCase().includes(searchLower));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 判断卫星是否被选中
|
||||||
|
const isSatelliteSelected = (tle: string) => {
|
||||||
|
return selectedSatellites.value.has(tle);
|
||||||
|
};
|
||||||
|
|
||||||
// 监听选中状态变化,更新全选和半选状态
|
// 监听选中状态变化,更新全选和半选状态
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
const count = selectedCount.value;
|
const count = selectedCount.value;
|
||||||
@ -57,43 +56,61 @@ watchEffect(() => {
|
|||||||
state.indeterminate = count > 0 && count < filteredCount;
|
state.indeterminate = count > 0 && count < filteredCount;
|
||||||
state.checkAll = count === filteredCount && filteredCount > 0;
|
state.checkAll = count === filteredCount && filteredCount > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 更新卫星实体(添加或移除)
|
// 更新卫星实体(添加或移除)
|
||||||
const updateSatelliteEntity = (satellite: SatelliteItem) => {
|
const updateSatelliteEntity = (tle: string, selected: boolean) => {
|
||||||
if (!viewer) return;
|
if (!viewer) return;
|
||||||
|
|
||||||
if (satellite.selected) {
|
const satelliteName = getSatelliteName(tle);
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
// 添加卫星到viewer
|
// 添加卫星到viewer
|
||||||
try {
|
try {
|
||||||
const satelliteObj = new SatelliteEntity(satellite.tle);
|
const satelliteObj = new SatelliteEntity(tle);
|
||||||
const cesiumSateEntity = satelliteObj.createSatelliteEntity();
|
const cesiumSateEntity = satelliteObj.createSatelliteEntity();
|
||||||
const result = viewer.entities.add(cesiumSateEntity);
|
const result = viewer.entities.add(cesiumSateEntity);
|
||||||
satellite.entity = result; // 保存实体引用以便后续移除
|
satelliteEntities.value.set(tle, result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`添加卫星 ${satellite.name} 失败:`, error);
|
console.error(`添加卫星 ${satelliteName} 失败:`, error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 从viewer中移除卫星
|
// 从viewer中移除卫星
|
||||||
if (satellite.entity) {
|
const entity = satelliteEntities.value.get(tle);
|
||||||
viewer.entities.remove(satellite.entity);
|
if (entity) {
|
||||||
satellite.entity = null;
|
viewer.entities.remove(entity);
|
||||||
|
satelliteEntities.value.delete(tle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 切换卫星选中状态
|
// 切换卫星选中状态
|
||||||
const toggleSatellite = (satellite: SatelliteItem) => {
|
const toggleSatellite = (tle: string) => {
|
||||||
satellite.selected = !satellite.selected;
|
const isSelected = isSatelliteSelected(tle);
|
||||||
updateSatelliteEntity(satellite);
|
|
||||||
|
if (isSelected) {
|
||||||
|
selectedSatellites.value.delete(tle);
|
||||||
|
} else {
|
||||||
|
selectedSatellites.value.add(tle);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSatelliteEntity(tle, !isSelected);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 全选/取消全选
|
// 全选/取消全选
|
||||||
const onCheckAllChange = (e: { target: { checked: boolean } }) => {
|
const onCheckAllChange = (e: { target: { checked: boolean } }) => {
|
||||||
const checked = e.target.checked;
|
const checked = e.target.checked;
|
||||||
|
|
||||||
filteredSatellites.value.forEach((satellite) => {
|
filteredSatellites.value.forEach((tle) => {
|
||||||
if (satellite.selected !== checked) {
|
const isCurrentlySelected = isSatelliteSelected(tle);
|
||||||
satellite.selected = checked;
|
|
||||||
updateSatelliteEntity(satellite);
|
if (isCurrentlySelected !== checked) {
|
||||||
|
if (checked) {
|
||||||
|
selectedSatellites.value.add(tle);
|
||||||
|
} else {
|
||||||
|
selectedSatellites.value.delete(tle);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSatelliteEntity(tle, checked);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -102,26 +119,27 @@ const onCheckAllChange = (e: { target: { checked: boolean } }) => {
|
|||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (viewer) {
|
if (viewer) {
|
||||||
// 移除所有实体
|
// 移除所有实体
|
||||||
satellites.value.forEach((satellite) => {
|
satelliteEntities.value.forEach((entity) => {
|
||||||
if (satellite.entity) {
|
viewer?.entities.remove(entity);
|
||||||
viewer?.entities.remove(satellite.entity);
|
|
||||||
satellite.entity = null;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
satelliteEntities.value.clear();
|
||||||
|
selectedSatellites.value.clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="absolute left-0 top-0 p-4">
|
<div class="SatelliteSelectorPanel">
|
||||||
<APopover :overlay-style="{ width: '320px' }" placement="bottomLeft" trigger="click">
|
<APopover :overlay-style="{ width: '320px' }" placement="bottomLeft" trigger="click">
|
||||||
<template #title>
|
<template #title>
|
||||||
选择卫星
|
选择卫星
|
||||||
<span class="text-xs font-normal text-gray-500">{{ `(已选择: ${selectedCount}/${totalCount})` }}</span>
|
<span class="text-xs font-normal text-gray-700 dark:text-gray-400">{{
|
||||||
|
`(已选择: ${selectedCount}/${totalCount})`
|
||||||
|
}}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<!-- 搜索框 -->
|
<!-- 搜索框 -->
|
||||||
<AInput v-model:value="state.searchText" allow-clear placeholder="搜索卫星">
|
<AInput v-model:value="searchText" allow-clear placeholder="搜索卫星">
|
||||||
<template #prefix><SearchOutlined /></template>
|
<template #prefix><SearchOutlined /></template>
|
||||||
</AInput>
|
</AInput>
|
||||||
|
|
||||||
@ -143,13 +161,13 @@ onBeforeUnmount(() => {
|
|||||||
<!-- 卫星列表 -->
|
<!-- 卫星列表 -->
|
||||||
<div class="satellite-list max-h-60 overflow-y-auto pr-1">
|
<div class="satellite-list max-h-60 overflow-y-auto pr-1">
|
||||||
<ACheckbox
|
<ACheckbox
|
||||||
v-for="satellite in filteredSatellites"
|
v-for="tle in filteredSatellites"
|
||||||
:key="satellite.tle"
|
:key="tle"
|
||||||
:checked="satellite.selected"
|
:checked="isSatelliteSelected(tle)"
|
||||||
class="my-2 flex w-full"
|
class="my-2 flex w-full"
|
||||||
@change="() => toggleSatellite(satellite)"
|
@change="() => toggleSatellite(tle)"
|
||||||
>
|
>
|
||||||
{{ satellite.name }}
|
{{ getSatelliteName(tle) }}
|
||||||
</ACheckbox>
|
</ACheckbox>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -6,19 +6,29 @@ import { TLE_LIST } from './cesium-helper/_TLE_DATA';
|
|||||||
import SatelliteSelector from './components/SatelliteSelector.vue';
|
import SatelliteSelector from './components/SatelliteSelector.vue';
|
||||||
|
|
||||||
// Cesium相关
|
// Cesium相关
|
||||||
let viewer: null | Viewer = $ref(null);
|
let viewer = $ref<null | Viewer>(null);
|
||||||
|
|
||||||
|
const tleList = ref([] as string[]);
|
||||||
|
const spinning = ref(true);
|
||||||
|
|
||||||
|
// 模拟接口获取TLE数据
|
||||||
|
const fetchTLEData = async () => {
|
||||||
|
spinning.value = true; // 开始加载,设置状态为true
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 2000)); // 模拟2秒延迟
|
||||||
|
tleList.value = TLE_LIST; // 设置TLE数据
|
||||||
|
spinning.value = false; // 加载完成,设置状态为false
|
||||||
|
};
|
||||||
|
|
||||||
// 初始化Cesium
|
// 初始化Cesium
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
|
fetchTLEData();
|
||||||
|
|
||||||
const container = document.querySelector('#cesiumContainer');
|
const container = document.querySelector('#cesiumContainer');
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
viewer = cesium_init(container);
|
viewer = cesium_init(container);
|
||||||
|
|
||||||
// 开发调试用,生产环境可移除
|
Object.assign(globalThis, { viewer });
|
||||||
if (import.meta.env.DEV) {
|
|
||||||
Object.assign(globalThis, { viewer });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 清理资源
|
// 清理资源
|
||||||
@ -32,13 +42,12 @@ onBeforeUnmount(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="cesiumContainer" class="z-0">
|
<div id="cesiumContainer" class="absolute top-0 left-0 right-0 bottom-0">
|
||||||
<SatelliteSelector class="z-1" :viewer="viewer" :tle-list="TLE_LIST" />
|
<ASpin
|
||||||
|
size="large"
|
||||||
|
v-if="spinning"
|
||||||
|
class="z-101 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
|
||||||
|
/>
|
||||||
|
<SatelliteSelector class="z-100 absolute left-0 top-0 p-4" :viewer="viewer" :tle-list />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
#cesiumContainer {
|
|
||||||
@apply absolute top-0 left-0 right-0 bottom-0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
Reference in New Issue
Block a user