Compare commits

...

13 Commits

Author SHA1 Message Date
0da7f2576d 修复若干bug 2026-04-23 14:44:07 +08:00
8a6a9f0c0c 修复若干bug 2026-04-23 11:40:13 +08:00
822061780e 新增天通卫星系统 2026-04-08 17:43:09 +08:00
6cca66434b 卫星科普互动展示系统 2026-03-18 16:38:50 +08:00
267462fc3f 通信模块配置软件 2025-12-31 15:17:05 +08:00
0a1fb3eeb3 添加版本号 2024-09-23 15:34:40 +08:00
ebfeb5c44e 修改配置 2024-09-23 14:50:31 +08:00
67ef2f01ec 优化渲染帧率 2024-01-25 14:14:48 +08:00
bfd0d631b7 优化渲染帧率 2024-01-24 16:19:31 +08:00
2c5d110ee8 优化渲染帧率 2024-01-24 11:43:23 +08:00
bcb9c769c4 将卫星图片改为3D模型 2024-01-05 17:40:51 +08:00
叶志超
d2cd84dab5 修改打包模式 2023-07-20 12:13:14 +08:00
叶志超
09f7eefe31 修复“添加启动验证功能”在生产模式下不能正常工作的问题 2023-07-20 12:12:32 +08:00
76 changed files with 13743 additions and 3851 deletions

View File

@@ -4,6 +4,49 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
/* 当屏幕宽度大于或等于3840px时 */
@media (min-width: 3840px) {
html {
font-size: 40px;
}
}
/* 当屏幕宽度在2560px到3839px之间时 */
@media (min-width: 2560px) and (max-width: 3839px) {
html {
font-size: 26px;
}
}
/* 当屏幕宽度在1920px到2559px之间时 */
@media (min-width: 1920px) and (max-width: 2559px) {
html {
font-size: 20px;
}
}
/* 当屏幕宽度在750px到1919px之间时 */
@media (min-width: 750px) and (max-width: 1919px) {
html {
font-size: 14px;
}
}
/* 当屏幕宽度在600px到749px之间时 */
@media (min-width: 600px) and (max-width: 749px) {
html {
font-size: 14px;
}
}
/* 当屏幕宽度小于600px时 */
@media (max-width: 599px) {
html {
font-size: 12px;
}
}
</style>
<title>数字星球互动体验系统</title>
</head>
<body class="w-h-full">

View File

@@ -2,7 +2,7 @@
"name": "global-navigation-satellite-system",
"description": "global-navigation-satellite-system",
"author": "hwasmart",
"version": "5.5.7",
"version": "6.0.0",
"main": "dist/main/app.js",
"scripts": {
"dev": "vite",
@@ -12,25 +12,31 @@
"electron:build": "rimraf dist && vite build && tsc -p tsconfig.electron.json && electron-builder"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@types/three": "^0.178.1",
"cesium": "^1.88.0",
"regedit": "^5.1.2",
"electron-store": "^8.2.0",
"element-plus": "^2.10.4",
"three": "^0.178.0",
"vue": "^3.2.26",
"vue-cesium": "^3.0.4",
"vue-cesium": "^v3.2.8",
"vuex": "^4.0.1"
},
"devDependencies": {
"@babel/types": "^7.28.1",
"@types/node": "^25.2.3",
"@vitejs/plugin-vue": "^1.1.5",
"@vue/compiler-sfc": "^3.2.26",
"autoprefixer": "^10.2.5",
"cross-env": "^7.0.3",
"electron": "^13.6.3",
"electron-builder": "^24.4.0",
"postcss": "^8.2.10",
"electron": "^40.3.0",
"electron-builder": "^25.0.5",
"postcss": "^8.4.47",
"rimraf": "^3.0.2",
"rollup-plugin-copy": "^3.4.0",
"tailwindcss": "^2.1.1",
"ts-node": "^9.1.1",
"typescript": "^4.2.3",
"ts-node": "^10.9.2",
"typescript": "^5.9.3",
"vite": "^2.0.5"
},
"build": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
src/main/KeyWord.js Normal file
View File

@@ -0,0 +1,3 @@
// 加密关键字
const keyword = 'BD';
export { keyword };

View File

@@ -1,14 +1,74 @@
import { app, globalShortcut, BrowserWindow, dialog } from 'electron'
import { join } from "path"
import { promisified as regedit } from 'regedit'
// const regedit = require('regedit').promisified
import { app, globalShortcut, BrowserWindow, ipcMain, dialog } from 'electron';
import { join } from 'path';
// const config = require('./config')
import config from './config';
import { keyword } from './KeyWord';
const child_process = require('child_process');
const crypto = require('crypto');
// 禁用硬件加速
// app.disableHardwareAcceleration();
function queryPass(passPath: string, passValue: string) {
return new Promise(function (resolve, reject) {
try {
child_process.exec(`reg query ${passPath} /v ${passValue}`, (error: Error, stdout: string, stderr: string) => {
if (error) {
reject(error);
return;
}
resolve({stdout, stderr});
});
} catch (error) {
reject(error);
}
});
};
function queryKey(keyPath: string, keyValue: string) {
return new Promise(function (resolve, reject) {
try {
child_process.exec(`reg query ${keyPath} /v ${keyValue}`, (error: Error, stdout: string, stderr: string) => {
if (error) {
reject(error);
return;
}
resolve({stdout, stderr});
})
} catch (error) {
reject(error);
}
});
};
function cryptMD5(GUID: string) {
let md5 = crypto.createHash('md5');
let ciphertext = md5.update(GUID).digest('hex');
return ciphertext.slice(0,8)+'-'+ciphertext.slice(8,12)+'-'+ciphertext.slice(12,16)+'-'+ciphertext.slice(16,20)+'-'+ciphertext.slice(20,32);
};
const passPath = 'HKEY_CURRENT_USER\\SOFTWARE\\HwaSmart';
const passValue = 'BDAuthorization';
const keyPath = 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography';
const keyValue = 'MachineGuid';
const REG_DIR_PATH:string[] = ['HKCU\\SOFTWARE\\HwaSmart']
async function checkLaunchEnv() {
const result = await regedit.list(REG_DIR_PATH)
// console.log(result[REG_DIR_PATH[0]].exists)
return result[REG_DIR_PATH[0]].exists
}
try {
const passResult: any = await queryPass(passPath, passValue);
const keyResult: any = await queryKey(keyPath, keyValue);
if(cryptMD5(keyResult.stdout.slice(83,119) + keyword) == passResult.stdout.slice(72,108)){
return true;
}else{
return false;
}
// 成功
// 查询到 有这个app启动项
} catch (error) {
// 没有查询到该app启动项目
return false;
}
};
function createWindow() {
const win = new BrowserWindow({
@@ -16,39 +76,75 @@ function createWindow() {
frame: false,
webPreferences: {
nodeIntegration: true,
preload: join(__dirname, 'preload.js')
preload: join(__dirname, 'preload.js'),
}
})
});
if (process.env.NODE_ENV === 'development') {
win.loadURL('http://localhost:3000/')
win.webContents.openDevTools()
win.loadURL('http://localhost:3000/');
win.webContents.openDevTools();
} else {
win.loadFile('dist/render/index.html')
win.loadFile('dist/render/index.html');
// win.webContents.openDevTools();
}
}
app.whenReady().then(async () => {
const checkReault = await checkLaunchEnv()
if (checkReault) {
createWindow()
ipcMain.on("APP_MOUNTED", (event) => {
const { sender } = event;
let softwareName = config.get('softwareName');
sender.send('softwareName', softwareName);
});
ipcMain.on('CLOSE', (event) => {
const res = dialog.showMessageBox({
type: 'warning',
title: '警告',
message: '确定要关闭软件吗?',
detail: '关闭软件',
cancelId: 1, // 按esc默认点击索引按钮
defaultId: 0, // 默认高亮的按钮下标
buttons: ['确认', '取消'], // 按钮按索引从右往左排序
});
// 屏蔽 F11 进入/退出全屏功能
globalShortcut.register('F11', () => {return})
res.then((data)=>{
if(data.response == 0){
win.close();
}else{
console.log('not close software');
}
});
});
};
async function main() {
// 异步代码
const checkReault: any = await checkLaunchEnv();
console.log('env right:', checkReault);
// 异步代码执行完毕后执行的代码
if (checkReault) {
app.whenReady().then(async () => {
createWindow();
// 屏蔽 F11 进入/退出全屏功能
globalShortcut.register('F11', () => {return});
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
})
} else {
dialog.showErrorBox('系统提示', '软件启动出错,请联系售后技术支持人员')
app.quit()
dialog.showErrorBox('系统提示', '软件启动出错,请联系售后技术支持人员');
process.exit(1);
}
})
};
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
main();

8
src/main/config.js Normal file
View File

@@ -0,0 +1,8 @@
const Store = require('electron-store');
const store = new Store();
const set = (key, val) => store.set(key, val);
const get = (key) => store.get(key);
module.exports = { set, get };

View File

@@ -1,20 +1,20 @@
const {ipcRenderer, contextBridge} = require('electron')
const {ipcRenderer, contextBridge} = require('electron');
contextBridge.exposeInMainWorld('ipcRenderer', {
send: (channel, data) => {
// whitelist channels
let validChannels = ['toMain']
let validChannels = ['toMain'];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data)
ipcRenderer.send(channel, data);
}
ipcRenderer.send(channel, data)
ipcRenderer.send(channel, data);
},
receive: (channel, func) => {
let validChannels = ['fromMain']
let validChannels = ['fromMain'];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args))
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
ipcRenderer.on(channel, (event, ...args) => func(...args))
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
})
});

View File

@@ -1,18 +1,332 @@
<template>
<div class="w-h-full">
<EarthView />
<div class="w-h-full overflow-hidden">
<div class="w-h-full overflow-hidden" v-show="satelliteSystemName == 'custom'">
<button class="backSatelliteSystemView" @click="handleBackSatelliteSystemView"> 返回卫星系统视图 </button>
<CustomSatelliteSystem ref="customSatelliteSystemRef" />
</div>
<div class="w-h-full overflow-hidden" v-show="satelliteSystemName !== 'custom'">
<div v-show="satelliteSystemName == 'beidou'" class="RSMC">
<div class="option">
北斗三号区域短报文通信信号覆盖区域
</div>
<div class="option">
<div>单波束:</div>
<input
:checked="singleRSMCVisible"
type="checkbox"
class="w-6 h-6 cursor-pointer"
@change="showRSMC('singleBeam')"
/>
</div>
<div class="option">
<div>双波束:</div>
<input
:checked="dualRSMCVisible"
type="checkbox"
class="w-6 h-6 cursor-pointer"
@change="showRSMC('dualBeam')"
/>
</div>
</div>
<FlagView class="fixed bottom-0 right-0" />
<SatelliteSystemSelectView class="fixed top-1" />
<SatelliteTableView class="fixed left-0 right-0 bottom-0" />
<EarthView ref="earthViewRef" @showModel="showModel" />
<ShowModel
ref="showModelRef"
v-if="allowShow"
class="fixed left-0"
style="bottom: 36vh; z-index: 99"
@showModel="showModel"
/>
<FlagView class="fixed bottom-0 right-0" />
<Quiz ref="quizRef" />
<SatelliteSystemSelectView
ref="SatelliteSystemSelectViewRef"
class="fixed top-1"
@handleSatelliteSystemSelect="handleSatelliteSystemSelect"
@handleElementsFly="handleElementsFly"
/>
<SatelliteTableView ref="satelliteTableViewRef" />
<Tour @handleNext="handleNextEvent" />
</div>
</div>
</template>
<script lang="ts" setup>
import FlagView from './components/FlagView.vue'
import EarthView from './components/EarthView.vue'
import SatelliteTableView from './components/SatelliteTableView.vue'
import SatelliteSystemSelectView from './components/SatelliteSystemSelectView.vue'
</script>
<script setup>
import { ref, watch } from "vue";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import CustomSatelliteSystem from "./components/CustomSatelliteSystem/CustomSatelliteSystem.vue";
import EarthView from "./components/SatelliteSystem/EarthView.vue";
import ShowModel from "./components/SatelliteSystem/ShowModel.vue";
import FlagView from "./components/SatelliteSystem/FlagView.vue";
import Quiz from "./components/SatelliteSystem/Quiz.vue";
import SatelliteTableView from "./components/SatelliteSystem/SatelliteTableView.vue";
import SatelliteSystemSelectView from "./components/SatelliteSystem/SatelliteSystemSelectView.vue";
import Tour from "./components/SatelliteSystem/Tour.vue";
const customSatelliteSystemRef = ref(null);
const earthViewRef = ref(null);
const showModelRef = ref(null);
const allowShow = ref(false);
const quizRef = ref(null);
const SatelliteSystemSelectViewRef = ref(null);
const satelliteTableViewRef = ref(null);
const satelliteSystemName = ref("beidou");
const singleRSMCVisible = ref(false);
const dualRSMCVisible = ref(false);
// 缓存模型对象
const modelsObj = {
BeidouGEO: {
scale: [0.3, 0.3, 0.3],
position: [0, -1.5, 0],
},
BeidouMEO: {
scale: [0.13, 0.13, 0.13],
position: [0, -1, 0],
},
BeidouIGSO: {
scale: [0.18, 0.18, 0.18],
position: [0, -1, 0],
},
"GPSII-F": {
scale: [0.35, 0.35, 0.35],
position: [0, 0, 0],
},
"GPSII-R": {
scale: [0.5, 0.5, 0.5],
position: [0, 0, 0],
},
"GPSIII-A": {
scale: [0.6, 0.6, 0.6],
position: [0, 0, 0],
},
"GPSIIR-M": {
scale: [0.5, 0.5, 0.5],
position: [0, 0, 0],
},
"GLONASSGLO-K": {
scale: [0.5, 0.5, 0.4],
position: [0, 0, -0.5],
},
"GLONASSGLO-M": {
scale: [0.5, 0.5, 0.5],
position: [0, 0, -0.5],
},
GalileoMEO: {
scale: [0.2, 0.2, 0.2],
position: [0, 0, 0],
},
};
let scene = new THREE.Scene();
class Model {
constructor(modelUrl) {
this.modelUrl = modelUrl;
this.model;
this.loadModel();
}
loadModel() {
const loader = new GLTFLoader();
const modelObj = modelsObj[this.modelUrl];
loader.load(`./models/geo/${this.modelUrl}.glb`, (gltf) => {
//设置模型大小
gltf.scene.scale.set(modelObj.scale[0], modelObj.scale[1], modelObj.scale[2]);
gltf.scene.position.set(
modelObj.position[0],
modelObj.position[1],
modelObj.position[2]
);
gltf.scene.name = this.modelUrl;
this.model = gltf.scene;
scene.add(gltf.scene);
});
}
}
const GEOModel = new Model("BeidouGEO");
const IGSOModel = new Model("BeidouIGSO");
const MEOModel = new Model("BeidouMEO");
const GPSIIFModel = new Model("GPSII-F");
const GPSIIRModel = new Model("GPSII-R");
const GPSIIIAModel = new Model("GPSIII-A");
const GPSIIRMModel = new Model("GPSIIR-M");
const GLONASSGLOKModel = new Model("GLONASSGLO-K");
const GLONASSGLOMModel = new Model("GLONASSGLO-M");
const GalileoMEOModel = new Model("GalileoMEO");
const modelCache = {
beidou: {
GEO: GEOModel,
IGSO: IGSOModel,
MEO: MEOModel
},
other: {
GEO: GEOModel,
"II-F": GPSIIFModel,
"II-R": GPSIIRModel,
"III-A": GPSIIIAModel,
"IIR-M": GPSIIRMModel,
"GLO-K": GLONASSGLOKModel,
"GLO-M": GLONASSGLOMModel,
MEO: GalileoMEOModel
}
};
const showModel = (show, model, system) => {
allowShow.value = show;
if (model !== "") {
let systemMark = "";
if (system == "-1") {
systemMark = "other"
} else {
systemMark = "beidou"
}
if (systemMark !== "") {
setTimeout(() => {
showModelRef.value.model = modelCache[systemMark][model];
showModelRef.value.loadModel();
}, 0);
}
}
};
const handleBackSatelliteSystemView = () => {
SatelliteSystemSelectViewRef.value.selected = "beidou";
satelliteSystemName.value = "beidou";
};
const showRSMC = (model) => {
if (model == 'singleBeam') {
earthViewRef.value.singleBeamShow = !earthViewRef.value.singleBeamShow;
singleRSMCVisible.value = !singleRSMCVisible.value;
} else if (model == 'dualBeam') {
earthViewRef.value.dualBeamShow = !earthViewRef.value.dualBeamShow;
dualRSMCVisible.value = !dualRSMCVisible.value;
}
};
const handleSatelliteSystemSelect = (value) => {
satelliteSystemName.value = value;
earthViewRef.value.firstClick = true;
earthViewRef.value.resetSceneMode();
};
const handleElementsFly = () => {
setTimeout(() => {
earthViewRef.value.flyfunc(true);
}, 1000);
};
const handleNextEvent = (step) => {
switch (step) {
case "#step9":
satelliteTableViewRef.value.show = true;
break;
case "#step12":
satelliteTableViewRef.value.onTrackButtonClicked(
window.document.getElementById("satelliteId").innerText
);
break;
case "#step15":
satelliteTableViewRef.value.onTrackButtonClicked(
window.document.getElementById("satelliteId").innerText
);
setTimeout(() => {
window.document.getElementById("step14").click();
}, 0);
break;
case "#step22":
quizRef.value.show = true;
break;
}
};
watch(allowShow, (value) => {
earthViewRef.value.showModelTour();
});
watch(satelliteSystemName, (value) => {
if (value == "custom") {
customSatelliteSystemRef.value.animationShow = true;
earthViewRef.value.animationShow = false;
} else {
if (value !== "beidou") {
earthViewRef.value.singleBeamShow = false;
earthViewRef.value.dualBeamShow = false;
} else {
singleRSMCVisible.value = false;
dualRSMCVisible.value = false;
}
customSatelliteSystemRef.value.animationShow = false;
earthViewRef.value.animationShow = true;
}
});
</script>
<style scoped>
.backSatelliteSystemView {
font-size: 1rem;
color: #000000;
background-color: #ffffff;
width: 10rem;
height: 2rem;
border-radius: 1rem;
line-height: 2rem;
overflow: hidden;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
top: 0.5rem;
left: 0.5rem;
z-index: 99;
}
.backSatelliteSystemView:hover {
background-color: rgb(165 243 252 / 1);
}
.RSMC {
font-size: 1rem;
color: #ffffff;
text-align: center;
background-color: #ffffff;
width: 1rem;
height: 8rem;
line-height: 2rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: hidden;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
bottom: 24rem;
right: 0;
z-index: 99;
border-top-left-radius: 1rem;
border-bottom-left-radius: 1rem;
}
.RSMC:hover {
width: 18rem;
height: 6rem;
color: #000000;
background-color: rgb(165 243 252 / 1);
}
.option {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -1,23 +1,23 @@
import store from '../store'
import store from '../store';
let entities = []
let entities = [];
const load = (value) => {
entities = value
entities = value;
// 将卫星状态信息存储到store中
const satellites = filter('Satellite')
const satellite_state_arr = satellites.map(({ id, show }) => { return { id, show } })
const satellites = filter('Satellite');
const satellite_state_arr = satellites.map(({ description, id, show }) => { return { description: description._value, id, show } });
store.dispatch('satelliteSystem/setSatellites', {
satellites: satellite_state_arr
})
}
});
};
const get = id => entities.find(entity => entity.id === id)
const get = id => entities.find(entity => entity.id === id);
const find = keyword => entities.find(entity => entity.id.startsWith(keyword))
const find = keyword => entities.find(entity => entity.id.startsWith(keyword));
const filter = keyword => entities.filter(entity => entity.id.startsWith(keyword))
const filter = keyword => entities.filter(entity => entity.id.startsWith(keyword));
export default { get, load, find }
export default { get, load, find };

BIN
src/render/assets/sky.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

View File

@@ -0,0 +1,154 @@
<template>
<table class="text-center">
<thead class="text-2xl">
<th
class="py-3 bg-blue-900 border border-gray-900"
v-for="(label, index) in ['轨道序号', '轨道名称', '显示/隐藏', '卫星列表', '操作']"
:key="label"
:style="{ width: index == 4 ? '40vw' : '20vw' }"
>
<div style="display: flex; justify-content: center; align-items: center;">
<div style="margin-right: 0.5rem;">
{{ label }}
</div>
<input
v-if="label == '显示/隐藏'"
:disabled="satelliteArr.length > 0 ? false : true"
indeterminate="false"
:checked="allShow"
type="checkbox"
class="w-6 h-6 cursor-pointer"
@change="allSatelliteShow('all')"
/>
<input
v-if="label == '操作'"
:disabled="satelliteArr.length > 0 ? false : true"
type="button"
class="buttonClass"
id="fly-btn"
@click="allClearSatellite('all')"
value="清除全部轨道"
/>
</div>
</th>
</thead>
<tbody class="text-lg odd:bg-gray-50">
<tr v-for="(entity, index) in satelliteArr" :key="index">
<td class="border border-gray-900"> {{ index + 1 }} </td>
<td class="border border-gray-900">{{ entity.orbitViewer.name }}</td>
<td class="border border-gray-900">
<div style="display: flex; justify-content: center; align-items: center;">
<input
:checked="entity.show"
type="checkbox"
class="w-6 h-6 cursor-pointer"
@change="onShowSatellite(index, 'main')"
/>
</div>
</td>
<td class="border border-gray-900">
<div style="display: flex; justify-content: center; align-items: center;">
<input
:disabled="satelliteArr[index].subSatelliteArr.length == 0"
:checked="entity.createSub"
type="checkbox"
class="w-6 h-6 cursor-pointer"
@change="showSubSatelliteTable(index)"
/>
</div>
</td>
<td class="border border-gray-900" style="display: flex; justify-content: space-around; align-items: center;">
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="modifySatellitePanel(index, 'main')"
value="修改数据"
/>
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="onShowSatelliteControlPanel(index, 'main')"
value="控制面板"
/>
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="createSubSatellite(index)"
value="添加卫星"
/>
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="clearSatellite(index, 'main')"
value="清除轨道"
/>
</td>
</tr>
</tbody>
</table>
</template>
<script setup>
const props = defineProps({
satelliteArr: Array,
allShow: Boolean
});
const emit = defineEmits([
"allSatelliteShow",
"allClearSatellite",
"modifySatellitePanel",
"onShowSatelliteControlPanel",
"onShowSatellite",
"clearSatellite",
"showSubSatelliteTable",
"createSubSatellite"
]);
const allSatelliteShow = (model) => {
emit("allSatelliteShow", model);
};
const allClearSatellite = (model) => {
emit("allClearSatellite", model);
};
const modifySatellitePanel = (index, model) => {
emit("modifySatellitePanel", index, model);
};
const onShowSatelliteControlPanel = (index, model) => {
emit("onShowSatelliteControlPanel", index, model);
};
const onShowSatellite = (index, model) => {
emit("onShowSatellite", index, model);
};
const clearSatellite = (index, model) => {
emit("clearSatellite", index, model);
};
const showSubSatelliteTable = (index) => {
emit("showSubSatelliteTable", index);
};
const createSubSatellite = (index) => {
emit("createSubSatellite", index);
};
</script>
<style scoped>
.buttonClass {
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
cursor: pointer;
height: 2rem;
line-height: 2rem;
}
</style>

View File

@@ -0,0 +1,177 @@
<template>
<div
v-if="show"
class="text-left p-10"
style="
color: #ffffff;
background-color: rgb(55 55 55 / 0.9);
border-radius: 2rem;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
top: 10vh;
left: 20vw;
width: 60vw;
height: 80vh;
font-size: 1rem;
"
>
<div
@click="close"
class="bg-gray-600 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
style="
width: 2rem;
height: 2rem;
border-top-right-radius: 1rem;
border-bottom-left-radius: 1rem;
"
>
<el-icon style="width: 2rem; height: 2rem">
<CloseBold color="#ffffff" />
</el-icon>
</div>
<div class="w-full text-center" style="font-size: 2rem; height: 4rem; line-height: 4rem;">
轨道控制面板
</div>
<div class="text-white text-center flex flex-wrap justify-center items-center" style="height: 80%; font-size: 1rem;">
<div class="option">
<label>卫星近地点颜色:</label>
<select v-model="nearPointSelected" style="cursor: pointer; color: black;">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<label>显示/隐藏卫星近地点:</label>
<input type="checkbox" v-model="nearPointShow" class="w-6 h-6 cursor-pointer" />
</div>
<div class="option">
<label>卫星远地点颜色:</label>
<select v-model="farPointSelected" style="cursor: pointer; color: black;">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<label>显示/隐藏卫星远地点:</label>
<input type="checkbox" v-model="farPointShow" class="w-6 h-6 cursor-pointer" />
</div>
<div class="option">
<label>卫星轨道颜色:</label>
<select v-model="orbitSelected" style="cursor: pointer; color: black;">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<label>显示/隐藏卫星轨道:</label>
<input type="checkbox" v-model="orbitShow" class="w-6 h-6 cursor-pointer" />
</div>
<div class="option">
<label>显示/隐藏轨道全部信息:</label>
<input type="checkbox" v-model="satelliteShow" class="w-6 h-6 cursor-pointer" />
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
const emit = defineEmits([
"closeSatelliteControlPanel",
"handleSatelliteControl"
]);
const show = ref(false);
const nearPointSelected = ref("ORANGE");
const nearPointShow = ref(true);
const farPointSelected = ref("PINK");
const farPointShow = ref(true);
const orbitSelected = ref("BLUE");
const orbitShow = ref(true);
const satelliteShow = ref(true);
const cesiumColor = {
BLUE: "蓝色",
PURPLE: "紫色",
RED: "红色",
YELLOW: "黄色",
CYAN: "青色",
ORANGE: "橙色",
PINK: "粉色",
GREEN: "绿色",
WHITE: "白色",
LIGHTBLUE: "浅蓝色",
LIME: "青柠色",
MAGENTA: "品红色",
};
const data = {
nearPointSelected,
nearPointShow,
farPointSelected,
farPointShow,
orbitSelected,
orbitShow,
satelliteShow
};
const close = () => {
show.value = false;
emit("closeSatelliteControlPanel");
};
defineExpose({ show, data });
watch(nearPointSelected, (value) => {
emit("handleSatelliteControl", "nearPointSelected", value);
});
watch(nearPointShow, (value) => {
emit("handleSatelliteControl", "nearPointShow", value);
});
watch(farPointSelected, (value) => {
emit("handleSatelliteControl", "farPointSelected", value);
});
watch(farPointShow, (value) => {
emit("handleSatelliteControl", "farPointShow", value);
});
watch(orbitSelected, (value) => {
emit("handleSatelliteControl", "orbitSelected", value);
});
watch(orbitShow, (value) => {
emit("handleSatelliteControl", "orbitShow", value);
});
watch(satelliteShow, (value) => {
emit("handleSatelliteControl", "satelliteShow", value);
});
</script>
<style scoped>
.option {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.buttonClass {
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
cursor: pointer;
width: 12rem;
height: 2rem;
line-height: 2rem;
}
</style>

View File

@@ -0,0 +1,775 @@
<template>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1rem;'>
仅显示输入框,适合熟悉卫星轨道六根数原理的用户使用。
</div>
"
raw-content
placement="left"
>
<button @click="showit" class="satelliteCreate">专家模式</button>
</el-tooltip>
<div
v-if="show"
class="text-left p-10"
style="
color: #ffffff;
background-color: rgb(55 55 55 / 0.9);
border-radius: 2rem;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
top: 10vh;
left: 20vw;
width: 60vw;
height: 80vh;
font-size: 1rem;
"
>
<div
@click="close"
class="bg-gray-600 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
style="
width: 2rem;
height: 2rem;
border-top-right-radius: 1rem;
border-bottom-left-radius: 1rem;
"
>
<el-icon style="width: 2rem; height: 2rem">
<CloseBold color="#ffffff" />
</el-icon>
</div>
<div class="w-full text-center" style="font-size: 2rem; height: 4rem; line-height: 4rem;">
添加轨道
</div>
<div class="text-white text-center flex flex-wrap justify-center items-center" style="height: 80%; font-size: 1rem;">
<div class="option">
<label :style="satelliteNameCheck ? '' : 'color: red;'">轨道名称:</label>
<input
type="text"
maxlength="12"
v-model="satelliteNameData"
placeholder="请输入轨道名称"
class="dataInput"
:style="satelliteNameCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
</div>
<div class="option">
<label :style="nearPointLengthCheck ? '' : 'color: red;'">近地点高度</label>
<label :style="nearPointLengthCheck ? 'color: #00efff;' : 'color: red;'">(介于200㎞~120000㎞之间,单位:㎞)</label>
<label :style="nearPointLengthCheck ? '' : 'color: red;'">:</label>
<input
type="number"
maxlength="6"
v-model="nearPointLengthData"
placeholder="请输入数据(单位:㎞)"
class="dataInput"
:style="nearPointLengthCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点高度就是卫星运行时,离地球最近的那个点,到地球表面的垂直距离。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星的运行轨道不是正圆形,而是有点 “扁” 的椭圆。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星绕着地球转时,会有一个离地球最近的位置,这个位置就是 “近地点”。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点高度,就是从这个最近的点,垂直往下量到地心的距离。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="farPointLengthCheck ? '' : 'color: red;'">远地点高度</label>
<label :style="farPointLengthCheck ? 'color: #00efff;' : 'color: red;'">(介于200㎞~120000㎞之间,单位:㎞)</label>
<label :style="farPointLengthCheck ? '' : 'color: red;'">:</label>
<input
type="number"
maxlength="6"
v-model="farPointLengthData"
placeholder="请输入数据(单位:㎞)"
class="dataInput"
:style="farPointLengthCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
远地点高度就是卫星运行时,离地球最远的那个点,到地球表面的垂直距离。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星的运行轨道不是正圆形,而是有点 “扁” 的椭圆。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星绕着地球转时,会有一个离地球最远的位置,这个位置就是 “远地点”。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
远地点高度,就是从这个最远的点,垂直往下量到地心的距离。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="semiMajorAxisCheck ? '' : 'color: red;'">轨道半长轴</label>
<label :style="semiMajorAxisCheck ? 'color: #00efff;' : 'color: red;'">(介于200㎞~120000㎞之间)</label>
<label :style="semiMajorAxisCheck ? '' : 'color: red;'">:</label>
<label :style="semiMajorAxisCheck ? 'color: rgb(250, 230, 164);' : 'color: red;'">{{ semiMajorAxisData === '' ? '' : Number(semiMajorAxisData).toFixed(3) }}</label>
<label :style="semiMajorAxisCheck ? 'color: #00efff;' : 'color: red;'">(单位:㎞)</label>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星轨道为椭圆形,轨道半长轴就是 “长轴的一半长度”,也就是从椭圆(轨道)中心到椭圆最长端(远地点)的距离。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
它不是到地球表面的距离,而是从椭圆中心(不是地球球心)到轨道边缘的最长半径。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
简单说:椭圆轨道像个 “拉长的圆”,半长轴就相当于这个 “拉长圆” 的 “平均半径”—— 能反映轨道的整体大小。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道半长轴决定了轨道的大小,决定卫星离地球的平均距离,决定卫星飞得高还是低。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星飞得越高,速度越慢,周期越长,半径越大,轨道越大,卫星离地球越远。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
图中b为半短轴,半短轴 b 越接近半长轴 a;当偏心率=0(正圆轨道)时,b=a,此时半短轴就是圆的半径。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="eccentricityCheck ? '' : 'color: red;'">轨道偏心率</label>
<label :style="eccentricityCheck ? 'color: #00efff;' : 'color: red;'">(不超过0.9)</label>
<label :style="eccentricityCheck ? '' : 'color: red;'">:</label>
<label :style="eccentricityCheck ? 'color: rgb(250, 230, 164);' : 'color: red;'">{{ eccentricityData === '' ? '' : Number(eccentricityData).toFixed(6) }}</label>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道偏心率决定了轨道的形状,是用来描述天体轨道形状偏离圆形程度的一个数值。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道偏心率是椭圆两焦点之间的距离(焦距)与长轴的比值,也可以换算为半焦距与半长轴的比值,描述椭圆扁平程度。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
用 0 到 1 之间的数字表示,偏心率越接近 0,轨道越圆;偏心率越接近 1,轨道越扁(但不会等于 1,等于 1 就不是椭圆了)。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点和远地点距离差不多(轨道圆),偏心率就小。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点离地球很近、远地点离地球很远(轨道扁),偏心率就大。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
用于描述轨道的形状,表征轨道椭圆的扁平程度。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
假设轨道半长轴为b,轨道焦点到轨道中心距离为a,轨道离心率为e,则:e = a ÷ b。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label>轨道周期</label>
<label style="color: #00efff;">(秒)</label>
<label>:</label>
<label style="color: rgb(250, 230, 164);">{{ orbitalPeriodData === '' ? '' : Number(orbitalPeriodData).toFixed(3) }}</label>
<label style="color: #00efff;">(单位:s)</label>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道周期就是卫星绕中心天体(比如地球)完整转一圈的时间。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
简单说,轨道周期就是从卫星某个位置(比如近地点)出发,绕轨道飞一圈后,再回到这个位置的总时间。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
万有引力常数 G = 6.67430 × 10^⁻¹¹ 米³ / (千克 × 秒²),地球质量 M = 5.965 × 10²⁴ 千克。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
人造地球卫星运转周期T(秒)与圆轨道半径或椭圆轨道半长轴R(米)之间的关系可用下列公式计算:T = 2π × (R³ ÷ GM)½ 。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label>平均角速度</label>
<label style="color: #00efff;">(度/秒)</label>
<label>:</label>
<label style="color: rgb(250, 230, 164);">{{ angularVelocityDegData === '' ? '' : Number(angularVelocityDegData).toFixed(6) }}</label>
<label style="color: #00efff;">(单位:°/s)</label>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
平均角速度就是卫星绕中心天体转的时候,单位时间内转过的角度,简单说就是 “转得快不快” 的 “角度版” 描述。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
周期越短(转一圈时间少),角速度越大(转得快)。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
通俗理解:比如卫星 1 小时转了 45°,它的平均角速度就是 45°/ 小时;转得越快,单位时间内转过的角度越大,角速度就越大。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星飞得越高,速度越慢,卫星飞得越低,速度越快,平均角速度是卫星在轨道上的平均运行速度。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="inclinationCheck ? '' : 'color: red;'">轨道倾角</label>
<label :style="inclinationCheck ? 'color: #00efff;' : 'color: red;'">(0°~180°)</label>
<label :style="inclinationCheck ? '' : 'color: red;'">:</label>
<input
type="number"
maxlength="3"
v-model="inclinationData"
placeholder="请输入数据(0°~180°)"
class="dataInput"
:style="inclinationCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道倾角就是轨道相对于地球赤道的倾斜程度,是卫星绕地球转的轨道平面,和地球赤道平面之间的 “夹角”,用 0° 到 180° 的数字表示,核心是描述轨道 “歪不歪”,“往哪个方向歪”。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
地球赤道平面:想象过地球赤道画一个无限大的 “隐形圆盘”,这就是赤道平面。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星轨道平面:卫星绕地球转的轨迹所在的 “隐形圆盘”,就是轨道平面。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道倾角,就是这两个 “隐形圆盘” 之间夹着的角度,就像两本书打开时书页之间的夹角一样。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
也可以类比滑滑梯的角度,把地面比作赤道,卫星轨道是倾斜的滑梯,倾角就是地面和滑梯的夹角。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="ascendantRightAscensionCheck ? '' : 'color: red;'">升交点赤经</label>
<label :style="ascendantRightAscensionCheck ? 'color: #00efff;' : 'color: red;'">(0°~360°)</label>
<label :style="ascendantRightAscensionCheck ? '' : 'color: red;'">:</label>
<input
type="number"
maxlength="3"
v-model="ascendantRightAscensionData"
placeholder="请输入数据(0°~360°)"
class="dataInput"
:style="ascendantRightAscensionCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道在空间中的方向,轨道平面在太空中的旋转角度,轨道在赤道上的起点,共同确定了轨道平面和赤道平面交点的位置,相当于给倾斜的轨道找一个基准起点。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
升交点赤经就是给卫星轨道在宇宙中 “定方向” 的参数,核心是描述轨道与地球赤道交点的 “东向位置”。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
升交点:卫星从地球南半球飞到北半球时,会穿过赤道平面,这个穿过的点就是升交点。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
春分点:太阳从地球的南半球向北半球运动穿过地球赤道平面的那个点,可以理解为太阳的升交点,每年春分(3月21日左右)的时候,太阳经过该点。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
赤经:与经度定义类似,但其参考点为春分点,且不随地球转自转运动。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
升交点赤经描述的是一个角度,这个角度就是“春分点到地心连线”与“升交点到地心连线”的夹角,表征了轨道平面相对春分点的方位。结合上一节的轨道倾角,就完全确定了轨道平面在三维空间中的方位。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="argumentOfPeriapsisCheck ? '' : 'color: red;'">近地点角距</label>
<label :style="argumentOfPeriapsisCheck ? 'color: #00efff;' : 'color: red;'">(0°~360°)</label>
<label :style="argumentOfPeriapsisCheck ? '' : 'color: red;'">:</label>
<input
type="number"
maxlength="3"
v-model="argumentOfPeriapsisData"
placeholder="请输入数据(0°~360°)"
class="dataInput"
:style="argumentOfPeriapsisCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点角距(近地点幅角)就是从升交点开始,沿着卫星轨道往飞行方向量,到近地点的角度,核心是给近地点 “定具体位置”。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点角距(近地点幅角),就是以地球球心为中心,在卫星轨道平面上,从升交点出发,顺着卫星飞行的方向,画一条线到近地点,这条线和 “升交点到地心连线” 之间的夹角(范围 0°-360°)。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道在平面内的朝向,轨道椭圆在轨道平面内的旋转角度,就像钟表指针指向的方向,确定近地点在轨道上的位置,近地点相对于升交点的位置,近地点角距(近地点幅角)决定了卫星离地球最近时,指向哪个方向。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label>卫星近地点颜色:</label>
<select v-model="nearPointSelectedData" style="cursor: pointer; color: black; margin-right: 1rem;">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
<label>卫星远地点颜色:</label>
<select v-model="farPointSelectedData" style="cursor: pointer; color: black;">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<label>卫星轨道颜色:</label>
<select v-model="orbitSelectedData" style="cursor: pointer; color: black; margin-right: 1rem;">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="generateOrbit"
value="添加轨道"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { CloseBold, QuestionFilled } from "@element-plus/icons-vue";
const props = defineProps({
satelliteArr: Array
});
const emit = defineEmits([
"selectSatelliteModel",
"generateOrbit"
]);
// 地球赤道半径 6378137 米
const equatorRadius = 6378.137;
const show = ref(false);
const satelliteNameData = ref();
const nearPointLengthData = ref();
const farPointLengthData = ref();
const semiMajorAxisData = ref();
const eccentricityData = ref();
const orbitalPeriodData = ref();
const angularVelocityRadData = ref();
const angularVelocityDegData = ref();
const inclinationData = ref();
const ascendantRightAscensionData = ref();
const argumentOfPeriapsisData = ref();
const satelliteNameCheck = ref(true);
const nearPointLengthCheck = ref(true);
const farPointLengthCheck = ref(true);
const semiMajorAxisCheck = ref(true);
const eccentricityCheck = ref(true);
const inclinationCheck = ref(true);
const ascendantRightAscensionCheck = ref(true);
const argumentOfPeriapsisCheck = ref(true);
const nearPointSelectedData = ref("ORANGE");
const farPointSelectedData = ref("PINK");
const orbitSelectedData = ref("BLUE");
const cesiumColor = {
BLUE: "蓝色",
PURPLE: "紫色",
RED: "红色",
YELLOW: "黄色",
CYAN: "青色",
ORANGE: "橙色",
PINK: "粉色",
GREEN: "绿色",
WHITE: "白色",
LIGHTBLUE: "浅蓝色",
LIME: "青柠色",
MAGENTA: "品红色",
};
const data = {
satelliteNameData,
nearPointLengthData,
farPointLengthData,
semiMajorAxisData,
eccentricityData,
orbitalPeriodData,
angularVelocityRadData,
angularVelocityDegData,
inclinationData,
ascendantRightAscensionData,
argumentOfPeriapsisData,
nearPointSelectedData,
farPointSelectedData,
orbitSelectedData,
};
const resetData = () => {
satelliteNameData.value = "";
nearPointLengthData.value = "";
farPointLengthData.value = "";
semiMajorAxisData.value = "";
eccentricityData.value = "";
orbitalPeriodData.value = "";
angularVelocityRadData.value = "";
angularVelocityDegData.value = "";
inclinationData.value = "";
ascendantRightAscensionData.value = "";
argumentOfPeriapsisData.value = "";
satelliteNameCheck.value = true;
nearPointLengthCheck.value = true;
farPointLengthCheck.value = true;
semiMajorAxisCheck.value = true;
eccentricityCheck.value = true;
inclinationCheck.value = true;
ascendantRightAscensionCheck.value = true;
argumentOfPeriapsisCheck.value = true;
nearPointSelectedData.value = "ORANGE";
farPointSelectedData.value = "PINK";
orbitSelectedData.value = "BLUE";
};
const showit = () => {
show.value = true;
};
const close = () => {
show.value = false;
setTimeout(() => {
resetData();
}, 0);
};
const generateOrbitData = () => {
if (nearPointLengthData.value != "" && nearPointLengthData.value != undefined && farPointLengthData.value != "" && farPointLengthData.value != undefined) {
semiMajorAxisData.value = (nearPointLengthData.value + equatorRadius + (farPointLengthData.value + equatorRadius)) / 2;
eccentricityData.value = (semiMajorAxisData.value - (nearPointLengthData.value + equatorRadius)) / semiMajorAxisData.value;
orbitalPeriodData.value = ((semiMajorAxisData.value * 1000) / 21613.546) ** 1.5;
angularVelocityRadData.value = (2 * Math.PI) / orbitalPeriodData.value;
angularVelocityDegData.value = angularVelocityRadData.value * (180 / Math.PI);
if (semiMajorAxisData.value === '' || semiMajorAxisData.value === undefined || semiMajorAxisData.value < 200 || semiMajorAxisData.value > 120000) {
semiMajorAxisCheck.value = false;
} else {
semiMajorAxisCheck.value = true;
}
if (eccentricityData.value === '' || eccentricityData.value === undefined || eccentricityData.value < 0 || eccentricityData.value > 0.9) {
eccentricityCheck.value = false;
} else {
eccentricityCheck.value = true;
}
}
};
watch(nearPointLengthData, (value) => {
generateOrbitData();
});
watch(farPointLengthData, (value) => {
generateOrbitData();
});
const booleanCheck = (checkArr) => {
let check = true;
checkArr.forEach(item => {
if (item == false) {
check = false;
}
});
return check;
};
const satelliteNameDataCheck = () => {
if (satelliteNameData.value === '' || satelliteNameData.value === undefined) {
satelliteNameCheck.value = false;
} else {
satelliteNameCheck.value = true;
}
props.satelliteArr.forEach((entity, index) => {
for (const element in entity) {
if (entity[element].name !== undefined) {
let name = entity[element].name;
if (satelliteNameData.value == name) {
satelliteNameCheck.value = false;
}
}
}
});
};
const nearPointLengthDataCheck = () => {
if (nearPointLengthData.value === "" || nearPointLengthData.value === undefined || nearPointLengthData.value < 200 || nearPointLengthData.value > 120000) {
nearPointLengthCheck.value = false;
} else {
nearPointLengthCheck.value = true;
}
if (nearPointLengthCheck.value == 1 && farPointLengthData.value !== "" && farPointLengthData.value !== undefined && nearPointLengthData.value > farPointLengthData.value) {
nearPointLengthCheck.value = false;
farPointLengthCheck.value = false;
} else {
farPointLengthCheck.value = true;
}
};
const farPointLengthDataCheck = () => {
if (farPointLengthData.value === "" || farPointLengthData.value === undefined || farPointLengthData.value < 200 || farPointLengthData.value > 120000) {
farPointLengthCheck.value = false;
} else {
farPointLengthCheck.value = true;
}
if (farPointLengthCheck.value == 1 && nearPointLengthData.value !== "" && nearPointLengthData.value !== undefined && nearPointLengthData.value > farPointLengthData.value) {
nearPointLengthCheck.value = false;
farPointLengthCheck.value = false;
} else {
nearPointLengthCheck.value = true;
}
};
const semiMajorAxisDataCheck = () => {
if (semiMajorAxisData.value === '' || semiMajorAxisData.value === undefined || semiMajorAxisData.value < 200 || semiMajorAxisData.value > 120000) {
semiMajorAxisCheck.value = false;
} else {
semiMajorAxisCheck.value = true;
}
};
const eccentricityDataCheck = () => {
if (eccentricityData.value === '' || eccentricityData.value === undefined || eccentricityData.value < 0 || eccentricityData.value > 0.9) {
eccentricityCheck.value = false;
} else {
eccentricityCheck.value = true;
}
};
const inclinationDataCheck = () => {
if (inclinationData.value === '' || inclinationData.value === undefined || inclinationData.value < 0 || inclinationData.value > 180) {
inclinationCheck.value = false;
} else {
inclinationCheck.value = true;
}
};
const ascendantRightAscensionDataCheck = () => {
if (ascendantRightAscensionData.value === '' || ascendantRightAscensionData.value === undefined || ascendantRightAscensionData.value < 0 || ascendantRightAscensionData.value > 360) {
ascendantRightAscensionCheck.value = false;
} else {
ascendantRightAscensionCheck.value = true;
}
};
const argumentOfPeriapsisDataCheck = () => {
if (argumentOfPeriapsisData.value === '' || argumentOfPeriapsisData.value === undefined || argumentOfPeriapsisData.value < 0 || argumentOfPeriapsisData.value > 360) {
argumentOfPeriapsisCheck.value = false;
} else {
argumentOfPeriapsisCheck.value = true;
}
};
watch(satelliteNameData, (value) => {
satelliteNameDataCheck();
});
watch(nearPointLengthData, (value) => {
nearPointLengthDataCheck();
});
watch(farPointLengthData, (value) => {
farPointLengthDataCheck();
});
watch(semiMajorAxisData, (value) => {
semiMajorAxisDataCheck();
});
watch(eccentricityData, (value) => {
eccentricityDataCheck();
});
watch(inclinationData, (value) => {
inclinationDataCheck();
});
watch(ascendantRightAscensionData, (value) => {
ascendantRightAscensionDataCheck();
});
watch(argumentOfPeriapsisData, (value) => {
argumentOfPeriapsisDataCheck();
});
const generateCheck = () => {
let checkArr = [];
satelliteNameDataCheck();
checkArr.push(satelliteNameCheck.value);
nearPointLengthDataCheck();
farPointLengthDataCheck();
checkArr.push(nearPointLengthCheck.value);
checkArr.push(farPointLengthCheck.value);
if (nearPointLengthData.value != "" && nearPointLengthData.value != undefined && farPointLengthData.value != "" && farPointLengthData.value != undefined) {
semiMajorAxisData.value = ((nearPointLengthData.value + equatorRadius) + (farPointLengthData.value + equatorRadius)) / 2;
eccentricityData.value = (semiMajorAxisData.value - (nearPointLengthData.value + equatorRadius)) / semiMajorAxisData.value;
orbitalPeriodData.value = ((semiMajorAxisData.value * 1000) / 21613.546) ** 1.5;
angularVelocityRadData.value = (2 * Math.PI) / orbitalPeriodData.value;
angularVelocityDegData.value = angularVelocityRadData.value * (180 / Math.PI);
}
semiMajorAxisDataCheck();
checkArr.push(semiMajorAxisCheck.value);
eccentricityDataCheck();
checkArr.push(eccentricityCheck.value);
inclinationDataCheck();
checkArr.push(inclinationCheck.value);
ascendantRightAscensionDataCheck();
checkArr.push(ascendantRightAscensionCheck.value);
argumentOfPeriapsisDataCheck();
checkArr.push(argumentOfPeriapsisCheck.value);
return booleanCheck(checkArr);
};
const generateOrbit = () => {
if (generateCheck()) {
emit("generateOrbit", "technology");
show.value = false;
setTimeout(() => {
resetData();
}, 0);
}
};
defineExpose({ data });
</script>
<style scoped>
.satelliteCreate {
font-size: 1rem;
color: #ffffff;
background-color: rgb(255 255 255 / 0.5);
width: 6rem;
height: 2rem;
line-height: 2rem;
overflow: hidden;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 0.5);
position: fixed;
bottom: 14.5rem;
right: 1rem;
border-radius: 1rem;
}
.satelliteCreate:hover {
color: #000000;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
background-color: rgb(250, 230, 164);
}
.option {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.dataInput {
padding-left: 0.5rem;
padding-right: 0.5rem;
height: 2rem;
background: #ffffff;
border-radius: 1rem;
}
.buttonClass {
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
width: 8rem;
height: 2rem;
line-height: 2rem;
color: #000000;
background-color: rgb(165 243 252 / 1);
overflow: hidden;
cursor: pointer;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
border-radius: 2rem;
}
</style>

View File

@@ -0,0 +1,631 @@
<template>
<div
v-if="show"
class="text-left p-10"
style="
color: #ffffff;
background-color: rgb(55 55 55 / 0.9);
border-radius: 2rem;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
top: 10vh;
left: 20vw;
width: 60vw;
height: 80vh;
font-size: 1rem;
"
>
<div
@click="close"
class="bg-gray-600 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
style="
width: 2rem;
height: 2rem;
border-top-right-radius: 1rem;
border-bottom-left-radius: 1rem;
"
>
<el-icon style="width: 2rem; height: 2rem">
<CloseBold color="#ffffff" />
</el-icon>
</div>
<div class="w-full text-center" style="font-size: 2rem; height: 4rem; line-height: 4rem;">修改轨道数据</div>
<div class="text-white text-center flex flex-wrap justify-center items-center" style="height: 80%; font-size: 1rem;">
<div class="option">
<label :style="satelliteNameCheck ? '' : 'color: red;'">轨道名称:</label>
<input
type="text"
v-model="satelliteNameModify"
placeholder="请输入卫星名称"
class="dataInput"
:style="satelliteNameCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
</div>
<div class="option">
<label :style="nearPointLengthCheck ? '' : 'color: red;'">卫星近地点高度</label>
<label :style="nearPointLengthCheck ? 'color: #00efff;' : 'color: red;'">(单位:㎞)</label>
<label :style="nearPointLengthCheck ? '' : 'color: red;'">:</label>
<input
type="number"
maxlength="6"
v-model="nearPointLengthModify"
placeholder="请输入数据(单位:㎞)"
class="dataInput"
:style="nearPointLengthCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点高度就是卫星运行时,离地球最近的那个点,到地球表面的垂直距离。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星的运行轨道不是正圆形,而是有点 “扁” 的椭圆。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星绕着地球转时,会有一个离地球最近的位置,这个位置就是 “近地点”。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点高度,就是从这个最近的点,垂直往下量到地心的距离。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="farPointLengthCheck ? '' : 'color: red;'">卫星远地点高度</label>
<label :style="farPointLengthCheck ? 'color: #00efff;' : 'color: red;'">(单位:㎞)</label>
<label :style="farPointLengthCheck ? '' : 'color: red;'">:</label>
<input
type="number"
maxlength="6"
v-model="farPointLengthModify"
placeholder="请输入数据(单位:㎞)"
class="dataInput"
:style="farPointLengthCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
远地点高度就是卫星运行时,离地球最远的那个点,到地球表面的垂直距离。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星的运行轨道不是正圆形,而是有点 “扁” 的椭圆。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星绕着地球转时,会有一个离地球最远的位置,这个位置就是 “远地点”。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
远地点高度,就是从这个最远的点,垂直往下量到地心的距离。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="semiMajorAxisCheck ? '' : 'color: red;'">轨道半长轴</label>
<label :style="semiMajorAxisCheck ? 'color: #00efff;' : 'color: red;'">(介于200㎞~120000㎞之间)</label>
<label :style="semiMajorAxisCheck ? '' : 'color: red;'">:</label>
<label :style="semiMajorAxisCheck ? 'color: rgb(250, 230, 164);' : 'color: red;'">{{ semiMajorAxisModify === '' ? '' :Number(semiMajorAxisModify).toFixed(3) }}</label>
<label :style="semiMajorAxisCheck ? 'color: #00efff;' : 'color: red;'">(单位:㎞)</label>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星轨道为椭圆形,轨道半长轴就是 “长轴的一半长度”,也就是从椭圆(轨道)中心到椭圆最长端(远地点)的距离。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
它不是到地球表面的距离,而是从椭圆中心(不是地球球心)到轨道边缘的最长半径。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
简单说:椭圆轨道像个 “拉长的圆”,半长轴就相当于这个 “拉长圆” 的 “平均半径”—— 能反映轨道的整体大小。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道半长轴决定了轨道的大小,决定卫星离地球的平均距离,决定卫星飞得高还是低。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星飞得越高,速度越慢,周期越长,半径越大,轨道越大,卫星离地球越远。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
b为半短轴,半短轴 b 越接近半长轴 a;当偏心率=0(正圆轨道)时,b=a,此时半短轴就是圆的半径。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="eccentricityCheck ? '' : 'color: red;'">轨道偏心率</label>
<label style="color: #00efff;">(不超过0.9)</label>
<label :style="eccentricityCheck ? '' : 'color: red;'">:</label>
<label :style="eccentricityCheck ? 'color: rgb(250, 230, 164);' : 'color: red;'">{{ eccentricityModify === '' ? '' : Number(eccentricityModify).toFixed(6) }}</label>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道偏心率决定了轨道的形状,是用来描述天体轨道形状偏离圆形程度的一个数值。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道偏心率是椭圆两焦点之间的距离(焦距)与长轴的比值,也可以换算为半焦距与半长轴的比值,描述椭圆扁平程度。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
用 0 到 1 之间的数字表示,偏心率越接近 0,轨道越圆;偏心率越接近 1,轨道越扁(但不会等于 1,等于 1 就不是椭圆了)。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点和远地点距离差不多(轨道圆),偏心率就小。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点离地球很近、远地点离地球很远(轨道扁),偏心率就大。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
用于描述轨道的形状,表征轨道椭圆的扁平程度。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
假设轨道半长轴为b,轨道焦点到轨道中心距离为a,轨道离心率为e,则:e = a ÷ b。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label>轨道周期</label>
<label style="color: #00efff;">(秒)</label>
<label>:</label>
<label style="color: rgb(250, 230, 164);">{{ orbitalPeriodModify === '' ? '' : Number(orbitalPeriodModify).toFixed(3) }}</label>
<label style="color: #00efff;">(单位:s)</label>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道周期就是卫星绕中心天体(比如地球)完整转一圈的时间。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
简单说,轨道周期就是从卫星某个位置(比如近地点)出发,绕轨道飞一圈后,再回到这个位置的总时间。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
万有引力常数 G = 6.67430 × 10^⁻¹¹ 米³ / (千克 × 秒²),地球质量 M = 5.965 × 10²⁴ 千克。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
人造地球卫星运转周期T(秒)与圆轨道半径或椭圆轨道半长轴R(米)之间的关系可用下列公式计算:T = 2π × (R³ ÷ GM)½ 。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label>平均角速度</label>
<label style="color: #00efff;">(度/秒)</label>
<label>:</label>
<label style="color: rgb(250, 230, 164);">{{ angularVelocityDegModify === '' ? '' : Number(angularVelocityDegModify).toFixed(6) }}</label>
<label style="color: #00efff;">(单位:°/s)</label>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
平均角速度就是卫星绕中心天体转的时候,单位时间内转过的角度,简单说就是 “转得快不快” 的 “角度版” 描述。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
周期越短(转一圈时间少),角速度越大(转得快)。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
通俗理解:比如卫星 1 小时转了 45°,它的平均角速度就是 45°/ 小时;转得越快,单位时间内转过的角度越大,角速度就越大。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星飞得越高,速度越慢,卫星飞得越低,速度越快,平均角速度是卫星在轨道上的平均运行速度。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="inclinationCheck ? '' : 'color: red;'">轨道倾角</label>
<label :style="inclinationCheck ? 'color: #00efff;' : 'color: red;'">(0°~180°)</label>
<label :style="inclinationCheck ? '' : 'color: red;'">:</label>
<input
type="number"
maxlength="3"
v-model="inclinationModify"
placeholder="请输入数据(0°~180°)"
class="dataInput"
:style="inclinationCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道倾角就是轨道相对于地球赤道的倾斜程度,是卫星绕地球转的轨道平面,和地球赤道平面之间的 “夹角”,用 0° 到 180° 的数字表示,核心是描述轨道 “歪不歪”,“往哪个方向歪”。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
地球赤道平面:想象过地球赤道画一个无限大的 “隐形圆盘”,这就是赤道平面。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
卫星轨道平面:卫星绕地球转的轨迹所在的 “隐形圆盘”,就是轨道平面。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道倾角,就是这两个 “隐形圆盘” 之间夹着的角度,就像两本书打开时书页之间的夹角一样。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
也可以类比滑滑梯的角度,把地面比作赤道,卫星轨道是倾斜的滑梯,倾角就是地面和滑梯的夹角。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="ascendantRightAscensionCheck ? '' : 'color: red;'">升交点赤经</label>
<label :style="ascendantRightAscensionCheck ? 'color: #00efff;' : 'color: red;'">(0°~360°)</label>
<label :style="ascendantRightAscensionCheck ? '' : 'color: red;'">:</label>
<input
type="number"
maxlength="3"
v-model="ascendantRightAscensionModify"
placeholder="请输入数据(0°~360°)"
class="dataInput"
:style="ascendantRightAscensionCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道在空间中的方向,轨道平面在太空中的旋转角度,轨道在赤道上的起点,共同确定了轨道平面和赤道平面交点的位置,相当于给倾斜的轨道找一个基准起点。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
升交点赤经就是给卫星轨道在宇宙中 “定方向” 的参数,核心是描述轨道与地球赤道交点的 “东向位置”。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
升交点:卫星从地球南半球飞到北半球时,会穿过赤道平面,这个穿过的点就是升交点。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
春分点:太阳从地球的南半球向北半球运动穿过地球赤道平面的那个点,可以理解为太阳的升交点,每年春分(3月21日左右)的时候,太阳经过该点。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
赤经:与经度定义类似,但其参考点为春分点,且不随地球转自转运动。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
升交点赤经描述的是一个角度,这个角度就是“春分点到地心连线”与“升交点到地心连线”的夹角,表征了轨道平面相对春分点的方位。结合上一节的轨道倾角,就完全确定了轨道平面在三维空间中的方位。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<label :style="argumentOfPeriapsisCheck ? '' : 'color: red;'">近地点角距</label>
<label :style="argumentOfPeriapsisCheck ? 'color: #00efff;' : 'color: red;'">(0°~360°)</label>
<label :style="argumentOfPeriapsisCheck ? '' : 'color: red;'">:</label>
<input
type="number"
maxlength="3"
v-model="argumentOfPeriapsisModify"
placeholder="请输入数据(0°~360°)"
class="dataInput"
:style="argumentOfPeriapsisCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
<el-tooltip
class="box-item"
effect="dark"
content="
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点角距(近地点幅角)就是从升交点开始,沿着卫星轨道往飞行方向量,到近地点的角度,核心是给近地点 “定具体位置”。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
近地点角距(近地点幅角),就是以地球球心为中心,在卫星轨道平面上,从升交点出发,顺着卫星飞行的方向,画一条线到近地点,这条线和 “升交点到地心连线” 之间的夹角(范围 0°-360°)。
</div>
<div style='font-size: 1rem; color: rgb(165 243 252 / 1); line-height: 1.5rem;'>
轨道在平面内的朝向,轨道椭圆在轨道平面内的旋转角度,就像钟表指针指向的方向,确定近地点在轨道上的位置,近地点相对于升交点的位置,近地点角距(近地点幅角)决定了卫星离地球最近时,指向哪个方向。
</div>
"
raw-content
placement="top"
>
<el-icon style="margin-left: 0.5rem;"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="option">
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="modifySatellite"
value="修改数据"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { CloseBold, QuestionFilled } from "@element-plus/icons-vue";
const props = defineProps({
satelliteArr: Array,
satelliteId: Number,
});
const emit = defineEmits([
"closeSatelliteModifyPanel",
"selectSatelliteModel",
"modifySatellite"
]);
// 地球赤道半径 6378137 米
const equatorRadius = 6378.137;
const show = ref(false);
const satelliteNameModify = ref();
const nearPointLengthModify = ref();
const farPointLengthModify = ref();
const semiMajorAxisModify = ref();
const eccentricityModify = ref();
const orbitalPeriodModify = ref();
const angularVelocityRadModify = ref();
const angularVelocityDegModify = ref();
const inclinationModify = ref();
const ascendantRightAscensionModify = ref();
const argumentOfPeriapsisModify = ref();
const satelliteNameCheck = ref(true);
const nearPointLengthCheck = ref(true);
const farPointLengthCheck = ref(true);
const semiMajorAxisCheck = ref(true);
const eccentricityCheck = ref(true);
const inclinationCheck = ref(true);
const ascendantRightAscensionCheck = ref(true);
const argumentOfPeriapsisCheck = ref(true);
const data = {
satelliteNameModify,
nearPointLengthModify,
farPointLengthModify,
semiMajorAxisModify,
eccentricityModify,
orbitalPeriodModify,
angularVelocityRadModify,
angularVelocityDegModify,
inclinationModify,
ascendantRightAscensionModify,
argumentOfPeriapsisModify,
};
const close = () => {
show.value = false;
emit("closeSatelliteModifyPanel");
};
const booleanCheck = (checkArr) => {
let check = true;
checkArr.forEach((item) => {
if (item == false) {
check = false;
}
});
return check;
};
const satelliteNameModifyCheck = () => {
if (satelliteNameModify.value === "" || satelliteNameModify.value === undefined) {
satelliteNameCheck.value = false;
} else {
satelliteNameCheck.value = true;
}
props.satelliteArr.forEach((entity, index) => {
for (const element in entity) {
if (entity[element].name !== undefined) {
let name = entity[element].name;
if (satelliteNameModify.value == name && index !== props.satelliteId - 1) {
satelliteNameCheck.value = false;
}
}
}
});
};
const nearPointLengthModifyCheck = () => {
if (nearPointLengthModify.value === "" || nearPointLengthModify.value === undefined || nearPointLengthModify.value < 200 || nearPointLengthModify.value > 120000) {
nearPointLengthCheck.value = false;
} else {
nearPointLengthCheck.value = true;
}
if (nearPointLengthCheck.value == 1 && farPointLengthModify.value !== "" && farPointLengthModify.value !== undefined && nearPointLengthModify.value > farPointLengthModify.value) {
nearPointLengthCheck.value = false;
farPointLengthCheck.value = false;
} else {
farPointLengthCheck.value = true;
}
};
const farPointLengthModifyCheck = () => {
if (farPointLengthModify.value === "" || farPointLengthModify.value === undefined || farPointLengthModify.value < 200 || farPointLengthModify.value > 120000) {
farPointLengthCheck.value = false;
} else {
farPointLengthCheck.value = true;
}
if (farPointLengthCheck.value == 1 && nearPointLengthModify.value !== "" && nearPointLengthModify.value !== undefined && nearPointLengthModify.value > farPointLengthModify.value) {
nearPointLengthCheck.value = false;
farPointLengthCheck.value = false;
} else {
nearPointLengthCheck.value = true;
}
};
const semiMajorAxisModifyCheck = () => {
if (semiMajorAxisModify.value === '' || semiMajorAxisModify.value === undefined || semiMajorAxisModify.value < 200 || semiMajorAxisModify.value > 120000) {
semiMajorAxisCheck.value = false;
} else {
semiMajorAxisCheck.value = true;
}
};
const eccentricityModifyCheck = () => {
if (eccentricityModify.value === "" || eccentricityModify.value === undefined || eccentricityModify.value < 0 || eccentricityModify.value > 0.9) {
eccentricityCheck.value = false;
} else {
eccentricityCheck.value = true;
}
};
const inclinationModifyCheck = () => {
if (inclinationModify.value === "" || inclinationModify.value === undefined || inclinationModify.value < 0 || inclinationModify.value > 180) {
inclinationCheck.value = false;
} else {
inclinationCheck.value = true;
}
};
const ascendantRightAscensionModifyCheck = () => {
if (ascendantRightAscensionModify.value === "" || ascendantRightAscensionModify.value === undefined || ascendantRightAscensionModify.value < 0 || ascendantRightAscensionModify.value > 360) {
ascendantRightAscensionCheck.value = false;
} else {
ascendantRightAscensionCheck.value = true;
}
};
const argumentOfPeriapsisModifyCheck = () => {
if (argumentOfPeriapsisModify.value === "" || argumentOfPeriapsisModify.value === undefined || argumentOfPeriapsisModify.value < 0 || argumentOfPeriapsisModify.value > 360) {
argumentOfPeriapsisCheck.value = false;
} else {
argumentOfPeriapsisCheck.value = true;
}
};
watch(satelliteNameModify, (value) => {
satelliteNameModifyCheck();
});
watch(nearPointLengthModify, (value) => {
nearPointLengthModifyCheck();
});
watch(farPointLengthModify, (value) => {
farPointLengthModifyCheck();
});
watch(semiMajorAxisModify, (value) => {
semiMajorAxisModifyCheck();
});
watch(eccentricityModify, (value) => {
eccentricityModifyCheck();
});
watch(inclinationModify, (value) => {
inclinationModifyCheck();
});
watch(ascendantRightAscensionModify, (value) => {
ascendantRightAscensionModifyCheck();
});
watch(argumentOfPeriapsisModify, (value) => {
argumentOfPeriapsisModifyCheck();
});
const generateCheck = () => {
let checkArr = [];
satelliteNameModifyCheck();
checkArr.push(satelliteNameCheck.value);
nearPointLengthModifyCheck();
farPointLengthModifyCheck();
checkArr.push(nearPointLengthCheck.value);
checkArr.push(farPointLengthCheck.value);
if (nearPointLengthModify.value != "" && nearPointLengthModify.value != undefined && farPointLengthModify.value != "" && farPointLengthModify.value != undefined) {
semiMajorAxisModify.value = (nearPointLengthModify.value + equatorRadius + (farPointLengthModify.value + equatorRadius)) / 2;
eccentricityModify.value = (semiMajorAxisModify.value - (nearPointLengthModify.value + equatorRadius)) / semiMajorAxisModify.value;
orbitalPeriodModify.value = ((semiMajorAxisModify.value * 1000) / 21613.546) ** 1.5;
angularVelocityRadModify.value = (2 * Math.PI) / orbitalPeriodModify.value;
angularVelocityDegModify.value = angularVelocityRadModify.value * (180 / Math.PI);
}
semiMajorAxisModifyCheck();
checkArr.push(semiMajorAxisCheck.value);
eccentricityModifyCheck();
checkArr.push(eccentricityCheck.value);
inclinationModifyCheck();
checkArr.push(inclinationCheck.value);
ascendantRightAscensionModifyCheck();
checkArr.push(ascendantRightAscensionCheck.value);
argumentOfPeriapsisModifyCheck();
checkArr.push(argumentOfPeriapsisCheck.value);
return booleanCheck(checkArr);
};
const modifySatellite = () => {
if (generateCheck()) {
emit("modifySatellite");
}
};
defineExpose({ show, data });
</script>
<style scoped>
.option {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.dataInput {
padding-left: 0.5rem;
padding-right: 0.5rem;
height: 2rem;
background: #ffffff;
border-radius: 1rem;
}
.buttonClass {
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
width: 12rem;
height: 2rem;
line-height: 2rem;
color: #000000;
background-color: rgb(165 243 252 / 1);
overflow: hidden;
cursor: pointer;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
border-radius: 2rem;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,141 @@
<template>
<table class="text-center">
<thead class="text-2xl">
<th
class="py-3 bg-blue-900 border border-gray-900"
v-for="(label, index) in ['卫星序号', '卫星名称', '显示/隐藏', '跟踪卫星', '操作']"
:key="label"
:style="{ width: index == 4 ? '40vw' : '20vw' }"
>
<div style="display: flex; justify-content: center; align-items: center;">
<div style="margin-right: 0.5rem;">
{{ label }}
</div>
<input
v-if="label == '显示/隐藏'"
:disabled="satelliteArr.length > 0 ? false : true"
indeterminate="false"
:checked="allSubShow"
type="checkbox"
class="w-6 h-6 cursor-pointer"
@change="allSatelliteShow('sub')"
/>
<input
v-if="label == '操作'"
:disabled="satelliteArr.length > 0 ? false : true"
type="button"
class="buttonClass"
id="fly-btn"
@click="allClearSatellite('sub')"
value="清除全部卫星"
/>
</div>
</th>
</thead>
<tbody class="text-lg odd:bg-gray-50">
<tr v-for="(entity, index) in satelliteArr[satelliteId - 1].subSatelliteArr" :key="index">
<td class="border border-gray-900"> {{ index + 1 }} </td>
<td class="border border-gray-900">{{ entity.satelliteModel.name }}</td>
<td class="border border-gray-900">
<div style="display: flex; justify-content: center; align-items: center;">
<input
:checked="entity.show"
type="checkbox"
class="w-6 h-6 cursor-pointer"
@change="onShowSatellite(index, 'sub')"
/>
</div>
</td>
<td class="border border-gray-900">
<div style="display: flex; justify-content: center; align-items: center;">
<button @click="onTrackSatellite(index, 'sub')">
<TrackSvg />
</button>
</div>
</td>
<td class="border border-gray-900" style="display: flex; justify-content: space-around; align-items: center;">
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="modifySatellitePanel(index, 'sub')"
value="修改数据"
/>
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="onShowSatelliteControlPanel(index, 'sub')"
value="控制面板"
/>
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="clearSatellite(index, 'sub')"
value="清除卫星"
/>
</td>
</tr>
</tbody>
</table>
</template>
<script setup>
import TrackSvg from "../../TrackSvg.vue";
const props = defineProps({
satelliteArr: Array,
satelliteId: String,
allSubShow: Boolean
});
const emit = defineEmits([
"allSatelliteShow",
"allClearSatellite",
"modifySatellitePanel",
"onShowSatelliteControlPanel",
"onShowSatellite",
"onTrackSatellite",
"clearSatellite"
]);
const allSatelliteShow = (model) => {
emit("allSatelliteShow", model);
};
const allClearSatellite = (model) => {
emit("allClearSatellite", model);
};
const modifySatellitePanel = (index, model) => {
emit("modifySatellitePanel", index, model);
};
const onShowSatelliteControlPanel = (index, model) => {
emit("onShowSatelliteControlPanel", index, model);
};
const onShowSatellite = (index, model) => {
emit("onShowSatellite", index, model);
};
const onTrackSatellite = (index, model) => {
emit("onTrackSatellite", index, model);
};
const clearSatellite = (index, model) => {
emit("clearSatellite", index, model);
};
</script>
<style scoped>
.buttonClass {
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
cursor: pointer;
height: 2rem;
line-height: 2rem;
}
</style>

View File

@@ -0,0 +1,139 @@
<template>
<div
v-if="show"
class="text-left p-10"
style="
color: #ffffff;
background-color: rgb(55 55 55 / 0.9);
border-radius: 2rem;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
top: 10vh;
left: 20vw;
width: 60vw;
height: 80vh;
font-size: 1rem;
"
>
<div
@click="close"
class="bg-gray-600 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
style="
width: 2rem;
height: 2rem;
border-top-right-radius: 1rem;
border-bottom-left-radius: 1rem;
"
>
<el-icon style="width: 2rem; height: 2rem;">
<CloseBold color="#ffffff" />
</el-icon>
</div>
<div class="w-full text-center" style="font-size: 2rem; height: 4rem; line-height: 4rem;">
卫星控制面板
</div>
<div class="text-white text-center flex flex-wrap justify-center items-center" style="height: 80%; font-size: 1rem;">
<div class="option">
<label>显示/隐藏卫星模型:</label>
<input type="checkbox" v-model="subSatelliteModelShow" class="w-6 h-6 cursor-pointer" />
</div>
<div class="option">
<label>卫星信号覆盖范围颜色:</label>
<select v-model="subCylinderSelected" style="cursor: pointer; color: black;">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<label>显示/隐藏卫星信号覆盖范围:</label>
<input type="checkbox" v-model="subCylinderShow" class="w-6 h-6 cursor-pointer" />
</div>
<div class="option">
<label>显示/隐藏卫星全部信息:</label>
<input type="checkbox" v-model="subSatelliteShow" class="w-6 h-6 cursor-pointer" />
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
const emit = defineEmits([
"closeSubSatelliteControlPanel",
"handleSubSatelliteControl",
]);
const show = ref(false);
const subSatelliteModelShow = ref(true);
const subCylinderSelected = ref("GREEN");
const subCylinderShow = ref(true);
const subSatelliteShow = ref(true);
const cesiumColor = {
BLUE: "蓝色",
PURPLE: "紫色",
RED: "红色",
YELLOW: "黄色",
CYAN: "青色",
ORANGE: "橙色",
PINK: "粉色",
GREEN: "绿色",
WHITE: "白色",
LIGHTBLUE: "浅蓝色",
LIME: "青柠色",
MAGENTA: "品红色",
};
const data = {
subSatelliteModelShow,
subCylinderSelected,
subCylinderShow,
subSatelliteShow,
};
const close = () => {
show.value = false;
emit("closeSubSatelliteControlPanel");
};
defineExpose({ show, data });
watch(subSatelliteModelShow, (value) => {
emit("handleSubSatelliteControl", "subSatelliteModelShow", value);
});
watch(subCylinderSelected, (value) => {
emit("handleSubSatelliteControl", "subCylinderSelected", value);
});
watch(subCylinderShow, (value) => {
emit("handleSubSatelliteControl", "subCylinderShow", value);
});
watch(subSatelliteShow, (value) => {
emit("handleSubSatelliteControl", "subSatelliteShow", value);
});
</script>
<style scoped>
.option {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.buttonClass {
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
cursor: pointer;
width: 12rem;
height: 2rem;
line-height: 2rem;
}
</style>

View File

@@ -0,0 +1,298 @@
<template>
<div
v-if="show"
class="text-left p-10"
style="
color: #ffffff;
background-color: rgb(55 55 55 / 0.9);
border-radius: 2rem;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
top: 10vh;
left: 20vw;
width: 60vw;
height: 80vh;
font-size: 1rem;
"
>
<div
@click="close"
class="bg-gray-600 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
style="
width: 2rem;
height: 2rem;
border-top-right-radius: 1rem;
border-bottom-left-radius: 1rem;
"
>
<el-icon style="width: 2rem; height: 2rem;">
<CloseBold color="#ffffff" />
</el-icon>
</div>
<div class="w-full text-center" style="font-size: 2rem; height: 4rem; line-height: 4rem;">
添加卫星
</div>
<div class="text-white text-center flex flex-wrap justify-center items-center" style="height: 80%; font-size: 1rem;">
<div class="option">
<label :style="satelliteNameCheck ? '' : 'color: red;'">卫星名称:</label>
<input
type="text"
maxlength="12"
v-model="subSatelliteNameData"
placeholder="请输入卫星名称"
class="dataInput"
:style="satelliteNameCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
</div>
<div class="option">
<label :style="trueAnomalyCheck ? '' : 'color: red;'">真近点角</label>
<label :style="trueAnomalyCheck ? 'color: #00efff;' : 'color: red;'">(0°~360°):</label>
<input
type="number"
maxlength="3"
v-model="subTrueAnomalyData"
placeholder="请输入数据(0°~360°)"
class="dataInput"
:style="trueAnomalyCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
</div>
<div class="option">
<label :style="beamWidthCheck ? '' : 'color: red;'">波束宽度</label>
<label :style="beamWidthCheck ? 'color: #00efff;' : 'color: red;'">(0.1°~22°):</label>
<input
type="number"
maxlength="4"
v-model="subBeamWidthData"
placeholder="请输入数据"
class="dataInput"
:style="beamWidthCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
</div>
<div class="option">
<label>卫星信号覆盖范围颜色:</label>
<select v-model="subCylinderSelectedData" style="cursor: pointer; color: black;">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<div>
<div>当前卫星模型:{{ satelliteName }}</div>
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="selectSatelliteModel"
value="选择卫星模型"
/>
</div>
</div>
<div class="option">
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="generateSubSatellite"
value="添加卫星"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
const props = defineProps({
satelliteArr: Array,
});
const emit = defineEmits([
"closeSubSatelliteCreatePanel",
"selectSatelliteModel",
"generateSubSatellite"
]);
const show = ref(false);
const subSatelliteNameData = ref();
const subTrueAnomalyData = ref();
const subBeamWidthData = ref();
const subCylinderSelectedData = ref("GREEN");
const satelliteNameCheck = ref(true);
const trueAnomalyCheck = ref(true);
const beamWidthCheck = ref(true);
const satelliteName = ref("BeidouGEO");
const satelliteGif = ref("/image/gif/BeidouGEO.gif");
const cesiumColor = {
BLUE: "蓝色",
PURPLE: "紫色",
RED: "红色",
YELLOW: "黄色",
CYAN: "青色",
ORANGE: "橙色",
PINK: "粉色",
GREEN: "绿色",
WHITE: "白色",
LIGHTBLUE: "浅蓝色",
LIME: "青柠色",
MAGENTA: "品红色",
};
const data = {
subSatelliteNameData,
subTrueAnomalyData,
subBeamWidthData,
subCylinderSelectedData,
};
const resetData = () => {
subSatelliteNameData.value = "";
subTrueAnomalyData.value = "";
subBeamWidthData.value = "";
satelliteNameCheck.value = true;
trueAnomalyCheck.value = true;
beamWidthCheck.value = true;
satelliteName.value = "BeidouGEO";
satelliteGif.value = "/image/gif/BeidouGEO.gif";
};
const close = () => {
show.value = false;
setTimeout(() => {
resetData();
}, 0);
emit("closeSubSatelliteCreatePanel");
};
const booleanCheck = (checkArr) => {
let check = true;
checkArr.forEach((item) => {
if (item == false) {
check = false;
}
});
return check;
};
const subSatelliteNameDataCheck = () => {
if (subSatelliteNameData.value === "" || subSatelliteNameData.value === undefined) {
satelliteNameCheck.value = false;
} else {
satelliteNameCheck.value = true;
}
props.satelliteArr.forEach((entity) => {
for (const element in entity) {
if (entity[element].name !== undefined) {
let name = entity[element].name;
if (subSatelliteNameData.value == name) {
satelliteNameCheck.value = false;
}
}
if (element == "subSatelliteArr") {
entity[element].forEach((item, index) => {
for (const subElement in item) {
if (item[subElement].name !== undefined) {
let name = item[subElement].name;
if (subSatelliteNameData.value == name) {
satelliteNameCheck.value = false;
}
}
}
});
}
}
});
};
const subTrueAnomalyDataCheck = () => {
if (subTrueAnomalyData.value === "" || subTrueAnomalyData.value === undefined || subTrueAnomalyData.value < 0 || subTrueAnomalyData.value > 360) {
trueAnomalyCheck.value = false;
} else {
trueAnomalyCheck.value = true;
}
};
const subBeamWidthDataCheck = () => {
if (subBeamWidthData.value === "" || subBeamWidthData.value === undefined || subBeamWidthData.value <= 0 || subBeamWidthData.value > 22) {
beamWidthCheck.value = false;
} else {
beamWidthCheck.value = true;
}
};
watch(subSatelliteNameData, (value) => {
subSatelliteNameDataCheck();
});
watch(subTrueAnomalyData, (value) => {
subTrueAnomalyDataCheck();
});
watch(subBeamWidthData, (value) => {
subBeamWidthDataCheck();
});
const generateCheck = () => {
let checkArr = [];
subSatelliteNameDataCheck();
checkArr.push(satelliteNameCheck.value);
subTrueAnomalyDataCheck();
checkArr.push(trueAnomalyCheck.value);
subBeamWidthDataCheck();
checkArr.push(beamWidthCheck.value);
return booleanCheck(checkArr);
};
const selectSatelliteModel = () => {
emit("selectSatelliteModel");
};
const generateSubSatellite = () => {
if (generateCheck()) {
setTimeout(() => {
resetData();
}, 0);
emit("generateSubSatellite", "subSatellite");
}
};
defineExpose({ show, satelliteName, satelliteGif, data });
</script>
<style scoped>
.dataInput {
padding-left: 0.5rem;
padding-right: 0.5rem;
height: 2rem;
background: #ffffff;
border-radius: 1rem;
}
.option {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.buttonClass {
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
cursor: pointer;
width: 15rem;
height: 2rem;
line-height: 2rem;
}
</style>

View File

@@ -0,0 +1,256 @@
<template>
<div
v-if="show"
class="text-left p-10"
style="
color: #ffffff;
background-color: rgb(55 55 55 / 0.9);
border-radius: 2rem;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
top: 10vh;
left: 20vw;
width: 60vw;
height: 80vh;
font-size: 1rem;
"
>
<div
@click="close"
class="bg-gray-600 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
style="
width: 2rem;
height: 2rem;
border-top-right-radius: 1rem;
border-bottom-left-radius: 1rem;
"
>
<el-icon style="width: 2rem; height: 2rem;">
<CloseBold color="#ffffff" />
</el-icon>
</div>
<div class="w-full text-center" style="font-size: 2rem; height: 4rem; line-height: 4rem;">修改卫星数据</div>
<div class="text-white text-center flex flex-wrap justify-center items-center" style="height: 80%; font-size: 1rem;">
<div class="option">
<label :style="satelliteNameCheck ? '' : 'color: red;'">卫星名称:</label>
<input
type="text"
v-model="subSatelliteNameModify"
placeholder="请输入卫星名称"
class="dataInput"
:style="satelliteNameCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
</div>
<div class="option">
<label :style="trueAnomalyCheck ? '' : 'color: red;'">真近点角</label>
<label :style="trueAnomalyCheck ? 'color: #00efff;' : 'color: red;'">(0°~360°):</label>
<input
type="number"
maxlength="3"
v-model="subTrueAnomalyModify"
placeholder="请输入数据(0°~360°)"
class="dataInput"
:style="trueAnomalyCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
</div>
<div class="option">
<label :style="beamWidthCheck ? '' : 'color: red;'">波束宽度</label>
<label :style="beamWidthCheck ? 'color: #00efff;' : 'color: red;'">(0.1°~22°):</label>
<input
type="number"
maxlength="4"
v-model="subBeamWidthModify"
placeholder="请输入数据"
class="dataInput"
:style="beamWidthCheck ? 'border: 2px solid black; color: black;' : 'border: 2px solid red; color: red;'"
/>
</div>
<div class="option">
<div>
<div>当前卫星模型:{{ satelliteName }}</div>
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="selectSatelliteModel"
value="选择卫星模型"
/>
</div>
</div>
<div class="option">
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="modifySubSatellite"
value="修改数据"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
const props = defineProps({
satelliteArr: Array,
subSatelliteId: String,
});
const emit = defineEmits([
"closeSubSatelliteModifyPanel",
"selectSatelliteModel",
"modifySubSatellite"
]);
const show = ref(false);
const subSatelliteNameModify = ref();
const subTrueAnomalyModify = ref();
const subBeamWidthModify = ref();
const satelliteNameCheck = ref(true);
const trueAnomalyCheck = ref(true);
const beamWidthCheck = ref(true);
const satelliteName = ref("BeidouGEO");
const satelliteGif = ref("/image/gif/BeidouGEO.gif");
const data = {
subSatelliteNameModify,
subTrueAnomalyModify,
subBeamWidthModify,
};
const close = () => {
show.value = false;
emit("closeSubSatelliteModifyPanel");
};
const booleanCheck = (checkArr) => {
let check = true;
checkArr.forEach((item) => {
if (item == false) {
check = false;
}
});
return check;
};
const subSatelliteNameModifyCheck = () => {
if (subSatelliteNameModify.value === "" || subSatelliteNameModify.value === undefined) {
satelliteNameCheck.value = false;
} else {
satelliteNameCheck.value = true;
}
props.satelliteArr.forEach((entity) => {
for (const element in entity) {
if (entity[element].name !== undefined) {
let name = entity[element].name;
if (subSatelliteNameModify.value == name) {
satelliteNameCheck.value = false;
}
}
if (element == "subSatelliteArr") {
entity[element].forEach((item, index) => {
for (const subElement in item) {
if (item[subElement].name !== undefined) {
let name = item[subElement].name;
if (subSatelliteNameModify.value == name) {
let item = Number(props.subSatelliteId.split("-")[1]) - 1;
if (index !== item) {
satelliteNameCheck.value = false;
}
}
}
}
});
}
}
});
};
const subTrueAnomalyModifyCheck = () => {
if (subTrueAnomalyModify.value === "" || subTrueAnomalyModify.value === undefined || subTrueAnomalyModify.value < 0 || subTrueAnomalyModify.value > 360) {
trueAnomalyCheck.value = false;
} else {
trueAnomalyCheck.value = true;
}
};
const subBeamWidthModifyCheck = () => {
if (subBeamWidthModify.value === "" || subBeamWidthModify.value === undefined || subBeamWidthModify.value <= 0 || subBeamWidthModify.value > 22) {
beamWidthCheck.value = false;
} else {
beamWidthCheck.value = true;
}
};
watch(subSatelliteNameModify, (value) => {
subSatelliteNameModifyCheck();
});
watch(subTrueAnomalyModify, (value) => {
subTrueAnomalyModifyCheck();
});
watch(subBeamWidthModify, (value) => {
subBeamWidthModifyCheck();
});
const generateCheck = () => {
let checkArr = [];
subSatelliteNameModifyCheck();
checkArr.push(satelliteNameCheck.value);
subTrueAnomalyModifyCheck();
checkArr.push(trueAnomalyCheck.value);
subBeamWidthModifyCheck();
checkArr.push(beamWidthCheck.value);
return booleanCheck(checkArr);
};
const selectSatelliteModel = () => {
emit("selectSatelliteModel");
};
const modifySubSatellite = () => {
if (generateCheck()) {
emit("modifySubSatellite");
}
};
defineExpose({ show, satelliteName, satelliteGif, data });
</script>
<style scoped>
.dataInput {
padding-left: 0.5rem;
padding-right: 0.5rem;
height: 2rem;
background: #ffffff;
border-radius: 1rem;
}
.option {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.buttonClass {
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
cursor: pointer;
width: 12rem;
height: 2rem;
line-height: 2rem;
}
</style>

View File

@@ -0,0 +1,285 @@
<template>
<button @click="showit" class="sceneControl">场景控制面板</button>
<div
v-if="show"
class="text-left p-10"
style="
color: #ffffff;
background-color: rgb(55 55 55 / 0.9);
border-radius: 2rem;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
top: 10vh;
left: 20vw;
width: 60vw;
height: 80vh;
font-size: 1rem;
"
>
<div
@click="close"
class="bg-gray-600 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
style="width: 2rem; height: 2rem; border-top-right-radius: 1rem; border-bottom-left-radius: 1rem;"
>
<el-icon style="width: 2rem; height: 2rem">
<CloseBold color="#ffffff" />
</el-icon>
</div>
<div class="w-full text-center" style="font-size: 2rem; height: 4rem; line-height: 4rem;">
场景控制面板
</div>
<div class="text-white text-center flex flex-wrap justify-center items-center" style="height: 80%; font-size: 1rem;">
<div class="option">
<label>显示/隐藏地球:</label>
<input
type="checkbox"
v-model="earthShow"
class="w-6 h-6 cursor-pointer"
/>
</div>
<div class="option">
<label>显示/隐藏坐标系:</label>
<input
type="checkbox"
v-model="axesShow"
class="w-6 h-6 cursor-pointer"
/>
</div>
<div class="option">
<label>坐标轴X颜色:</label>
<select v-model="axesXSelected" style="cursor: pointer; color: black">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<label>显示/隐藏坐标轴X:</label>
<input
type="checkbox"
v-model="axesXShow"
class="w-6 h-6 cursor-pointer"
/>
</div>
<div class="option">
<label>坐标轴Y颜色:</label>
<select v-model="axesYSelected" style="cursor: pointer; color: black">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<label>显示/隐藏坐标轴Y:</label>
<input
type="checkbox"
v-model="axesYShow"
class="w-6 h-6 cursor-pointer"
/>
</div>
<div class="option">
<label>坐标轴Z颜色:</label>
<select v-model="axesZSelected" style="cursor: pointer; color: black">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<label>显示/隐藏坐标轴Z:</label>
<input
type="checkbox"
v-model="axesZShow"
class="w-6 h-6 cursor-pointer"
/>
</div>
<div class="option">
<label>赤道线颜色:</label>
<select v-model="equatorLineSelected" style="cursor: pointer; color: black">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<label>显示/隐藏赤道线:</label>
<input
type="checkbox"
v-model="equatorLineShow"
class="w-6 h-6 cursor-pointer"
/>
</div>
<div class="option">
<label>赤道面颜色:</label>
<select v-model="equatorPlaneSelected" style="cursor: pointer; color: black">
<option v-for="(value, key) in cesiumColor" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
<div class="option">
<label>显示/隐藏赤道面:</label>
<input
type="checkbox"
v-model="equatorPlaneShow"
class="w-6 h-6 cursor-pointer"
/>
</div>
<div class="option">
<input
type="button"
class="buttonClass"
id="fly-btn"
@click="resetScene"
value="重置场景"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
const emit = defineEmits(["handleSceneControl"]);
const show = ref(false);
const earthShow = ref(true);
const axesXSelected = ref("LIME");
const axesXShow = ref(true);
const axesYSelected = ref("PURPLE");
const axesYShow = ref(true);
const axesZSelected = ref("RED");
const axesZShow = ref(true);
const axesShow = ref(true);
const equatorLineSelected = ref("CYAN");
const equatorLineShow = ref(true);
const equatorPlaneSelected = ref("YELLOW");
const equatorPlaneShow = ref(true);
const cesiumColor = {
BLUE: "蓝色",
PURPLE: "紫色",
RED: "红色",
YELLOW: "黄色",
CYAN: "青色",
ORANGE: "橙色",
PINK: "粉色",
GREEN: "绿色",
WHITE: "白色",
LIGHTBLUE: "浅蓝色",
LIME: "青柠色",
MAGENTA: "品红色",
};
const showit = () => {
show.value = true;
};
const close = () => {
show.value = false;
};
const resetScene = () => {
earthShow.value = true;
axesXSelected.value = "LIME";
axesXShow.value = true;
axesYSelected.value = "PURPLE";
axesYShow.value = true;
axesZSelected.value = "RED";
axesZShow.value = true;
axesShow.value = true;
equatorLineSelected.value = "CYAN";
equatorLineShow.value = true;
equatorPlaneSelected.value = "YELLOW";
equatorPlaneShow.value = true;
};
watch(earthShow, (value) => {
emit("handleSceneControl", "earthShow", value);
});
watch(axesXSelected, (value) => {
emit("handleSceneControl", "axesXSelected", value);
});
watch(axesXShow, (value) => {
emit("handleSceneControl", "axesXShow", value);
});
watch(axesYSelected, (value) => {
emit("handleSceneControl", "axesYSelected", value);
});
watch(axesYShow, (value) => {
emit("handleSceneControl", "axesYShow", value);
});
watch(axesZSelected, (value) => {
emit("handleSceneControl", "axesZSelected", value);
});
watch(axesZShow, (value) => {
emit("handleSceneControl", "axesZShow", value);
});
watch(axesShow, (value) => {
emit("handleSceneControl", "axesShow", value);
});
watch(equatorLineSelected, (value) => {
emit("handleSceneControl", "equatorLineSelected", value);
});
watch(equatorLineShow, (value) => {
emit("handleSceneControl", "equatorLineShow", value);
});
watch(equatorPlaneSelected, (value) => {
emit("handleSceneControl", "equatorPlaneSelected", value);
});
watch(equatorPlaneShow, (value) => {
emit("handleSceneControl", "equatorPlaneShow", value);
});
</script>
<style scoped>
.sceneControl {
font-size: 1rem;
color: #ffffff;
background-color: rgb(255 255 255 / 0.5);
width: 8rem;
height: 2rem;
line-height: 2rem;
overflow: hidden;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 0.5);
position: fixed;
bottom: 22rem;
right: 0;
border-radius: 1rem;
}
.sceneControl:hover {
color: #000000;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
background-color: rgb(250, 230, 164);
}
.option {
width: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.buttonClass {
font-size: 1rem;
cursor: pointer;
width: 6rem;
height: 2rem;
line-height: 2rem;
}
</style>

View File

@@ -0,0 +1,283 @@
<template>
<div class="bg-white" style="width: 30vw; height: 44vh; z-index: 99; border-radius: 1rem;">
<div
class="text-3xl absolute bottom-2 right-2 cursor-pointer flex justify-center items-center"
style="width: 2rem; height: 6rem;"
>
<div
class="bg-gray-600"
style="width: 2rem; height: 2rem; border-top-left-radius: 50%; border-top-right-radius: 50%;"
>
<div
class="bg-gray-600 hover:bg-red-600"
style="width: 2rem; height: 2rem; border-radius: 50%;"
>
<el-icon @click="plus">
<Plus color="#ffffff" />
</el-icon>
</div>
</div>
<div class="bg-gray-600" style="width: 2rem; height: 2rem;">
<div
class="hover:bg-red-600"
style="width: 2rem; height: 2rem; border-radius: 50%;"
>
<el-icon @click="refresh">
<RefreshRight color="#ffffff" />
</el-icon>
</div>
</div>
<div
class="bg-gray-600"
style="width: 2rem; height: 2rem; border-bottom-left-radius: 50%; border-bottom-right-radius: 50%;"
>
<div
class="bg-gray-600 hover:bg-red-600"
style="width: 2rem; height: 2rem; border-radius: 50%;"
>
<el-icon @click="minus">
<Minus color="#ffffff" />
</el-icon>
</div>
</div>
</div>
<div
id="threeCanvas"
ref="canvasContainer"
class="canvasContainer"
>
<select v-model="selected" style="border-radius: 1rem; font-size: 1rem; width: 50%; height: 2rem; cursor: pointer; color: black; position: absolute; top: 10%; left: 25%;">
<option v-for="(value, key) in modelName" :key="key" :value="key">
{{ value }}
</option>
</select>
<div
@click="closeModel"
class="bg-gray-600 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
style="width: 2rem; height: 2rem; position: absolute; top: 0; right: 0; border-top-right-radius: 1rem; border-bottom-left-radius: 1rem;"
>
<el-icon style="width: 2rem; height: 2rem;">
<CloseBold color="#ffffff" />
</el-icon>
</div>
<input
type="button"
class="buttonClass"
@click="confirmModel"
value="确认"
/>
</div>
</div>
</template>
<script setup>
import { ref, watch, onMounted } from "vue";
import { CloseBold, Plus, RefreshRight, Minus } from "@element-plus/icons-vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import sky from "../../assets/sky.jpg";
const selected = ref("BeidouGEO");
const modelName = {
BeidouGEO: "北斗 GEO卫星",
BeidouIGSO: "北斗 IGSO卫星",
BeidouMEO: "北斗 MEO卫星",
"GPSII-F": "GPS II-F卫星",
"GPSII-R": "GPS II-R卫星",
"GPSIII-A": "GPS III-A卫星",
"GPSIIR-M": "GPS IIR-M卫星",
"GLONASSGLO-K": "格洛纳斯 GLO-K卫星",
"GLONASSGLO-M": "格洛纳斯 GLO-M卫星",
GalileoMEO: "伽利略 MEO卫星"
};
const satelliteModelType = ref("BeidouGEO");
const canvasContainer = ref(null);
const model = ref(null);
let scene, camera, renderer, clonedModel, controls;
// 初始化场景
const initScene = () => {
const threeCanvas = document.getElementById("threeCanvas");
// 创建场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0x666666);
// 创建相机(透视相机)
camera = new THREE.PerspectiveCamera(
75,
threeCanvas.clientWidth / threeCanvas.clientHeight,
0.1,
1000
);
camera.position.z = 5;
// 创建渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(threeCanvas.clientWidth, threeCanvas.clientHeight);
// 挂载到 DOM
canvasContainer.value.appendChild(renderer.domElement);
//创建鼠标控制器
controls = new OrbitControls(camera, renderer.domElement);
// 添加灯光
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1);
scene.add(light);
const light1 = new THREE.DirectionalLight(0xffffff, 1);
light1.position.set(-1, 1, 1);
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffffff, 1);
light2.position.set(1, -1, 1);
scene.add(light2);
const light3 = new THREE.DirectionalLight(0xffffff, 1);
light3.position.set(1, 1, -1);
scene.add(light3);
const light4 = new THREE.DirectionalLight(0xffffff, 1);
light4.position.set(-1, -1, 1);
scene.add(light4);
const light5 = new THREE.DirectionalLight(0xffffff, 1);
light5.position.set(1, -1, -1);
scene.add(light5);
const light6 = new THREE.DirectionalLight(0xffffff, 1);
light6.position.set(-1, 1, -1);
scene.add(light6);
const light7 = new THREE.DirectionalLight(0xffffff, 1);
light7.position.set(-1, -1, -1);
scene.add(light7);
// 创建半球体几何体
const skyGeometry = new THREE.SphereGeometry(66, 66, 66); // 半径、宽度分段、高度分段
// 加载天空纹理
const skyTextureLoader = new THREE.TextureLoader();
const skyTexture = skyTextureLoader.load(sky);
// 创建材质,并应用天空纹理
const skyMaterial = new THREE.MeshBasicMaterial({
map: skyTexture,
side: THREE.BackSide,
}); // 使用背面显示纹理
// 创建天空穹顶网格
const skyDome = new THREE.Mesh(skyGeometry, skyMaterial);
// 将天空穹顶添加到场景中
scene.add(skyDome);
};
// 动画循环
const animate = () => {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
};
// 初始球体半径
// r1 = Math.sqrt(x * x + y * y + z * z)
// 相机移动后的球体半径为 r2 ,假设相机向球心移动距离为 1 单位长度
// r1 - r2 = 1
// 调整相机移动距离 d 以获取相机移动后的球体半径 r2
// r2 = r1 - d
// 坐标值与球体半径比例是固定的,初始坐标值为 x1,相机移动后的坐标值为 x2
// x1 / r1 = x2 / r2
// x2 = x1 / r1 * r2
const plus = () => {
const moveValue = 1;
const r1 = Math.sqrt(camera.position.x * camera.position.x + camera.position.y * camera.position.y + camera.position.z * camera.position.z);
const r2 = r1 - moveValue;
const x = (camera.position.x / r1) * r2;
const y = (camera.position.y / r1) * r2;
const z = (camera.position.z / r1) * r2;
camera.position.set(x, y, z);
};
const refresh = () => {
camera.position.set(0, 0, 5);
};
const minus = () => {
const moveValue = 1;
const r1 = Math.sqrt(camera.position.x * camera.position.x + camera.position.y * camera.position.y + camera.position.z * camera.position.z);
const r2 = r1 + moveValue;
const x = (camera.position.x / r1) * r2;
const y = (camera.position.y / r1) * r2;
const z = (camera.position.z / r1) * r2;
camera.position.set(x, y, z);
};
const emit = defineEmits(["selectModel", "closeModel", "changeModel"]);
const confirmModel = () => {
emit("selectModel", satelliteModelType.value);
emit("closeModel");
};
const closeModel = () => {
emit("closeModel");
};
defineExpose({ selected, model });
watch(selected, (value) => {
satelliteModelType.value = value;
emit("changeModel", value);
setTimeout(() => {
scene.remove(clonedModel);
clonedModel = model.value.model.clone();
scene.add(clonedModel);
}, 0);
});
onMounted(() => {
initScene();
setTimeout(() => {
clonedModel = model.value.model.clone();
scene.add(clonedModel);
}, 0);
animate();
});
</script>
<style scoped>
.canvasContainer {
width: 30vw;
height: 44vh;
z-index: 99;
margin: 0;
overflow: hidden;
position: relative;
border-radius: 1rem;
}
.buttonClass {
font-size: 1rem;
width: 20%;
height: 2rem;
line-height: 2rem;
color: #000000;
background-color: rgb(165 243 252 / 1);
overflow: hidden;
cursor: pointer;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: absolute;
left: 40%;
bottom: 10%;
border-radius: 1rem;
}
.buttonClass:hover {
background-color: rgb(250, 230, 164);
color: #41ddee;
}
</style>

View File

@@ -1,42 +0,0 @@
<template>
<vc-viewer class="vc-viewer" :animation="true" :showCredit="false" :shouldAnimate="true" :sceneModePicker="true" @ready="onViewerReady">
<vc-layer-imagery>
<vc-provider-imagery-singletile :url="earth" />
</vc-layer-imagery>
<vc-navigation :offset="[10, 40]" :position="'top-left'" :printOpts="false" :locationOpts="false" :otherOpts="false" />
<SatelliteSystemView />
</vc-viewer>
</template>
<script lang="ts" setup>
import earth from '../assets/earth.jpg'
import SatelliteSystemView from './SatelliteSystemView.vue';
// 初始化视图参数
const onViewerReady = ({ viewer }) => {
viewer.scene.debugShowFramesPerSecond = false
// 限定缩放大小
viewer.scene.screenSpaceCameraController.minimumZoomDistance = 10000000 // 相机高度的最小值设定为 10000000 米
// 修改infobox的样式
const infoBox = viewer.infoBox.frame;
infoBox.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-popups allow-forms')
infoBox.setAttribute('src', '') //必须设置src为空 否则不会生效
infoBox.addEventListener('load', function () {
const infoBoxDescriptionElement = infoBox.contentWindow.document.getElementsByClassName('cesium-infoBox-description')[0]
infoBoxDescriptionElement.style.fontSize = 'larger'
infoBoxDescriptionElement.style.paddingLeft = '20px'
infoBoxDescriptionElement.style.paddingRight = '20px'
})
}
</script>
<style>
/* 隐藏动画仪表盘中的日期和时间 */
.vc-viewer .cesium-viewer .cesium-viewer-animationContainer svg > g > g text:not(:last-of-type) {
display: none;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,23 @@
<template>
<div v-show="!!src">
<img :src="src">
<img :src="src" @dblclick="close">
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore()
const src = computed(() => './image/flag/' + store.getters["satelliteSystem/name"] + '.png')
const close = () => {
window.ipcRenderer.send('CLOSE')
}
</script>
<style scoped>
img {
width: 169px;
width: 10rem;
}
</style>

View File

@@ -0,0 +1,336 @@
<template>
<button id="step21" @click="showit" class="quiz"> 知识问答 </button>
<div
id="step22"
v-if="show"
class="text-center p-10"
style="
color: #ffffff;
background-color: rgb(0 0 0 / 0.6);
border-radius: 2rem;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
top: 10vh;
left: 20vw;
width: 60vw;
height: 80vh;
font-size: 2rem;
"
>
<div
@click="close"
class="bg-gray-600 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
style="width: 2rem; height: 2rem; border-top-right-radius: 1rem; border-bottom-left-radius: 1rem;"
>
<el-icon style="width: 2rem; height: 2rem;">
<CloseBold color="#ffffff" />
</el-icon>
</div>
<div style="font-weight: bold; color: #ffffff; font-size: 2rem; text-align: left;"> 题目:{{ topic.stem }} </div>
<el-radio-group v-model="radio">
<div class="w-full">
<el-radio id="topicA" value="a" class="align-left" style="margin-right: 0px; margin-bottom: 2rem;color: #ffffff;">
<div @click="radio = 'a'" :style="!checked ? radio == 'a' ? 'font-size: 2rem; background: radial-gradient(circle, rgba(60, 133, 211, 0.9), rgba(60, 133, 211, 0.1));' : 'font-size: 2rem;' : topic.answer == 'A' ? 'font-size: 2rem; background: radial-gradient(circle, rgba(65, 184, 131, 0.9), rgba(65, 184, 131, 0.1));' : 'font-size: 2rem; background: radial-gradient(circle, rgba(204, 62, 62, 0.9), rgba(204, 62, 62, 0.1));'">
<label>A:{{ topic.A }}</label>
<label style="color: rgba(0, 0, 0, 0)">{{ labelA }}</label>
</div>
</el-radio>
</div>
<div class="w-full">
<el-radio id="topicB" value="b" class="align-left" style="margin-right: 0px; margin-bottom: 2rem; color: #ffffff;">
<div @click="radio = 'b'" :style="!checked ? radio == 'b' ? 'font-size: 2rem; background: radial-gradient(circle, rgba(60, 133, 211, 0.9), rgba(60, 133, 211, 0.1));' : 'font-size: 2rem;' : topic.answer == 'B' ? 'font-size: 2rem; background: radial-gradient(circle, rgba(65, 184, 131, 0.9), rgba(65, 184, 131, 0.1));' : 'font-size: 2rem; background: radial-gradient(circle, rgba(204, 62, 62, 0.9), rgba(204, 62, 62, 0.1));'">
<label>B:{{ topic.B }}</label>
<label style="color: rgba(0, 0, 0, 0)">{{ labelB }}</label>
</div>
</el-radio>
</div>
<div class="w-full">
<el-radio id="topicC" value="c" class="align-left" style="margin-right: 0px; margin-bottom: 2rem; color: #ffffff;">
<div @click="radio = 'c'" :style="!checked ? radio == 'c' ? 'font-size: 2rem; background: radial-gradient(circle, rgba(60, 133, 211, 0.9), rgba(60, 133, 211, 0.1));' : 'font-size: 2rem;' : topic.answer == 'C' ? 'font-size: 2rem; background: radial-gradient(circle, rgba(65, 184, 131, 0.9), rgba(65, 184, 131, 0.1));' : 'font-size: 2rem; background: radial-gradient(circle, rgba(204, 62, 62, 0.9), rgba(204, 62, 62, 0.1));'">
<label>C:{{ topic.C }}</label>
<label style="color: rgba(0, 0, 0, 0)">{{ labelC }}</label>
</div>
</el-radio>
</div>
</el-radio-group>
<br />
<button type="text" :disabled="radio == ''" style="font-size: 2rem; background: radial-gradient(circle, rgba(60, 133, 211, 0.9), rgba(60, 133, 211, 0.1));" @click="showAnswer"> 揭晓答案 </button>
<br />
<el-text v-show="checked" style="font-weight: bold; color: #ffffff; font-size: 2rem; background: radial-gradient(circle, rgba(65, 184, 131, 0.9), rgba(65, 184, 131, 0.1));">
正确答案:{{ topic.answer }}
</el-text>
<br />
<el-text v-show="checked" :style="radio.toUpperCase() == topic.answer ? 'font-weight: bold; color: #ffffff; font-size: 2rem; background: radial-gradient(circle, rgba(223, 93, 126, 0.9), rgba(223, 93, 126, 0.1));' : 'font-weight: bold; color: #ffffff; font-size: 2rem; background: radial-gradient(circle, rgba(217, 203, 48, 0.9), rgba(217, 203, 48, 0.1));'">
{{ radio.toUpperCase() == topic.answer ? "恭喜您,答对了!" : "答错了,请再接再厉!" }}
</el-text>
<br />
<button v-show="checked" type="text" style="font-size: 2rem; background: radial-gradient(circle, rgba(60, 133, 211, 0.9), rgba(60, 133, 211, 0.1));" @click="nextTopic"> 下一题 </button>
</div>
</template>
<script setup>
import { ref, reactive, watch } from "vue";
import { useStore } from "vuex";
import { CloseBold } from "@element-plus/icons-vue";
const store = useStore();
const satellites = store.state;
const allowMakeTopic = ref(false);
const show = ref(false);
const showFrist = ref(true);
const topic = reactive({
stem: "",
A: "",
B: "",
C: "",
answer: "",
});
const radio = ref("");
const checked = ref(false);
const labelA = ref("");
const labelB = ref("");
const labelC = ref("");
const getRandomDate = () => {
// 将日期转换为时间戳
const startDate = new Date("1997-01-01").getTime();
const endDate = new Date(new Date().toISOString().split('T')[0]).getTime();
// 生成一个随机时间戳
const randomTime = Math.random() * (endDate - startDate) + startDate;
// 将时间戳转换回日期对象
const randomDate = new Date(randomTime);
// 格式化日期为YYYY-MM-DD
const year = randomDate.getFullYear();
const month = String(randomDate.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要加1
const day = String(randomDate.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
const adder = (num) => {
let placeholder = "";
for (let index = 0; index < num; index++) {
placeholder = placeholder + "0";
}
return placeholder;
};
const makeTopic = () => {
const satelliteSystem = {
beidou: "中国北斗卫星导航",
tiantong: "天通卫星系统",
"gps-ops": "美国全球定位",
"glo-ops": "俄罗斯格洛纳斯卫星导航",
galileo: "欧盟研制伽利略卫星导航",
};
let x = Math.floor(Math.random() * satellites.satelliteSystem.satellites.length),
y = satellites.satelliteSystem.satellites[x].description.split("<p>")[2].slice(4, -4),
z = satelliteSystem[satellites.satelliteSystem.name],
topicId,
topics = [
{
topic: `NORAD ID 是指由北美防空司令部(North American Aerospace Defense Command,缩写为 NORAD) 为地球轨道上的所有人造物体分配的唯一识别编号,用于全球范围内的追踪、监测和管理。请问,PRN为${y}的${z}系统卫星的NORAD ID是多少?`,
makeAnswer: () => {
const correctAnswer = satellites.satelliteSystem.satellites[x].description.split("<p>")[1].slice(9, -4);
const uniqueNumbers = [];
while (uniqueNumbers.length < 3) {
let num = Math.floor(Math.random() * 90000) + 10000;
if (!uniqueNumbers.includes(num) && num !== correctAnswer) {
uniqueNumbers.push(num);
}
}
topic.A = uniqueNumbers[0];
topic.B = uniqueNumbers[1];
topic.C = uniqueNumbers[2];
const answerId = ["A", "B", "C"];
const answer = answerId[Math.floor(Math.random() * 3)];
topic.answer = answer;
topic[answer] = correctAnswer;
},
},
{
topic: `PRN为${y}的${z}系统卫星的发射日期是:`,
// 发射日期:2018-03-29 /1997
makeAnswer: () => {
const correctAnswer = satellites.satelliteSystem.satellites[x].description.split("<p>")[4].slice(5, -4);
const uniqueNumbers = [];
while (uniqueNumbers.length < 3) {
let num = getRandomDate();
if (!uniqueNumbers.includes(num) && num !== correctAnswer) {
uniqueNumbers.push(num);
}
}
topic.A = uniqueNumbers[0];
topic.B = uniqueNumbers[1];
topic.C = uniqueNumbers[2];
const answerId = ["A", "B", "C"];
const answer = answerId[Math.floor(Math.random() * 3)];
topic.answer = answer;
topic[answer] = correctAnswer;
},
},
{
topic: `PRN为${y}的${z}系统卫星的运行周期是:`,
makeAnswer: () => {
const correctAnswer = satellites.satelliteSystem.satellites[x].description.split("<p>")[5].slice(9, -4);
const uniqueNumbers = [];
while (uniqueNumbers.length < 3) {
let num = ((Math.random() * 1350) + 90).toFixed(2);
if (!uniqueNumbers.includes(num) && num !== correctAnswer) {
uniqueNumbers.push(num);
}
}
topic.A = uniqueNumbers[0];
topic.B = uniqueNumbers[1];
topic.C = uniqueNumbers[2];
const answerId = ["A", "B", "C"];
const answer = answerId[Math.floor(Math.random() * 3)];
topic.answer = answer;
topic[answer] = correctAnswer;
},
},
{
topic: `PRN为${y}的${z}系统卫星的轨道面倾角是多少度:`,
makeAnswer: () => {
const correctAnswer = satellites.satelliteSystem.satellites[x].description.split("<p>")[6].slice(9, -4);
const uniqueNumbers = [];
while (uniqueNumbers.length < 3) {
let num = ((Math.random() * 90)).toFixed(2);
if (!uniqueNumbers.includes(num) && num !== correctAnswer) {
uniqueNumbers.push(num);
}
}
topic.A = uniqueNumbers[0];
topic.B = uniqueNumbers[1];
topic.C = uniqueNumbers[2];
const answerId = ["A", "B", "C"];
const answer = answerId[Math.floor(Math.random() * 3)];
topic.answer = answer;
topic[answer] = correctAnswer;
},
},
{
topic: `PRN为${y}的${z}系统卫星的远地点高度是多少公里:`,
makeAnswer: () => {
const correctAnswer = satellites.satelliteSystem.satellites[x].description.split("<p>")[7].slice(10, -4);
const uniqueNumbers = [];
while (uniqueNumbers.length < 3) {
let num = Math.floor(Math.random() * 34586) + 1200;
if (!uniqueNumbers.includes(num) && num !== correctAnswer) {
uniqueNumbers.push(num);
}
}
topic.A = uniqueNumbers[0];
topic.B = uniqueNumbers[1];
topic.C = uniqueNumbers[2];
const answerId = ["A", "B", "C"];
const answer = answerId[Math.floor(Math.random() * 3)];
topic.answer = answer;
topic[answer] = correctAnswer;
},
},
{
topic: `PRN为${y}的${z}系统卫星的近地点高度是多少公里:`,
makeAnswer: () => {
const correctAnswer = satellites.satelliteSystem.satellites[x].description.split("<p>")[8].slice(10, -4);
const uniqueNumbers = [];
while (uniqueNumbers.length < 3) {
let num = Math.floor(Math.random() * 34586) + 1200;
if (!uniqueNumbers.includes(num) && num !== correctAnswer) {
uniqueNumbers.push(num);
}
}
topic.A = uniqueNumbers[0];
topic.B = uniqueNumbers[1];
topic.C = uniqueNumbers[2];
const answerId = ["A", "B", "C"];
const answer = answerId[Math.floor(Math.random() * 3)];
topic.answer = answer;
topic[answer] = correctAnswer;
},
},
];
topicId = Math.floor(Math.random() * topics.length);
topic.stem = topics[topicId].topic;
topics[topicId].makeAnswer();
topic.A = String(topic.A);
topic.B = String(topic.B);
topic.C = String(topic.C);
labelA.value = ""
labelB.value = ""
labelC.value = ""
if (topic.A.length > topic.B.length && topic.A.length > topic.C.length) {
labelB.value = adder(topic.A.length - topic.B.length);
labelC.value = adder(topic.A.length - topic.C.length);
} else if (topic.B.length > topic.A.length && topic.B.length > topic.C.length) {
labelA.value = adder(topic.B.length - topic.A.length);
labelC.value = adder(topic.B.length - topic.C.length);
} else if (topic.C.length > topic.A.length && topic.C.length > topic.B.length) {
labelA.value = adder(topic.C.length - topic.A.length);
labelB.value = adder(topic.C.length - topic.B.length);
} else if (topic.A.length < topic.B.length && topic.B.length == topic.C.length) {
labelA.value = adder(topic.B.length - topic.A.length);
} else if (topic.B.length < topic.A.length && topic.A.length == topic.C.length) {
labelB.value = adder(topic.A.length - topic.B.length);
} else if (topic.C.length < topic.A.length && topic.A.length == topic.B.length) {
labelC.value = adder(topic.A.length - topic.C.length);
}
};
const showit = () => {
if (showFrist.value) {
showFrist.value = false;
makeTopic();
}
show.value = true;
};
const showAnswer = () => {
checked.value = true;
};
const nextTopic = () => {
makeTopic();
checked.value = false;
radio.value = '';
};
const close = () => {
show.value = false;
checked.value = false;
radio.value = '';
};
defineExpose({ show });
watch(satellites, () => {
if (allowMakeTopic.value) {
nextTopic();
}
allowMakeTopic.value = true;
});
</script>
<style scoped>
.quiz {
font-size: 1rem;
color: rgba(255, 255, 255, 0);
background-color: #ffffff;
width: 1rem;
height: 2rem;
line-height: 2rem;
overflow: hidden;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
bottom: 20rem;
right: 0;
border-top-left-radius: 1rem;
border-bottom-left-radius: 1rem;
}
.quiz:hover {
width: 6rem;
color: #000000;
background-color: rgb(165 243 252 / 1);
}
</style>

View File

@@ -0,0 +1,59 @@
<template>
<div id="step20">
<select class="cesium-button" v-model="selected">
<option v-for="(value, key) in option_dic" :key="key" :value="key">
{{ value }}
</option>
</select>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, onMounted } from "vue";
import { useStore } from "vuex";
const store = useStore();
const selected = ref("");
const firstLoad = ref(true);
const emit = defineEmits(["handleSatelliteSystemSelect", "handleElementsFly"]);
defineExpose({ selected });
watch(selected, (value) => {
if (value !== "custom") {
store.dispatch("satelliteSystem/setName", { name: value });
}
window.allowUpdate = true;
emit("handleSatelliteSystemSelect", value);
if (!firstLoad.value) {
let viewName = window.document.getElementsByClassName("cesium-toolbar-button")[1]
.classList[2];
if (viewName == "cesium-sceneModePicker-button2D") {
window.document.getElementsByClassName("cesium-toolbar-button")[4].click();
setTimeout(() => {
window.document.getElementsByClassName("cesium-toolbar-button")[3].click();
emit("handleElementsFly");
}, 0);
}
}
});
onMounted(
() => (
(selected.value = Object.keys(option_dic)[0]),
setTimeout(() => {
firstLoad.value = false;
}, 0)
)
);
const option_dic = {
beidou: "中国北斗卫星导航系统",
tiantong: "天通卫星系统",
"gps-ops": "美国全球定位系统",
"glo-ops": "俄罗斯格洛纳斯卫星导航系统",
galileo: "欧盟研制伽利略卫星导航系统",
custom: "自定义卫星导航系统",
};
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,183 @@
<template>
<div>
<div id="satellite-table-container" class="text-white absolute bottom-0 text-center">
<SvgButton id="step9" @click="show = !show" />
<table ref="satelliteTable" v-show="show" class="text-center">
<thead class="text-2xl">
<th
class="py-3 bg-blue-900 border border-gray-900"
v-for="(label, index) in ['序号', '卫星名称', '显示/隐藏', '跟踪']"
:key="label"
:style="{ width: index == 1 ? '37.5vw' : '12.5vw' }"
>
<div style="display: flex; justify-content: center; align-items: center;">
<div style="margin-right: 0.5rem;">
{{ label }}
</div>
<input
id="step10"
v-if="label == '显示/隐藏'"
:indeterminate="someShow"
:checked="allShow"
type="checkbox"
class="w-6 h-6 cursor-pointer"
@change="allOnShowStateCheckboxChanged"
/>
</div>
</th>
</thead>
<tbody class="text-lg odd:bg-gray-50">
<tr v-for="({ id, show }, index) in satellites" :key="id">
<td class="border border-gray-900"> {{ index + 1 }} </td>
<td id="satelliteId" class="border border-gray-900">{{ id }}</td>
<td class="border border-gray-900">
<div style="display: flex; justify-content: center; align-items: center;">
<input
id="step11"
:checked="ashow"
type="checkbox"
class="w-6 h-6 cursor-pointer"
@change="onShowStateCheckboxChanged(id)"
/>
</div>
</td>
<td class="border border-gray-900">
<button id="step12" @click="onTrackButtonClicked(id)">
<TrackSvg />
</button>
</td>
</tr>
</tbody>
</table>
<div class="text-white bg-blue-900"> {{ softwareName }} </div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from "vue";
import { useStore } from "vuex";
import SvgButton from "../SvgButton.vue";
import TrackSvg from "../TrackSvg.vue";
const show = ref(false);
const ashow = ref(true);
const someShow = ref(false);
const allShow = ref(true);
const showarr = ref({});
const softwareName = ref("卫星科普互动展示系统V1.0");
const store = useStore();
const satellites = computed(() => store.getters["satelliteSystem/satellites"]);
const onTrackButtonClicked = (id) => {
store.dispatch("satelliteSystem/track", id);
};
const onShowStateCheckboxChanged = (id) => {
window.allowUpdate = true;
window.toallowUpdate = true;
someShow.value = true;
showarr.value[id] = !showarr.value[id];
store.dispatch("satelliteSystem/toggleShow", id);
let ashowsum = 0;
satellites.value.forEach((item, index) => {
if (showarr.value[item.id] == true) {
ashowsum = ashowsum + 1;
}
});
if (ashowsum == 0) {
someShow.value = false;
} else if (ashowsum == satellites.value.length) {
someShow.value = false;
}
};
const allOnShowStateCheckboxChanged = () => {
if (allShow.value) {
allShow.value = false;
ashow.value = false;
} else {
allShow.value = true;
ashow.value = true;
}
satellites.value.forEach((element) => {
if (showarr.value[element.id] !== ashow.value) {
onShowStateCheckboxChanged(element.id);
}
onShowStateCheckboxChanged(element.id);
});
someShow.value = false;
};
// 表格数据更新时,滚动条回到最顶部
const satelliteTable = ref(null);
watch(satellites, () => {
ashow.value = true;
someShow.value = false;
allShow.value = true;
showarr.value = {};
satellites.value.forEach((item) => {
showarr.value[item.id] = false;
});
satelliteTable.value.scrollTop = 0;
});
const changeName = (newName) => {
if (newName !== undefined) {
softwareName.value = newName;
}
};
defineExpose({ show, onTrackButtonClicked });
onMounted(() => {
if ("ipcRenderer" in window) {
ipcRenderer.receive("softwareName", changeName);
ipcRenderer.send("APP_MOUNTED");
}
});
</script>
<style scoped>
#satellite-table-container {
left: 12.5vw;
right: 12.5vw;
}
table {
display: block;
height: 18rem;
overflow: auto;
}
tr:nth-child(odd) {
background-color: rgb(17, 24, 39);
}
tr:nth-child(even) {
background-color: rgb(31, 41, 55);
}
td {
padding: 0.25rem 0;
}
::-webkit-scrollbar {
width: 0.6rem;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgb(0 0 0 / 30%);
background-color: #4fbee2;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgb(0 0 0 / 30%);
background-color: #111827;
}
</style>

View File

@@ -0,0 +1,221 @@
<template>
<div id="step15" class="bg-white" style="width: 30vw; height: 44vh; z-index: 99;">
<div
class="text-3xl absolute bottom-2 right-2 cursor-pointer"
style="width: 2rem; height: 6rem"
>
<div
class="bg-gray-600"
style="
width: 2rem;
height: 2rem;
border-top-left-radius: 50%;
border-top-right-radius: 50%;
"
>
<div
id="step16"
class="bg-gray-600 hover:bg-red-600"
style="width: 2rem; height: 2rem; border-radius: 50%"
>
<el-icon @click="plus">
<Plus color="#ffffff" />
</el-icon>
</div>
</div>
<div class="bg-gray-600" style="width: 2rem; height: 2rem">
<div
id="step17"
class="hover:bg-red-600"
style="width: 2rem; height: 2rem; border-radius: 50%"
>
<el-icon @click="refresh">
<RefreshRight color="#ffffff" />
</el-icon>
</div>
</div>
<div
class="bg-gray-600"
style="
width: 2rem;
height: 2rem;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;
"
>
<div
id="step18"
class="bg-gray-600 hover:bg-red-600"
style="width: 2rem; height: 2rem; border-radius: 50%;"
>
<el-icon @click="minus">
<Minus color="#ffffff" />
</el-icon>
</div>
</div>
</div>
<div
@click="closeModel"
class="bg-gray-600 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
style="width: 2rem; height: 2rem"
>
<el-icon style="width: 2rem; height: 2rem">
<CloseBold color="#ffffff" />
</el-icon>
</div>
<div
id="threeCanvas"
ref="canvasContainer"
style="width: 30vw; height: 44vh; z-index: 99; margin: 0; overflow: hidden;"
></div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { CloseBold, Plus, RefreshRight, Minus } from "@element-plus/icons-vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import sky from "../../assets/sky.jpg";
const canvasContainer = ref(null);
const model = ref(null);
let scene, camera, renderer, clonedModel, controls;
// 初始化场景
const initScene = () => {
const threeCanvas = document.getElementById("threeCanvas");
// 创建场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0x666666);
// 创建相机(透视相机)
camera = new THREE.PerspectiveCamera(75, threeCanvas.clientWidth / threeCanvas.clientHeight, 0.1, 1000);
camera.position.z = 5;
// 创建渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(threeCanvas.clientWidth, threeCanvas.clientHeight);
// 挂载到 DOM
canvasContainer.value.appendChild(renderer.domElement);
//创建鼠标控制器
controls = new OrbitControls(camera, renderer.domElement);
// 添加灯光
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1);
scene.add(light);
const light1 = new THREE.DirectionalLight(0xffffff, 1);
light1.position.set(-1, 1, 1);
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffffff, 1);
light2.position.set(1, -1, 1);
scene.add(light2);
const light3 = new THREE.DirectionalLight(0xffffff, 1);
light3.position.set(1, 1, -1);
scene.add(light3);
const light4 = new THREE.DirectionalLight(0xffffff, 1);
light4.position.set(-1, -1, 1);
scene.add(light4);
const light5 = new THREE.DirectionalLight(0xffffff, 1);
light5.position.set(1, -1, -1);
scene.add(light5);
const light6 = new THREE.DirectionalLight(0xffffff, 1);
light6.position.set(-1, 1, -1);
scene.add(light6);
const light7 = new THREE.DirectionalLight(0xffffff, 1);
light7.position.set(-1, -1, -1);
scene.add(light7);
// 创建半球体几何体
const skyGeometry = new THREE.SphereGeometry(66, 66, 66); // 半径、宽度分段、高度分段
// 加载天空纹理
const skyTextureLoader = new THREE.TextureLoader();
const skyTexture = skyTextureLoader.load(sky);
// 创建材质,并应用天空纹理
const skyMaterial = new THREE.MeshBasicMaterial({
map: skyTexture,
side: THREE.BackSide,
}); // 使用背面显示纹理
// 创建天空穹顶网格
const skyDome = new THREE.Mesh(skyGeometry, skyMaterial);
// 将天空穹顶添加到场景中
scene.add(skyDome);
};
const loadModel = () => {
setTimeout(() => {
if (clonedModel !== undefined) {
scene.remove(clonedModel);
}
clonedModel = model.value.model.clone();
scene.add(clonedModel);
}, 0);
};
// 动画循环
const animate = () => {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
};
// 初始球体半径
// r1 = Math.sqrt(x * x + y * y + z * z)
// 相机移动后的球体半径为 r2 ,假设相机向球心移动距离为 1 单位长度
// r1 - r2 = 1
// 调整相机移动距离 d 以获取相机移动后的球体半径 r2
// r2 = r1 - d
// 坐标值与球体半径比例是固定的,初始坐标值为 x1,相机移动后的坐标值为 x2
// x1 / r1 = x2 / r2
// x2 = x1 / r1 * r2
const plus = () => {
const moveValue = 1;
const r1 = Math.sqrt(camera.position.x * camera.position.x + camera.position.y * camera.position.y + camera.position.z * camera.position.z);
const r2 = r1 - moveValue;
const x = (camera.position.x / r1) * r2;
const y = (camera.position.y / r1) * r2;
const z = (camera.position.z / r1) * r2;
camera.position.set(x, y, z);
};
const refresh = () => {
camera.position.set(0, 0, 5);
};
const minus = () => {
const moveValue = 1;
const r1 = Math.sqrt(camera.position.x * camera.position.x + camera.position.y * camera.position.y + camera.position.z * camera.position.z);
const r2 = r1 + moveValue;
const x = (camera.position.x / r1) * r2;
const y = (camera.position.y / r1) * r2;
const z = (camera.position.z / r1) * r2;
camera.position.set(x, y, z);
};
defineExpose({ model, loadModel });
onMounted(() => {
initScene();
loadModel();
animate();
});
const emit = defineEmits(["showModel"]);
const closeModel = () => {
emit("showModel", false, "", "");
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,387 @@
<template>
<el-dropdown class="text-center felx justify-end items-center">
<button class="tour">
系统导航
<el-icon
style="width: 1rem; height: 1rem; font-size: 1rem"
class="el-icon--right"
><arrow-down
/></el-icon>
</button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="(step, index) in tourSteps"
@click="tour(index)"
>
<el-link
style="width: 8rem; height: 1.8rem; font-weight: bold; font-size: 1rem;"
:underline="false"
type="primary"
>
{{ step.tourName }}
</el-link>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-tour v-for="step in tourSteps" v-model="step.tourOpen">
<template #default>
<el-tour-step
v-for="(stepData, index) in step.tourData"
:target="stepData.target"
:prev-button-props="{
children: '上一步',
onClick: stepData.handlePrevClick,
}"
:next-button-props="{
children: index == step.tourData.length - 1 ? '完成' : '下一步',
onClick: stepData.handleNextClick,
}"
>
<template #header>
<div style="color: blueviolet">{{ stepData.title }}</div>
</template>
<template #default>
<div style="color: blueviolet">{{ stepData.content }}</div>
</template>
</el-tour-step>
</template>
<template #indicators="{ current, total }">
<span>{{ current + 1 }} / {{ total }}</span>
</template>
</el-tour>
</template>
<script setup>
import { ref } from "vue";
import { ArrowDown } from "@element-plus/icons-vue";
const emit = defineEmits(["handleNext"]);
const tourSteps = ref([
{
tourName: "罗盘导航",
tourOpen: false,
tourData: [
{
target: "#step1",
title: "罗盘外部的作用",
content: "用于视图的方向(北、南、东、西)控制",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step2",
title: "罗盘内部的作用",
content: "用于视图的旋转控制",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step3",
title: "放大按钮的作用",
content: "用于放大视图",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step4",
title: "复位按钮的作用",
content: "用于定位和复位视图",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step5",
title: "缩小按钮的作用",
content: "用于缩小视图",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
],
},
{
tourName: "动画控制导航",
tourOpen: false,
tourData: [
{
target: "#animation_pathPointer",
title: "速度调节指针的作用",
content: "用于控制动画速度",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step6",
title: "反转按钮的作用",
content: "用于反转动画",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step7",
title: "暂停按钮的作用",
content: "用于暂停动画",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step8",
title: "播放按钮的作用",
content: "用于播放动画",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
],
},
{
tourName: "卫星列表导航",
tourOpen: false,
tourData: [
{
target: "#step9",
title: "卫星列表开关展示/隐藏按钮的作用",
content: "用于展示或隐藏卫星列表",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step10",
title: "显示/隐藏所有卫星勾选框的作用",
content: "用于显示/隐藏所有卫星的运行轨迹和卫星模型",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step11",
title: "显示/隐藏选中卫星勾选框的作用",
content: "用于显示/隐藏选中卫星的运行轨迹和卫星模型",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
// 触发查看卫星的事件
emit("handleNext", "#step12");
},
},
{
target: "#step12",
title: "查看卫星详情按钮的作用",
content: "用于查看卫星的详细信息并跟踪该卫星的视角",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step13",
title: "卫星详情信息",
content: "卫星的详细信息",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step14",
title: "跟踪/解除卫星视角按钮的作用",
content: "用于跟踪或解除跟随卫星运动的视角,并在左侧窗口中展示卫星的模型",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
],
},
{
tourName: "卫星模型展示导航",
tourOpen: false,
tourData: [
{
target: "#step15",
title: "卫星模型展示区域的作用",
content: "用于展示更为精细的卫星模型",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step16",
title: "放大按钮的作用",
content: "用于放大视图",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step17",
title: "复位按钮的作用",
content: "用于复位视图",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step18",
title: "缩小按钮的作用",
content: "用于缩小视图",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
],
},
{
tourName: "视图与卫星系统切换",
tourOpen: false,
tourData: [
{
target: "#step19",
title: "视图切换按钮的作用",
content: "用于切换地球的3D,2D和哥伦布视图",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
{
target: "#step20",
title: "卫星系统切换按钮的作用",
content: "用于切换不同的卫星系统,包括北斗和其他国际卫星系统",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
],
},
{
tourName: "知识问答导航",
tourOpen: false,
tourData: [
{
target: "#step21",
title: "趣味问答按钮的作用",
content: "用于显示趣味问答区域",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
emit("handleNext", "#step22");
},
},
{
target: "#step22",
title: "趣味问答区域的作用",
content: "用于展示与卫星相关的趣味问答和相关知识",
handlePrevClick: () => {
// console.log("prevEvent");
},
handleNextClick: () => {
// console.log("nextEvent");
},
},
],
},
]);
const tour = (index) => {
emit("handleNext", tourSteps.value[index].tourData[0].target);
setTimeout(() => {
tourSteps.value[index].tourOpen = true;
}, 0);
};
</script>
<style scoped>
.tour {
font-size: 1rem;
color: rgba(255, 255, 255, 0);
background-color: #ffffff;
width: 1rem;
height: 2rem;
line-height: 2rem;
overflow: hidden;
box-shadow: 0 0px 1rem 0px rgb(165 243 252 / 1);
position: fixed;
bottom: 16rem;
right: 0;
border-top-left-radius: 1rem;
border-bottom-left-radius: 1rem;
}
.tour:hover {
width: 10rem;
color: #000000;
background-color: rgb(165 243 252 / 1);
}
</style>

View File

@@ -1,29 +0,0 @@
<template>
<div>
<select class="cesium-button" v-model="selected">
<option
v-for = "(value, key) in option_dic"
:key = "key"
:value = "key">
{{ value }}
</option>
</select>
</div>
</template>
<script lang="ts" setup>
import { useStore } from 'vuex'
import { ref, watch, onMounted } from 'vue'
const store = useStore()
const selected = ref('')
watch(selected, (value) => store.dispatch('satelliteSystem/setName', { name: value }))
onMounted(() => selected.value = Object.keys(option_dic)[0])
const option_dic = {
'beidou' : '中国北斗卫星导航系统',
'gps-ops' : '美国全球定位系统',
'glo-ops' : '俄罗斯格洛纳斯卫星导航系统',
'galileo' : '欧盟研制伽利略卫星导航系统'
}
</script>

View File

@@ -1,45 +0,0 @@
<template>
<vc-datasource-czml v-if="czml" :czml="czml" @ready="onDataSourceReady" />
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
import entity from '../api/entity'
// 卫星轨道数据处理
let vcViewerInstance: any = null
const store = useStore()
const czml = computed(() => './CZML/' + store.getters["satelliteSystem/name"] + '.czml')
const onDataSourceReady = ({ viewer, cesiumObject }) => {
if (!vcViewerInstance) vcViewerInstance = viewer
viewer.flyTo(cesiumObject)
entity.load(cesiumObject.entities.values)
// 显示卫星导航系统的科普信息
viewer.selectedEntity = entity.find('Constellation')
}
// 显示/隐藏、跟踪卫星等交互操作
store.subscribeAction(({ type, payload }) => {
// 显示/隐藏卫星
if (type === 'satelliteSystem/toggleShow') {
const satellite = entity.get(payload)
if (!satellite) return
satellite.show = !satellite.show
}
// 跟踪卫星
if (type === 'satelliteSystem/track') {
const satellite = entity.get(payload)
if (!satellite || !vcViewerInstance) return
vcViewerInstance.selectedEntity = satellite
vcViewerInstance.trackedEntity = satellite
}
})
</script>

View File

@@ -1,99 +0,0 @@
<template>
<div>
<div id="satellite-table-container" class="text-white absolute bottom-0 text-center">
<button @click="show = !show" class="rounded border-blue-900 bg-blue-900">
<svg v-if="show" xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 13l-7 7-7-7m14-8l-7 7-7-7" />
</svg>
<svg v-else xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 11l7-7 7 7M5 19l7-7 7 7" />
</svg>
</button>
<table ref="satelliteTable" v-show="show" class="text-center">
<thead class="text-2xl">
<th class="py-3 bg-blue-900 border border-gray-900"
v-for="label, index in ['序号', '卫星名称', '显示/隐藏', '跟踪']"
:key="label"
:style="{width: index == 1 ? '37.5vw' : '12.5vw'}">
{{ label }}
</th>
</thead>
<tbody class="text-lg odd:bg-gray-50">
<tr v-for="{ id, show }, index in satellites" :key="id">
<td class="border border-gray-900"> {{ index + 1 }} </td>
<td class="border border-gray-900"> {{ id }} </td>
<td class="border border-gray-900">
<input :checked="show" type="checkbox" class="w-6 h-6 cursor-pointer" @change="onShowStateCheckboxChanged(id)" />
</td>
<td class="border border-gray-900">
<button @click="onTrackButtonClicked(id)">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script lang="ts" setup>
import { useStore } from 'vuex'
import { ref, computed, watch } from 'vue'
const show = ref(false)
const store = useStore()
const satellites = computed(() => store.getters["satelliteSystem/satellites"])
const onTrackButtonClicked = id => store.dispatch('satelliteSystem/track', id)
const onShowStateCheckboxChanged = id => store.dispatch('satelliteSystem/toggleShow', id)
// 表格数据更新时,滚动条回到最顶部
const satelliteTable = ref(null)
watch(satellites, () => satelliteTable.value.scrollTop = 0)
</script>
<style scoped>
#satellite-table-container {
left: 12.5vw;
right: 12.5vw;
}
table {
display: block;
height: 360px;
overflow: auto;
}
tr:nth-child(odd) {
background-color: rgb(17, 24, 39);
}
tr:nth-child(even) {
background-color:rgb(31, 41, 55)
}
td {
padding: 5px 0;
}
::-webkit-scrollbar
{
width: 12px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgb(0 0 0 / 30%);
background-color: #4fbee2;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgb(0 0 0 / 30%);
background-color: #111827;
}
</style>>

View File

@@ -0,0 +1,67 @@
<template>
<button @click="show = !show" class="rounded border-blue-900 bg-blue-900">
<svg
v-if="show"
xmlns="http://www.w3.org/2000/svg"
width="600"
height="30"
fill="none"
viewBox="-10 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="3"
d="M -200 8 l 200 10 200 -10 m -400 -6 l 200 10 200 -10"
>
<animate
attributeName="stroke"
attributeType="XML"
from="#ffffff"
to="#666666"
begin="0s"
dur="1.5s"
fill="remove"
repeatCount="indefinite"
/>
</path>
</svg>
<svg
v-else
xmlns="http://www.w3.org/2000/svg"
width="600"
height="30"
fill="none"
viewBox="-10 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="3"
d="M -200 20 l 200 -10 200 10 m -400 -6 l 200 -10 200 10"
>
<animate
attributeName="stroke"
attributeType="XML"
from="#ffffff"
to="#666666"
begin="0s"
dur="1.5s"
fill="remove"
repeatCount="indefinite"
/>
</path>
</svg>
</button>
</template>
<script setup>
import { ref } from "vue";
const show = ref(false);
defineExpose({ show });
</script>
<style scoped></style>

View File

@@ -0,0 +1,21 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
</template>
<script setup></script>
<style scoped></style>

View File

@@ -1,17 +1,22 @@
import { createApp } from 'vue'
import App from './App.vue'
import { createApp } from 'vue';
import App from './App.vue';
import 'vue-cesium/dist/index.css'
import { VcViewer, VcLayerImagery, VcNavigation, VcDatasourceCzml, VcProviderImagerySingletile } from 'vue-cesium'
import 'vue-cesium/dist/index.css';
import { VcConfigProvider, VcViewer, VcLayerImagery, VcNavigation, VcDatasourceCzml, VcImageryProviderSingletile } from 'vue-cesium';
import store from './store'
import './main.css'
import store from './store';
import './main.css';
const cesiumPath = (process.env.NODE_ENV === 'development' ? './node_modules/cesium/Build/Cesium/Cesium.js': './Cesium/Cesium.js')
const app = createApp(App)
app.use(store)
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
const cesiumPath = (process.env.NODE_ENV === 'development' ? './node_modules/cesium/Build/Cesium/Cesium.js': './Cesium/Cesium.js');
const app = createApp(App);
app.use(store);
// 局部引入VueCesium
app.use(VcViewer).use(VcLayerImagery).use(VcNavigation).use(VcDatasourceCzml).use(VcProviderImagerySingletile)
app.config.globalProperties.$VueCesium = { cesiumPath }
app.mount('#app')
app.use(VcConfigProvider).use(VcViewer).use(VcLayerImagery).use(VcNavigation).use(VcDatasourceCzml).use(VcImageryProviderSingletile);
app.use(ElementPlus);
app.config.globalProperties.$VueCesium = { cesiumPath };
app.mount('#app');

View File

@@ -1,12 +1,12 @@
import { createStore } from 'vuex'
import satelliteSystem from './modules/satelliteSystem'
import { createStore } from 'vuex';
import satelliteSystem from './modules/satelliteSystem';
const debug = process.env.NODE_ENV !== 'production'
const debug = process.env.NODE_ENV !== 'production';
export default createStore({
modules: {
satelliteSystem
},
strict: debug
})
});

View File

@@ -1,13 +1,13 @@
const state = {
name: '',
satellites: []
}
};
// getters
const getters = {
name: (state) => state.name,
satellites: (state) => state.satellites
}
};
// actions
const actions = {
@@ -19,10 +19,12 @@ const actions = {
commit('setSatellites', satellites)
},
track () {},
track () {
},
toggleShow () {},
}
toggleShow () {
},
};
// mutations
const mutations = {
@@ -33,7 +35,7 @@ const mutations = {
setSatellites (state, satellites) {
state.satellites = satellites
},
}
};
export default {
namespaced: true,
@@ -41,4 +43,4 @@ export default {
getters,
actions,
mutations
}
};

3530
yarn.lock

File diff suppressed because it is too large Load Diff