Compare commits

...

No commits in common. "2ed697d8ff3b4f0de4d89fe3ecf3ec2ac8bd445b" and "2c5d110ee8fd76e88f00cf8a27b067e7ab32cf99" have entirely different histories.

60 changed files with 9439 additions and 2 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
.vscode
.idea
public/Cesium

View File

@ -1,3 +1,32 @@
# global-navigation-satellite-system
# Vite Electron Typescript Template
卫星轨道展示系统
`vite 2` `vue 3` `electron 12`
## How to use
clone the repo via git and install dependencies:
```shell
git clone --depth 1 --single-branch https://github.com/hocili/vite-electron-typescript-template.git your-project-name
cd your-project-name
yarn
```
## Starting Development
Start the app in the `dev` environment:
```shell
yarn dev
```
```shell
yarn electron:dev
```
## Packaging for Production
To package apps for the local platform:
```shell
yarn electron:build
```
# TLE数据源
https://celestrak.com/NORAD/elements/
# CZML数据预览
https://cesium.com/cesiumjs/cesium-viewer/

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html class="w-h-full">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>数字星球互动体验系统</title>
</head>
<body class="w-h-full">
<div id="app" class="w-h-full"></div>
<script type="module" src="/src/render/main.ts"></script>
</body>
</html>

4
index.js Normal file
View File

@ -0,0 +1,4 @@
require('ts-node').register({
project:'./tsconfig.electron.json'
});
require('./src/main/app.ts')

51
package.json Normal file
View File

@ -0,0 +1,51 @@
{
"name": "global-navigation-satellite-system",
"description": "global-navigation-satellite-system",
"author": "hwasmart",
"version": "6.0.0",
"main": "dist/main/app.js",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"electron:dev": "cross-env NODE_ENV=development electron index.js",
"electron:build": "rimraf dist && vite build && tsc -p tsconfig.electron.json && electron-builder --dir"
},
"dependencies": {
"cesium": "^1.88.0",
"vue": "^3.2.26",
"vue-cesium": "^3.0.4",
"vuex": "^4.0.1"
},
"devDependencies": {
"@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",
"rimraf": "^3.0.2",
"rollup-plugin-copy": "^3.4.0",
"tailwindcss": "^2.1.1",
"ts-node": "^9.1.1",
"typescript": "^4.2.3",
"vite": "^2.0.5"
},
"build": {
"appId": "hwasmart.id",
"mac": {
"category": "hwasmart.app.category.type"
},
"files": [
"dist/main/**/*",
"dist/render/**/*"
],
"directories": {
"output": "dist/release"
},
"win": {
"icon": "public/favicon.ico"
}
}
}

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

1
public/CZML/beidou.czml Normal file

File diff suppressed because one or more lines are too long

1
public/CZML/galileo.czml Normal file

File diff suppressed because one or more lines are too long

1
public/CZML/glo-ops.czml Normal file

File diff suppressed because one or more lines are too long

1
public/CZML/gps-ops.czml Normal file

File diff suppressed because one or more lines are too long

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 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: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
public/image/logo/GPS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

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 }

139
src/main/app.ts Normal file
View File

@ -0,0 +1,139 @@
import { app, globalShortcut, BrowserWindow, ipcMain, dialog } from 'electron'
import { join } from 'path'
import { keyword } from './KeyWord'
const child_process = require('child_process')
const crypto = require('crypto')
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'
async function checkLaunchEnv() {
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({
fullscreen: true,
frame: false,
webPreferences: {
nodeIntegration: true,
preload: join(__dirname, 'preload.js')
}
})
if (process.env.NODE_ENV === 'development') {
win.loadURL('http://localhost:3000/')
win.webContents.openDevTools()
} else {
win.loadFile('dist/render/index.html')
}
ipcMain.on('CLOSE', (event) => {
const res = dialog.showMessageBox({
type: 'warning',
title: '警告',
message: '确定要关闭软件吗?',
detail: '关闭软件',
cancelId: 1, // 按esc默认点击索引按钮
defaultId: 0, // 默认高亮的按钮下标
buttons: ['确认', '取消'], // 按钮按索引从右往左排序
})
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('系统提示', '软件启动出错,请联系售后技术支持人员')
process.exit(1)
}
}
main()

20
src/main/preload.js Normal file
View File

@ -0,0 +1,20 @@
const {ipcRenderer, contextBridge} = require('electron')
contextBridge.exposeInMainWorld('ipcRenderer', {
send: (channel, data) => {
// whitelist channels
let validChannels = ['toMain']
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data)
}
ipcRenderer.send(channel, data)
},
receive: (channel, func) => {
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))
}
})

18
src/render/App.vue Normal file
View File

@ -0,0 +1,18 @@
<template>
<div class="w-h-full">
<EarthView />
<FlagView class="fixed bottom-0 right-0" />
<SatelliteSystemSelectView class="fixed top-1" />
<SatelliteTableView class="fixed left-0 right-0 bottom-0" />
</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>

23
src/render/api/entity.js Normal file
View File

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
<template>
<div v-show="!!src">
<img :src="src" @dblclick="close">
</div>
</template>
<script lang="ts" setup>
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;
}
</style>

View File

@ -0,0 +1,29 @@
<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 });window.allowUpdate = true;})
onMounted(() => selected.value = Object.keys(option_dic)[0])
const option_dic = {
'beidou' : '中国北斗卫星导航系统',
'gps-ops' : '美国全球定位系统',
'glo-ops' : '俄罗斯格洛纳斯卫星导航系统',
'galileo' : '欧盟研制伽利略卫星导航系统'
}
</script>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
<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="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>
<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 }}
<input v-if="label == '/'" :indeterminate="someshow" :checked="allshow" type="checkbox" class="w-6 h-6 cursor-pointer" @change="allOnShowStateCheckboxChanged" />
</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="ashow" 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 ashow = ref(true)
const someshow = ref(false)
const allshow = ref(true)
const showarr = ref({})
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
})
</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>>

9
src/render/main.css Normal file
View File

@ -0,0 +1,9 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.w-h-full {
@apply w-full h-full m-0 p-0
}
}

17
src/render/main.ts Normal file
View File

@ -0,0 +1,17 @@
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 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)
// 局部引入VueCesium
app.use(VcViewer).use(VcLayerImagery).use(VcNavigation).use(VcDatasourceCzml).use(VcProviderImagerySingletile)
app.config.globalProperties.$VueCesium = { cesiumPath }
app.mount('#app')

5
src/render/shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare module '*.vue' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

12
src/render/store/index.js Normal file
View File

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

View File

@ -0,0 +1,46 @@
const state = {
name: '',
satellites: []
}
// getters
const getters = {
name: (state) => state.name,
satellites: (state) => state.satellites
}
// actions
const actions = {
setName ({ commit }, { name }) {
commit('setName', name)
},
setSatellites ({ commit }, { satellites }) {
commit('setSatellites', satellites)
},
track () {
},
toggleShow () {
},
}
// mutations
const mutations = {
setName (state, name) {
state.name = name
},
setSatellites (state, satellites) {
state.satellites = satellites
},
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}

11
tailwind.config.js Normal file
View File

@ -0,0 +1,11 @@
module.exports = {
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}

16
tsconfig.electron.json Normal file
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "esnext",
"module": "CommonJS",
"moduleResolution": "node",
"strict": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"allowJs": true,
"outDir": "dist/main"
},
"include": [
"src/main/app.ts",
"src/main/preload.js",
]
}

16
tsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"types": ["vite/client"]
},
"include": ["src/render/**/*.ts", "src/render/**/*.d.ts", "src/render/**/*.tsx", "src/render/**/*.vue"],
"exclude": ["src/app.ts"]
}

28
vite.config.ts Normal file
View File

@ -0,0 +1,28 @@
import { join } from 'path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import copy from 'rollup-plugin-copy'
const outDir = join(__dirname, 'dist/render')
const renderDir = join(__dirname, 'src/render')
const publicDir = join(__dirname, 'public')
// https://vitejs.dev/config/
export default defineConfig({
publicDir,
base: './',
plugins: [vue(),copy({
targets: [
{ src: './node_modules/cesium/Build/Cesium', dest: publicDir }, //执行拷贝
]
})],
build: {
outDir,
emptyOutDir: true,
},
resolve: {
alias: {
'@': renderDir,
}
},
})

3573
yarn.lock Normal file

File diff suppressed because it is too large Load Diff