diff --git a/.electron-vite/afterPack.js b/.electron-vite/afterPack.js
new file mode 100644
index 0000000..d93e36c
--- /dev/null
+++ b/.electron-vite/afterPack.js
@@ -0,0 +1,17 @@
+// pack后对不同位数的系统迁移不同的文件
+'use strict';
+const { copySync, ensureDirSync } = require('fs-extra');
+const { join } = require('path');
+const { Arch } = require('electron-builder');
+// type ElectronPlatformName = "darwin" | "linux" | "win32" | "mas"
+exports.default = async context => {
+ const LIB_OUTPUT_DIR = context.appOutDir;
+ const LIB_INPUT_DIR = join("rootLib", context.electronPlatformName, Arch[context.arch]);
+ const LIB_COMMON_INPUT_DIR = join("rootLib", "common");
+ // 确保文件夹存在
+ ensureDirSync(LIB_COMMON_INPUT_DIR);
+ ensureDirSync(LIB_INPUT_DIR);
+ // 移动文件文件
+ copySync(LIB_INPUT_DIR, LIB_OUTPUT_DIR);
+ copySync(LIB_COMMON_INPUT_DIR, LIB_OUTPUT_DIR);
+};
\ No newline at end of file
diff --git a/.electron-vite/build.ts b/.electron-vite/build.ts
new file mode 100644
index 0000000..6befff8
--- /dev/null
+++ b/.electron-vite/build.ts
@@ -0,0 +1,94 @@
+process.env.NODE_ENV = 'production'
+
+import { join } from 'path'
+import { say } from 'cfonts'
+import { sync } from 'del'
+import { build } from 'vite'
+import chalk from 'chalk'
+import { rollup, OutputOptions } from 'rollup'
+import Multispinner from 'Multispinner'
+import rollupOptions from './rollup.config'
+import { okayLog, errorLog, doneLog } from './log'
+
+
+const mainOpt = rollupOptions(process.env.NODE_ENV, "main");
+const isCI = process.env.CI || false
+
+
+if (process.env.BUILD_TARGET === 'web') web()
+else unionBuild()
+
+function clean() {
+ sync(['dist/electron/main/*', 'dist/electron/renderer/*', 'dist/web/*', 'build/*', '!build/icons', '!build/lib', '!build/lib/electron-build.*', '!build/icons/icon.*'])
+ console.log(`\n${doneLog}clear done`)
+ if (process.env.BUILD_TARGET === 'onlyClean') process.exit()
+}
+
+function unionBuild() {
+ greeting()
+ if (process.env.BUILD_TARGET === 'clean' || process.env.BUILD_TARGET === 'onlyClean') clean()
+
+ const tasks = ['main', 'renderer']
+ const m = new Multispinner(tasks, {
+ preText: 'building',
+ postText: 'process'
+ })
+ let results = ''
+
+ m.on('success', () => {
+ process.stdout.write('\x1B[2J\x1B[0f')
+ console.log(`\n\n${results}`)
+ console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`)
+ process.exit()
+ })
+
+ rollup(mainOpt)
+ .then(build => {
+ results += `${doneLog}MainProcess build success` + '\n\n'
+ build.write(mainOpt.output as OutputOptions).then(() => {
+ m.success('main')
+ })
+ })
+ .catch(error => {
+ m.error('main')
+ console.log(`\n ${errorLog}failed to build main process`)
+ console.error(`\n${error}\n`)
+ process.exit(1)
+ });
+
+ build({ configFile: join(__dirname, 'vite.config') }).then(res => {
+ results += `${doneLog}RendererProcess build success` + '\n\n'
+ m.success('renderer')
+ }).catch(err => {
+ m.error('renderer')
+ console.log(`\n ${errorLog}failed to build renderer process`)
+ console.error(`\n${err}\n`)
+ process.exit(1)
+ })
+}
+
+function web() {
+ sync(['dist/web/*', '!.gitkeep'])
+ build({ configFile: join(__dirname, 'vite.config') }).then(res => {
+ console.log(`${doneLog}RendererProcess build success`)
+ process.exit()
+ })
+}
+
+function greeting() {
+ const cols = process.stdout.columns
+ let text: boolean | string = ''
+
+ if (cols > 85) text = `let's-build`
+ else if (cols > 60) text = `let's-|build`
+ else text = false
+
+ if (text && !isCI) {
+ say(text, {
+ colors: ['yellow'],
+ font: 'simple3d',
+ space: false
+ })
+ } else console.log(chalk.yellow.bold(`\n let's-build`))
+ console.log()
+}
\ No newline at end of file
diff --git a/.electron-vite/dev-runner.ts b/.electron-vite/dev-runner.ts
new file mode 100644
index 0000000..11e9415
--- /dev/null
+++ b/.electron-vite/dev-runner.ts
@@ -0,0 +1,189 @@
+process.env.NODE_ENV = 'development'
+
+import electron from 'electron';
+import chalk from 'chalk';
+import { join } from 'path';
+import { watch } from 'rollup';
+import Portfinder from 'Portfinder';
+import config from '../config'
+import { say } from 'cfonts';
+import { spawn } from 'child_process';
+import type { ChildProcess } from 'child_process'
+import { createServer } from 'vite'
+import rollupOptions from './rollup.config'
+
+
+const mainOpt = rollupOptions(process.env.NODE_ENV, "main");
+
+let electronProcess: ChildProcess | null = null
+let manualRestart = false
+
+function logStats(proc: string, data: any) {
+ let log = ''
+
+ log += chalk.yellow.bold(`┏ ${proc} ${config.dev.chineseLog ? '编译过程' : 'Process'} ${new Array((19 - proc.length) + 1).join('-')}`)
+ log += '\n\n'
+
+ if (typeof data === 'object') {
+ data.toString({
+ colors: true,
+ chunks: false
+ }).split(/\r?\n/).forEach(line => {
+ log += ' ' + line + '\n'
+ })
+ } else {
+ log += ` ${data}\n`
+ }
+
+ log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n'
+ console.log(log)
+}
+
+function removeJunk(chunk: string) {
+ if (config.dev.removeElectronJunk) {
+ // Example: 2018-08-10 22:48:42.866 Electron[90311:4883863] *** WARNING: Textured window
+ if (/\d+-\d+-\d+ \d+:\d+:\d+\.\d+ Electron(?: Helper)?\[\d+:\d+] /.test(chunk)) {
+ return false;
+ }
+
+ // Example: [90789:0810/225804.894349:ERROR:CONSOLE(105)] "Uncaught (in promise) Error: Could not instantiate: ProductRegistryImpl.Registry", source: chrome-devtools://devtools/bundled/inspector.js (105)
+ if (/\[\d+:\d+\/|\d+\.\d+:ERROR:CONSOLE\(\d+\)\]/.test(chunk)) {
+ return false;
+ }
+
+ // Example: ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
+ if (/ALSA lib [a-z]+\.c:\d+:\([a-z_]+\)/.test(chunk)) {
+ return false;
+ }
+ }
+
+
+ return chunk;
+}
+
+function startRenderer(): Promise {
+ return new Promise((resolve, reject) => {
+ Portfinder.basePort = config.dev.port || 9080
+ Portfinder.getPort(async (err, port) => {
+ if (err) {
+ reject("PortError:" + err)
+ } else {
+ const server = await createServer({ configFile: join(__dirname, 'vite.config') })
+ process.env.PORT = String(port)
+ await server.listen(port)
+ console.log('\n\n' + chalk.blue(`${config.dev.chineseLog ? ' 正在准备主进程,请等待...' : ' Preparing main process, please wait...'}`) + '\n\n')
+ resolve()
+ }
+ })
+ })
+}
+
+function startMain(): Promise {
+ return new Promise((resolve, reject) => {
+ const MainWatcher = watch(mainOpt);
+ MainWatcher.on('change', filename => {
+ // 主进程日志部分
+ logStats(`${config.dev.chineseLog ? '主进程文件变更' : 'Main-FileChange'}`, filename)
+ });
+ MainWatcher.on('event', event => {
+ if (event.code === 'END') {
+ if (electronProcess && electronProcess.kill) {
+ manualRestart = true
+ process.kill(electronProcess.pid)
+ electronProcess = null
+ startElectron()
+
+ setTimeout(() => {
+ manualRestart = false
+ }, 5000)
+ }
+
+ resolve()
+
+ } else if (event.code === 'ERROR') {
+ reject(event.error)
+ }
+ })
+ })
+}
+
+
+function startElectron() {
+
+ var args = [
+ '--inspect=5858',
+ join(__dirname, '../dist/electron/main/main.js')
+ ]
+
+ // detect yarn or npm and process commandline args accordingly
+ if (process.env.npm_execpath.endsWith('yarn.js')) {
+ args = args.concat(process.argv.slice(3))
+ } else if (process.env.npm_execpath.endsWith('npm-cli.js')) {
+ args = args.concat(process.argv.slice(2))
+ }
+
+ electronProcess = spawn(electron as any, args)
+
+ electronProcess.stdout.on('data', (data: string) => {
+ electronLog(removeJunk(data), 'blue')
+ })
+ electronProcess.stderr.on('data', (data: string) => {
+ electronLog(removeJunk(data), 'red')
+ })
+
+ electronProcess.on('close', () => {
+ if (!manualRestart) process.exit()
+ })
+}
+
+function electronLog(data: any, color: string) {
+ if (data) {
+ let log = ''
+ data = data.toString().split(/\r?\n/)
+ data.forEach(line => {
+ log += ` ${line}\n`
+ })
+ console.log(
+ chalk[color].bold(`┏ ${config.dev.chineseLog ? '主程序日志' : 'Electron'} -------------------`) +
+ '\n\n' +
+ log +
+ chalk[color].bold('┗ ----------------------------') +
+ '\n'
+ )
+ }
+
+}
+
+function greeting() {
+ const cols = process.stdout.columns
+ let text: string | boolean = ''
+
+ if (cols > 104) text = 'electron-vite'
+ else if (cols > 76) text = 'electron-|vite'
+ else text = false
+
+ if (text) {
+ say(text, {
+ colors: ['yellow'],
+ font: 'simple3d',
+ space: false
+ })
+ } else console.log(chalk.yellow.bold('\n electron-vite'))
+ console.log(chalk.blue(`${config.dev.chineseLog ? ' 准备启动...' : ' getting ready...'}`) + '\n')
+}
+
+async function init() {
+ greeting()
+
+ try {
+ await startRenderer()
+ await startMain()
+ startElectron()
+ } catch (error) {
+ console.error(error)
+ process.exit(1)
+ }
+
+}
+
+init()
\ No newline at end of file
diff --git a/.electron-vite/hot-updater.ts b/.electron-vite/hot-updater.ts
new file mode 100644
index 0000000..718d628
--- /dev/null
+++ b/.electron-vite/hot-updater.ts
@@ -0,0 +1,130 @@
+/**
+ * power by biuuu
+ */
+
+import chalk from 'chalk'
+import { join } from 'path'
+import { ensureDir, emptyDir, copy, outputJSON, remove, stat, readFile } from 'fs-extra'
+import { createHmac } from 'crypto'
+import { platform } from 'os'
+import AdmZip from 'adm-zip'
+import packageFile from '../package.json'
+import buildConfig from '../build.json'
+import config from '../config'
+import { okayLog, infoLog, errorLog, doneLog } from './log'
+
+
+const platformName = platform().includes('win32') ? 'win' : platform().includes('darwin') ? 'mac' : 'linux'
+const buildPath = join('.', 'build', `${platformName === 'mac' ? 'mac' : platformName + '-unpacked'}`)
+
+const hash = (data, type = 'sha256') => {
+ const hmac = createHmac(type, 'Sky')
+ hmac.update(data)
+ return hmac.digest('hex')
+}
+
+const createZip = (filePath: string, dest: string) => {
+ const zip = AdmZip()
+ zip.addLocalFolder(filePath, "", null)
+ zip.toBuffer()
+ zip.writeZip(dest, null)
+}
+
+const start = async () => {
+ console.log(chalk.green.bold(`Start packing \n`))
+
+ if (buildConfig.asar) {
+ console.log(
+ "\n" +
+ errorLog +
+ " " +
+ chalk.red("Please make sure the build.asar option in the Package.json file is set to false") +
+ "\n"
+ );
+ return;
+ }
+
+ if (config.build.hotPublishConfigName === '') {
+ console.log(
+ "\n" +
+ errorLog +
+ " " +
+ chalk.red("HotPublishConfigName is not set, which will cause the update to fail, please set it in the config/index.js \n")
+ + chalk.red.bold(`\n Packing failed \n`)
+ );
+ process.exit(1)
+ }
+
+ stat(join(buildPath, 'resources', 'app'), async (err, stats) => {
+ if (err) {
+ console.log(
+ "\n" +
+ errorLog +
+ " " +
+ chalk.red("No resource files were found, please execute this command after the build command") +
+ "\n"
+ );
+ return;
+ }
+
+ try {
+ console.log(chalk.green.bold(`Check the resource files \n`))
+ const packResourcesPath = join('.', 'build', 'resources', 'dist');
+ const packPackagePath = join('.', 'build', 'resources');
+ const resourcesPath = join('.', 'dist');
+ const appPath = join('.', 'build', 'resources');
+ const name = "app.zip";
+ const outputPath = join('.', 'build', 'update');
+ const zipPath = join(outputPath, name);
+
+
+ await ensureDir(packResourcesPath);
+ await emptyDir(packResourcesPath);
+ await copy(resourcesPath, packResourcesPath);
+ console.log(`${okayLog} ${chalk.cyan.bold(`File copy complete \n`)}`)
+ await outputJSON(join(packPackagePath, "package.json"), {
+ name: packageFile.name,
+ productName: buildConfig.productName,
+ version: packageFile.version,
+ description: packageFile.description,
+ main: packageFile.main,
+ author: packageFile.author,
+ dependencies: packageFile.dependencies
+ });
+ console.log(`${okayLog} ${chalk.cyan.bold(`Rewrite package file complete \n`)}`)
+ await ensureDir(outputPath);
+ await emptyDir(outputPath);
+ createZip(appPath, zipPath);
+ const buffer = await readFile(zipPath);
+ const sha256 = hash(buffer);
+ const hashName = sha256.slice(7, 12);
+ await copy(zipPath, join(outputPath, `${hashName}.zip`));
+ await outputJSON(join(outputPath, `${config.build.hotPublishConfigName}.json`),
+ {
+ version: packageFile.version,
+ name: `${hashName}.zip`,
+ hash: sha256
+ }
+ );
+ console.log(`${okayLog} ${chalk.cyan.bold(`Zip file complete, Start cleaning up redundant files \n`)}`)
+ await remove(zipPath);
+ await remove(appPath)
+ console.log(`${okayLog} ${chalk.cyan.bold(`Cleaning up redundant files completed \n`)}`)
+ console.log(
+ "\n" + doneLog + " " + "The resource file is packaged!\n"
+ );
+ console.log("File location: " + chalk.green(outputPath) + "\n");
+ } catch (error) {
+ console.log(
+ "\n" +
+ errorLog +
+ " " +
+ chalk.red(error.message || error) +
+ "\n"
+ );
+ process.exit(1)
+ }
+ });
+}
+
+start()
\ No newline at end of file
diff --git a/.electron-vite/log/index.ts b/.electron-vite/log/index.ts
new file mode 100644
index 0000000..ecb7327
--- /dev/null
+++ b/.electron-vite/log/index.ts
@@ -0,0 +1,7 @@
+import chalk from 'chalk'
+
+export const doneLog = chalk.bgGreen.white(' DONE ') + ' '
+export const errorLog = chalk.bgRed.white(' ERROR ') + ' '
+export const okayLog = chalk.bgBlue.white(' OKAY ') + ' '
+export const warningLog = chalk.bgYellow.white(' WARNING ') + ' '
+export const infoLog = chalk.bgCyan.white(' INFO ') + ' '
\ No newline at end of file
diff --git a/.electron-vite/rollup.config.ts b/.electron-vite/rollup.config.ts
new file mode 100644
index 0000000..93cce3d
--- /dev/null
+++ b/.electron-vite/rollup.config.ts
@@ -0,0 +1,85 @@
+import path from "path";
+import { nodeResolve } from '@rollup/plugin-node-resolve'
+import { builtinModules } from 'module'
+import commonjs from '@rollup/plugin-commonjs'
+import replace from '@rollup/plugin-replace'
+import alias from '@rollup/plugin-alias'
+import json from '@rollup/plugin-json';
+import esbuild from 'rollup-plugin-esbuild'
+// import obfuscator from 'rollup-plugin-obfuscator'
+import { defineConfig } from 'rollup'
+
+export default (env = "production", type = "main") => {
+ return defineConfig({
+ input:
+ type === "main"
+ ? path.join(__dirname, "..", "src", "main", "index.ts")
+ : path.join(__dirname, "..", "src", "preload", "index.ts"),
+ output: {
+ file: path.join(
+ __dirname,
+ "..", "dist", "electron", "main", `${type === "main" ? type : "preload"}.js`
+ ),
+ format: "cjs",
+ name: type === "main" ? "MainProcess" : "MainPreloadProcess",
+ sourcemap: false,
+ },
+ plugins: [
+ replace({
+ preventAssignment: true,
+ "process.env.NODE_ENV": JSON.stringify(env),
+ }),
+ // 提供路径和读取别名
+ nodeResolve({ preferBuiltins: true, browser: false, extensions: ['.mjs', '.ts', '.js', '.json', '.node'] }),
+ commonjs({
+ sourceMap: false,
+ }),
+ json(),
+ esbuild({
+ // All options are optional
+ include: /\.[jt]s?$/, // default, inferred from `loaders` option
+ exclude: /node_modules/, // default
+ // watch: process.argv.includes('--watch'), // rollup 中有配置
+ sourceMap: env != "production", // default
+ minify: env === "production",
+ target: "es2017", // default, or 'es20XX', 'esnext'
+ // Like @rollup/plugin-replace
+ define: {
+ __VERSION__: '"x.y.z"',
+ },
+ // Add extra loaders
+ loaders: {
+ // Add .json files support
+ // require @rollup/plugin-commonjs
+ ".json": "json",
+ // Enable JSX in .js files too
+ ".js": "jsx",
+ },
+ }),
+ alias({
+ entries: [
+ { find: "@main", replacement: path.join(__dirname, "../src/main") },
+ {
+ find: "@config",
+ replacement: path.join(__dirname, "..", "config"),
+ },
+ ],
+ }),
+ // process.env.NODE_ENV == "production" ? obfuscator({}) : null,
+ ],
+ external: [
+ ...builtinModules,
+ 'axios',
+ 'electron',
+ 'express',
+ 'ffi-napi',
+ 'ref-napi',
+ 'ref-struct-napi',
+ // 修正部分人会导致丢失依赖的问题,如果updater工作不正常请取消下面的注释,并自行安装semver
+ 'semver',
+ 'glob',
+ ],
+ })
+};
+
+
diff --git a/.electron-vite/vite.config.ts b/.electron-vite/vite.config.ts
new file mode 100644
index 0000000..6f75778
--- /dev/null
+++ b/.electron-vite/vite.config.ts
@@ -0,0 +1,49 @@
+import { join } from 'path'
+import { defineConfig } from 'vite'
+import vuePlugin from '@vitejs/plugin-vue'
+import vueJsx from '@vitejs/plugin-vue-jsx'
+import userConfig from '../config'
+
+const IsWeb = process.env.BUILD_TARGET === 'web'
+
+function resolve(dir: string) {
+ return join(__dirname, '..', dir)
+}
+userConfig.build.env.is_web = IsWeb
+userConfig.dev.env.is_web = IsWeb
+
+const root = resolve('src/renderer')
+
+export default defineConfig({
+ mode: process.env.NODE_ENV,
+ root,
+ define: {
+ 'process.env': process.env.NODE_ENV === 'production' ? userConfig.build.env : userConfig.dev.env,
+ },
+ resolve: {
+ alias: {
+ '@renderer': root,
+ '@store': join(root, '/store/modules'),
+ }
+ },
+ base: './',
+ build: {
+ outDir: IsWeb ? resolve('dist/web') : resolve('dist/electron/renderer'),
+ emptyOutDir: true,
+ target: 'esnext',
+ minify: 'esbuild'
+ },
+ server: {
+ },
+ plugins: [
+ vueJsx(),
+ vuePlugin({
+ script: {
+ refSugar: true
+ }
+ })
+ ],
+ optimizeDeps: {
+ },
+ publicDir: resolve('static')
+})
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 18772fe..9a15004 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
.DS_Store
node_modules
-/dist
+dist/
+build/
+!build/icons
lib/CesiumUnminified/
mars3d-*src.*
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..3cb7228
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/
\ No newline at end of file
diff --git a/.yarnrc b/.yarnrc
new file mode 100644
index 0000000..3cb7228
--- /dev/null
+++ b/.yarnrc
@@ -0,0 +1 @@
+ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index 0b91077..c73d816 100644
--- a/LICENSE
+++ b/LICENSE
@@ -203,4 +203,3 @@
See the License for the specific language governing permissions and
limitations under the License.
-
diff --git a/README.md b/README.md
index 1bfa873..71ee5d4 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,6 @@
-
基于Vue3.x + Vite2 + Electron 技术栈的 Mars3D🌎CS桌面程序项目模板
@@ -17,62 +16,64 @@
- Mars3D平台基于`Vue3.x ` 、 `Vite ` 和 `Electron` 的最简的CS应用项目模版,可以使用该模板建立自己的CS桌面版三维地球程序。
+Mars3D 平台基于`Vue3.x ` 、 `Vite ` 和 `Electron` 的最简的 CS 应用项目模版,可以使用该模板建立自己的 CS 桌面版三维地球程序。
-
+## 如何安装
+> 请确保您的 node 版本大于等于 16.
-## 首次运行前安装依赖
-```
-npm install
-```
+```bash
+npm config edit
+# 该命令会打开npm的配置文件,请在空白处添加,记得去除#号
+# electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
+# electron_custom_dir={{ version }}
+# electron_mirror=https://cdn.npmmirror.com/binaries/electron/v
+# registry=https://registry.npmmirror.com/
+# 然后关闭该窗口,重启命令行.
-## CS桌面程序访问
+# 使用yarn安装
+yarn or yarn install
-### 编译并热部署的Electron开发环境 (Compiles and hot-reloads for Electron development)
-```
-npm run dev
-```
+# 启动之后,会在9080端口监听
+yarn dev
+
+# build命令在不同系统环境中,需要的的不一样,需要自己根据自身环境进行配置
+yarn build
-### 编译并压缩的Electron生产环境 (Compiles and minifies for Electron production)
-```
-npm run build
```
+## 常见问题
+### npm 安装是 electron 总是超时
-## BS浏览器访问
+删除 package.json 中 electron 包,通过 npm install 安装成功后,再恢复 electron 包,通过 cnpm install 安装完成。
-### 编译并热部署的web浏览器开发环境 (Compiles and hot-reloads for development)
-```
-npm run dev:vue
-```
-### 编译并压缩的web浏览器生产环境 (Compiles and minifies for production)
-```
-npm run build:vue
-```
-
-## 常见问题
-### npm安装是electron总是超时
-删除package.json中electron包,通过npm install安装成功后,再恢复electron包,通过cnpm install安装完成。
+## 参考了仓库
+- [electron-vite-template](https://gitee.com/Zh-Sky/electron-vite-template)
+
+
-## Mars3D 是什么
-> `Mars3D平台` 是[火星科技](http://marsgis.cn/)研发的一款基于 WebGL 技术实现的三维客户端开发平台,基于[Cesium](https://cesium.com/cesiumjs/)优化提升与B/S架构设计,支持多行业扩展的轻量级高效能GIS开发平台,能够免安装、无插件地在浏览器中高效运行,并可快速接入与使用多种GIS数据和三维模型,呈现三维空间的可视化,完成平台在不同行业的灵活应用。
- > Mars3D平台可用于构建无插件、跨操作系统、 跨浏览器的三维 GIS 应用程序。平台使用 WebGL 来进行硬件加速图形化,跨平台、跨浏览器来实现真正的动态大数据三维可视化。通过 Mars3D产品可快速实现浏览器和移动端上美观、流畅的三维地图呈现与空间分析。
-### 相关网站
-- Mars3D官网:[http://mars3d.cn](http://mars3d.cn)
+## Mars3D 是什么
-- Mars3D开源项目列表:[https://github.com/marsgis/mars3d](https://github.com/marsgis/mars3d)
+> `Mars3D平台` 是[火星科技](http://marsgis.cn/)研发的一款基于 WebGL 技术实现的三维客户端开发平台,基于[Cesium](https://cesium.com/cesiumjs/)优化提升与 B/S 架构设计,支持多行业扩展的轻量级高效能 GIS 开发平台,能够免安装、无插件地在浏览器中高效运行,并可快速接入与使用多种 GIS 数据和三维模型,呈现三维空间的可视化,完成平台在不同行业的灵活应用。
+> Mars3D 平台可用于构建无插件、跨操作系统、 跨浏览器的三维 GIS 应用程序。平台使用 WebGL 来进行硬件加速图形化,跨平台、跨浏览器来实现真正的动态大数据三维可视化。通过 Mars3D 产品可快速实现浏览器和移动端上美观、流畅的三维地图呈现与空间分析。
+
+### 相关网站
+
+- Mars3D 官网:[http://mars3d.cn](http://mars3d.cn)
+
+- Mars3D 开源项目列表:[https://github.com/marsgis/mars3d](https://github.com/marsgis/mars3d)
## 版权说明
-1. Mars3D平台由[火星科技](http://marsgis.cn/)自主研发,拥有所有权利。
+
+1. Mars3D 平台由[火星科技](http://marsgis.cn/)自主研发,拥有所有权利。
2. 任何个人或组织可以在遵守相关要求下可以免费无限制使用。
diff --git a/build.json b/build.json
new file mode 100644
index 0000000..3cbbb10
--- /dev/null
+++ b/build.json
@@ -0,0 +1,43 @@
+{
+ "asar": false,
+ "extraFiles": [],
+ "publish": [
+ {
+ "provider": "generic",
+ "url": "http://127.0.0.1"
+ }
+ ],
+ "afterPack": ".electron-vite/afterPack.js",
+ "productName": "electron-vite-template",
+ "appId": "org.sky.electron-vite-template",
+ "directories": {
+ "output": "build"
+ },
+ "files": ["dist/electron/**/*"],
+ "dmg": {
+ "contents": [
+ {
+ "x": 410,
+ "y": 150,
+ "type": "link",
+ "path": "/Applications"
+ },
+ {
+ "x": 130,
+ "y": 150,
+ "type": "file"
+ }
+ ]
+ },
+ "mac": {
+ "icon": "build/icons/icon.icns"
+ },
+ "win": {
+ "icon": "build/icons/icon.ico",
+ "target": "nsis"
+ },
+ "linux": {
+ "target": "deb",
+ "icon": "build/icons"
+ }
+}
diff --git a/config/dev.env.ts b/config/dev.env.ts
new file mode 100644
index 0000000..bc20170
--- /dev/null
+++ b/config/dev.env.ts
@@ -0,0 +1,5 @@
+export default {
+ NODE_ENV: 'development',
+ BASE_API: 'http://127.0.0.1:25565',
+ is_web: false
+}
diff --git a/config/index.ts b/config/index.ts
new file mode 100644
index 0000000..ec7ce0e
--- /dev/null
+++ b/config/index.ts
@@ -0,0 +1,23 @@
+import prod from './prod.env'
+import dev from './dev.env'
+
+export default {
+ build: {
+ DisableF12: true,
+ env: prod,
+ // 示例
+ hotPublishUrl:"http://umbrella22.github.io/electron-vite-template",
+ hotPublishConfigName: "update-config"
+ },
+ dev: {
+ env: dev,
+ removeElectronJunk: true,
+ chineseLog: false,
+ port: 9080,
+ },
+ DllFolder: '',
+ HotUpdateFolder: 'update',
+ UseStartupChart: true,
+ IsUseSysTitle: true,
+ BuiltInServerPort: 25565
+}
\ No newline at end of file
diff --git a/config/prod.env.ts b/config/prod.env.ts
new file mode 100644
index 0000000..20c6c00
--- /dev/null
+++ b/config/prod.env.ts
@@ -0,0 +1,5 @@
+export default {
+ NODE_ENV: 'production',
+ BASE_API: 'http://127.0.0.1:25565',
+ is_web: false
+}
diff --git a/customTypes/Item.d.ts b/customTypes/Item.d.ts
new file mode 100644
index 0000000..ac18238
--- /dev/null
+++ b/customTypes/Item.d.ts
@@ -0,0 +1,4 @@
+interface ColorInfo {
+ color?: string,
+ percentage?: number
+}
\ No newline at end of file
diff --git a/customTypes/global.d.ts b/customTypes/global.d.ts
new file mode 100644
index 0000000..0e0138b
--- /dev/null
+++ b/customTypes/global.d.ts
@@ -0,0 +1,17 @@
+interface AnyObject {
+ [key: string]: any
+}
+
+interface memoryInfo {
+ jsHeapSizeLimit: number;
+ totalJSHeapSize: number;
+ usedJSHeapSize: number;
+}
+
+interface Window {
+ performance: {
+ memory: memoryInfo
+ }
+ __lib: string;
+ __static: string;
+}
\ No newline at end of file
diff --git a/customTypes/image.d.ts b/customTypes/image.d.ts
new file mode 100644
index 0000000..4bc2b76
--- /dev/null
+++ b/customTypes/image.d.ts
@@ -0,0 +1,7 @@
+declare module '*.svg'
+declare module '*.png'
+declare module '*.jpg'
+declare module '*.jpeg'
+declare module '*.gif'
+declare module '*.bmp'
+declare module '*.tiff'
\ No newline at end of file
diff --git a/customTypes/shims-vue.d.ts b/customTypes/shims-vue.d.ts
new file mode 100644
index 0000000..ac1ded7
--- /dev/null
+++ b/customTypes/shims-vue.d.ts
@@ -0,0 +1,5 @@
+declare module '*.vue' {
+ import { DefineComponent } from 'vue'
+ const component: DefineComponent<{}, {}, any>
+ export default component
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..8146a0d
--- /dev/null
+++ b/package.json
@@ -0,0 +1,87 @@
+{
+ "name": "mars3d-electron",
+ "version": "3.3.0",
+ "description": "Mars3D平台,electron桌面程序最简项目模版",
+ "main": "./dist/electron/main/main.js",
+ "scripts": {
+ "dev": "esno .electron-vite/dev-runner.ts",
+ "build": "cross-env BUILD_TARGET=clean esno .electron-vite/build.ts && electron-builder -c build.json",
+ "build:win32": "cross-env BUILD_TARGET=clean esno .electron-vite/build.ts && electron-builder -c build.json --win --ia32",
+ "build:win64": "cross-env BUILD_TARGET=clean esno .electron-vite/build.ts && electron-builder -c build.json --win --x64",
+ "build:mac": "cross-env BUILD_TARGET=clean esno .electron-vite/build.ts && electron-builder -c build.json --mac",
+ "build:dir": "cross-env BUILD_TARGET=clean esno .electron-vite/build.ts && electron-builder -c build.json --dir",
+ "build:clean": "cross-env BUILD_TARGET=onlyClean esno .electron-vite/build.ts",
+ "build:web": "cross-env BUILD_TARGET=web esno .electron-vite/build.ts",
+ "pack:resources": "esno .electron-vite/hot-updater.ts",
+ "pack:rustUpdater": "electron_updater_node_cli -p -c updateConfig.json",
+ "dep:upgrade": "yarn upgrade-interactive --latest",
+ "postinstall": "electron-builder install-app-deps"
+ },
+ "dependencies": {
+ "axios": "^0.27.2",
+ "electron-log": "^4.4.6",
+ "electron-updater": "^5.0.1",
+ "express": "^4.18.1",
+ "glob": "^8.0.1",
+ "semver": "^7.3.5"
+ },
+ "devDependencies": {
+ "@rollup/plugin-alias": "^3.1.9",
+ "@rollup/plugin-commonjs": "^22.0.0",
+ "@rollup/plugin-json": "^4.1.0",
+ "@rollup/plugin-node-resolve": "^13.1.3",
+ "@rollup/plugin-replace": "^4.0.0",
+ "@types/fs-extra": "^9.0.13",
+ "@types/node": "^17.0.31",
+ "@vitejs/plugin-vue": "^2.3.1",
+ "@vitejs/plugin-vue-jsx": "^1.3.9",
+ "@vue/compiler-sfc": "^3.2.31",
+ "adm-zip": "^0.5.9",
+ "cfonts": "^2.10.0",
+ "chalk": "5.0.1",
+ "cross-env": "^7.0.3",
+ "del": "^6.0.0",
+ "electron": "17.2.0",
+ "electron-builder": "^23.0.3",
+ "electron-devtools-vendor": "^1.0.5",
+ "electron_updater_node_cli": "^0.1.3",
+ "electron_updater_node_core": "^0.1.4",
+ "element-plus": "^2.1.11",
+ "esno": "^0.14.1",
+ "extract-zip": "^2.0.1",
+ "fs-extra": "^10.1.0",
+ "multispinner": "^0.2.1",
+ "pinia": "^2.0.12",
+ "portfinder": "^1.0.28",
+ "rollup-plugin-esbuild": "^4.8.2",
+ "sass": "^1.51.0",
+ "tslib": "^2.4.0",
+ "typescript": "^4.6.4",
+ "vite": "^2.9.6",
+ "vue": "^3.2.31",
+ "vue-i18n": "^9.1.10",
+ "vue-router": "^4.0.14"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/marsgis/mars3d-vue-template.git"
+ },
+ "bugs": {
+ "url": "https://github.com/marsgis/mars3d-vue-template/issues",
+ "email": "wh@marsgis.cn"
+ },
+ "keywords": [
+ "marsgis",
+ "mars3d",
+ "cesium",
+ "vite",
+ "electron",
+ "vue3",
+ "rollup",
+ "vue",
+ "gis"
+ ],
+ "author": "火星科技",
+ "license": "Apache-2.0",
+ "homepage": "http://mars3d.cn"
+}
diff --git a/rootLib/win32/ia32/updater.exe b/rootLib/win32/ia32/updater.exe
new file mode 100644
index 0000000..2e50055
Binary files /dev/null and b/rootLib/win32/ia32/updater.exe differ
diff --git a/rootLib/win32/x64/updater.exe b/rootLib/win32/x64/updater.exe
new file mode 100644
index 0000000..2e50055
Binary files /dev/null and b/rootLib/win32/x64/updater.exe differ
diff --git a/src/index.d.ts b/src/index.d.ts
new file mode 100644
index 0000000..ef6ac5e
--- /dev/null
+++ b/src/index.d.ts
@@ -0,0 +1,3 @@
+interface Window {
+ mars3d: any;
+}
diff --git a/src/main/config/DisableButton.ts b/src/main/config/DisableButton.ts
new file mode 100644
index 0000000..c93f2ba
--- /dev/null
+++ b/src/main/config/DisableButton.ts
@@ -0,0 +1,12 @@
+import { globalShortcut } from 'electron'
+import config from '@config/index'
+
+export default {
+ Disablef12 () {
+ if (process.env.NODE_ENV === 'production' && config.DisableF12) {
+ globalShortcut.register('f12', () => {
+ console.log('用户试图启动控制台')
+ })
+ }
+ }
+}
diff --git a/src/main/config/StaticPath.ts b/src/main/config/StaticPath.ts
new file mode 100644
index 0000000..22647d6
--- /dev/null
+++ b/src/main/config/StaticPath.ts
@@ -0,0 +1,81 @@
+// 这里定义了静态文件路径的位置
+import { join } from 'path'
+import config from '@config/index'
+import { app } from 'electron'
+import { URL } from 'url';
+const isDev = process.env.NODE_ENV === 'development';
+class StaticPath {
+ constructor() {
+ const basePath = isDev ? join(__dirname, '..', '..', '..') : join(app.getAppPath(), '..', '..');
+ this.__updateFolder = join(basePath, `${config.HotUpdateFolder}`)
+ if (isDev) {
+ this.__static = join(basePath, 'static');
+ this.__lib = join(basePath, `rootLib`, `${process.platform}`, `${process.arch}`);
+ this.__common = join(basePath, 'rootLib', 'common');
+ } else {
+ this.__static = join(__dirname, '..', 'renderer');
+ this.__lib = basePath;
+ this.__common = basePath;
+ }
+ }
+ /**
+ * 静态文件路径 渲染进程目录下
+ *
+ * @type {string}
+ * @memberof StaticPath
+ */
+ __static: string;
+ /**
+ * dll文件夹及其他os平台相关的文件路径
+ *
+ * @type {string}
+ * @memberof StaticPath
+ */
+ __lib: string;
+ /**
+ * 与os无关的资源
+ *
+ * @type {string}
+ * @memberof StaticPath
+ */
+ __common: string;
+ /**
+ * 增量更新文件夹
+ *
+ * @type {string}
+ * @memberof StaticPath
+ */
+ __updateFolder: string;
+}
+const staticPath = new StaticPath();
+/**
+ * 获取真正的地址
+ *
+ * @param {string} devPath 开发环境路径
+ * @param {string} proPath 生产环境路径
+ * @param {string} [hash=""] hash值
+ * @param {string} [search=""] search值
+ * @return {*} {string} 地址
+ */
+function getUrl(devPath: string, proPath: string, hash: string = "", search: string = ""): string {
+ const url = isDev ? new URL(`http://localhost:${process.env.PORT}`) : new URL('file://');
+ url.pathname = isDev ? devPath : proPath;
+ url.hash = hash;
+ url.search = search;
+ return url.href;
+}
+export const winURL = getUrl("", join(__dirname, '..', 'renderer', 'index.html'));
+export const loadingURL = getUrl("/loader.html", `${staticPath.__static}/loader.html`);
+export const preloadURL = getUrl("/preload.html", `${staticPath.__static}/preload.html`);
+export const printURL = getUrl("", join(__dirname, '..', 'renderer', 'index.html'), "#/Print");
+export const preloadPath = isDev ? join(app.getAppPath(), "..", "preload.js") : join(app.getAppPath(), "dist", "electron", "preload.js");
+export const lib = staticPath.__lib
+export const common = staticPath.__common
+export const updateFolder = staticPath.__updateFolder
+export const staticPaths = getUrl('', staticPath.__static)
+
+// process.env 修改
+for (const key in staticPath) {
+ process.env[key] = staticPath[key];
+}
+
diff --git a/src/main/config/hotPublish.ts b/src/main/config/hotPublish.ts
new file mode 100644
index 0000000..b146208
--- /dev/null
+++ b/src/main/config/hotPublish.ts
@@ -0,0 +1,10 @@
+import config from '@config/index.js'
+interface hotPublish {
+ url: string;
+ configName: string;
+}
+
+export const hotPublishConfig: hotPublish = {
+ url: config.build.hotPublishUrl,
+ configName: config.build.hotPublishConfigName
+}
\ No newline at end of file
diff --git a/src/main/config/menu.ts b/src/main/config/menu.ts
new file mode 100644
index 0000000..fc27be8
--- /dev/null
+++ b/src/main/config/menu.ts
@@ -0,0 +1,38 @@
+// 这里是定义菜单的地方,详情请查看 https://electronjs.org/docs/api/menu
+import { dialog } from 'electron'
+import { type, arch, release } from 'os'
+import packageInfo from '../../../package.json'
+
+const menu = [
+ {
+ label: '设置',
+ submenu: [{
+ label: '快速重启',
+ accelerator: 'F5',
+ role: 'reload'
+ }, {
+ label: '退出',
+ accelerator: 'CmdOrCtrl+F4',
+ role: 'close'
+ }]
+ }, {
+ label: '帮助',
+ submenu: [{
+ label: '关于',
+ click: function () {
+ info()
+ }
+ }]
+ }]
+function info() {
+ dialog.showMessageBox({
+ title: '关于',
+ type: 'info',
+ message: 'electron-Vue框架',
+ detail: `版本信息:${packageInfo.version}\n引擎版本:${process.versions.v8}\n当前系统:${type()} ${arch()} ${release()}`,
+ noLink: true,
+ buttons: ['查看github', '确定']
+ })
+}
+
+export default menu
diff --git a/src/main/config/windowsConfig.ts b/src/main/config/windowsConfig.ts
new file mode 100644
index 0000000..ea7d0a1
--- /dev/null
+++ b/src/main/config/windowsConfig.ts
@@ -0,0 +1,40 @@
+import config from '@config/index'
+import { BrowserWindowConstructorOptions } from 'electron';
+import { preloadPath } from './StaticPath';
+
+export const mainWindowConfig: BrowserWindowConstructorOptions = {
+ height: 800,
+ useContentSize: true,
+ width: 1700,
+ minWidth: 1366,
+ show: false,
+ frame: config.IsUseSysTitle,
+ webPreferences: {
+ contextIsolation: false,
+ nodeIntegration: true,
+ webSecurity: false,
+ // 如果是开发模式可以使用devTools
+ devTools: process.env.NODE_ENV === 'development',
+ // 在macos中启用橡皮动画
+ scrollBounce: process.platform === 'darwin',
+ }
+};
+
+export const otherWindowConfig: BrowserWindowConstructorOptions = {
+ height: 595,
+ useContentSize: true,
+ width: 1140,
+ autoHideMenuBar: true,
+ minWidth: 842,
+ frame: config.IsUseSysTitle,
+ show: false,
+ webPreferences: {
+ contextIsolation: false,
+ nodeIntegration: true,
+ webSecurity: false,
+ // 如果是开发模式可以使用devTools
+ devTools: process.env.NODE_ENV === 'development',
+ // 在macos中启用橡皮动画
+ scrollBounce: process.platform === 'darwin',
+ }
+}
diff --git a/src/main/index.ts b/src/main/index.ts
new file mode 100644
index 0000000..9f7b4a9
--- /dev/null
+++ b/src/main/index.ts
@@ -0,0 +1,38 @@
+'use strict'
+
+import { app, session } from 'electron'
+import InitWindow from './services/windowManager'
+import DisableButton from './config/DisableButton'
+function onAppReady() {
+ new InitWindow().initWindow()
+ DisableButton.Disablef12()
+ if (process.env.NODE_ENV === 'development') {
+ const { VUEJS3_DEVTOOLS } = require("electron-devtools-vendor");
+ session.defaultSession.loadExtension(VUEJS3_DEVTOOLS, {
+ allowFileAccess: true,
+ });
+ console.log('已安装: vue-devtools')
+ }
+}
+
+app.whenReady().then(onAppReady)
+
+// 由于9.x版本问题,需要加入该配置关闭跨域问题
+app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors')
+
+app.on('window-all-closed', () => {
+ // 所有平台均为所有窗口关闭就退出软件
+ app.quit()
+})
+app.on('browser-window-created', () => {
+ console.log('window-created')
+})
+
+if (process.defaultApp) {
+ if (process.argv.length >= 2) {
+ app.removeAsDefaultProtocolClient('electron-vue-template')
+ console.log('由于框架特殊性开发环境下无法使用')
+ }
+} else {
+ app.setAsDefaultProtocolClient('electron-vue-template')
+}
diff --git a/src/main/server/index.ts b/src/main/server/index.ts
new file mode 100644
index 0000000..7318c62
--- /dev/null
+++ b/src/main/server/index.ts
@@ -0,0 +1,68 @@
+/* eslint-disable prefer-promise-reject-errors */
+import app from './server'
+import config from '@config/index'
+import { createServer, Server } from 'http';
+const port = config.BuiltInServerPort
+
+class SingleServer {
+ constructor(app: any) {
+ app.set('port', port)
+ this.server = createServer(app);
+ this.server.keepAliveTimeout = 0;
+ this.server.on('connection', (socket) => {
+ // keep-alive 1s后自动关闭
+ socket.setTimeout(1000);
+ })
+ }
+ server: Server
+ statrServer() {
+ return new Promise((resolve, reject) => {
+ try {
+ this.server.listen(port);
+ resolve("服务端已经启动");
+ } catch (error) {
+ switch (error.code) {
+ case "ERR_SERVER_ALREADY_LISTEN":
+ resolve("服务端已经启动")
+ break;
+ case "EACCES":
+ reject("权限不足内置服务器启动失败,请使用管理员权限运行。")
+ break;
+ case "EADDRINUSE":
+ reject("内置服务器端口已被占用,请检查。")
+ break;
+ default:
+ reject(error)
+ }
+ }
+ })
+ }
+ stopServer() {
+ return new Promise((resolve, reject) => {
+ this.server.close((err) => {
+ if (err) {
+ switch ((err as any).code) {
+ case "ERR_SERVER_NOT_RUNNING":
+ resolve("服务端未启动")
+ break;
+ default:
+ reject(err)
+ }
+ } else {
+ resolve(1)
+ }
+ });
+ })
+ }
+}
+
+const singleServer = new SingleServer(app);
+
+export default {
+ StatrServer() {
+ return singleServer.statrServer();
+ },
+ StopServer() {
+ return singleServer.stopServer();
+ }
+}
diff --git a/src/main/server/server.ts b/src/main/server/server.ts
new file mode 100644
index 0000000..96fa658
--- /dev/null
+++ b/src/main/server/server.ts
@@ -0,0 +1,14 @@
+import express from 'express'
+const app = express()
+
+app.get('/message', (req, res) => {
+ res.send('这是来自node服务端的信息')
+})
+
+app.post('/message', (req, res) => {
+ if (req) {
+ res.send(req + '--来自node')
+ }
+})
+
+export default app
diff --git a/src/main/services/HotUpdater.ts b/src/main/services/HotUpdater.ts
new file mode 100644
index 0000000..79cbc99
--- /dev/null
+++ b/src/main/services/HotUpdater.ts
@@ -0,0 +1,93 @@
+/**
+ * power by biuuu
+ */
+
+import { emptyDir, createWriteStream, readFile, copy, remove } from 'fs-extra'
+import { join, resolve } from 'path'
+import { promisify } from 'util'
+import { pipeline } from 'stream'
+import { app, BrowserWindow } from 'electron'
+import { gt } from 'semver'
+import { createHmac } from 'crypto'
+import extract from 'extract-zip'
+import { version } from '../../../package.json'
+import { hotPublishConfig } from '../config/hotPublish'
+import axios from 'axios'
+
+const streamPipeline = promisify(pipeline)
+const appPath = app.getAppPath()
+const updatePath = resolve(appPath, '..', '..', 'update')
+const request = axios.create()
+
+/**
+ * @param data 文件流
+ * @param type 类型,默认sha256
+ * @param key 密钥,用于匹配计算结果
+ * @returns {string} 计算结果
+ * @author umbrella22
+ * @date 2021-03-05
+ */
+function hash(data, type = 'sha256', key = 'Sky') {
+ const hmac = createHmac(type, key)
+ hmac.update(data)
+ return hmac.digest('hex')
+}
+
+
+/**
+ * @param url 下载地址
+ * @param filePath 文件存放地址
+ * @returns {void}
+ * @author umbrella22
+ * @date 2021-03-05
+ */
+async function download(url: string, filePath: string) {
+ const res = await request({ url, responseType: "stream" })
+ await streamPipeline(res.data, createWriteStream(filePath))
+}
+
+const updateInfo = {
+ status: 'init',
+ message: ''
+}
+
+/**
+ * @param windows 指主窗口
+ * @returns {void}
+ * @author umbrella22
+ * @date 2021-03-05
+ */
+export const updater = async (windows?: BrowserWindow) => {
+ try {
+ const res = await request({ url: `${hotPublishConfig.url}/${hotPublishConfig.configName}.json?time=${new Date().getTime()}`, })
+ if (!gt(res.data.version, version)) return
+
+ await emptyDir(updatePath)
+ const filePath = join(updatePath, res.data.name)
+ updateInfo.status = 'downloading'
+ if (windows) windows.webContents.send('hot-update-status', updateInfo);
+ await download(`${hotPublishConfig.url}/${res.data.name}`, filePath);
+ const buffer = await readFile(filePath)
+ const sha256 = hash(buffer)
+ if (sha256 !== res.data.hash) throw new Error('sha256 error')
+ const appPathTemp = join(updatePath, 'temp')
+ await extract(filePath, { dir: appPathTemp })
+ updateInfo.status = 'moving'
+ if (windows) windows.webContents.send('hot-update-status', updateInfo);
+ await remove(join(`${appPath}`, 'dist'));
+ await remove(join(`${appPath}`, 'package.json'));
+ await copy(appPathTemp, appPath)
+ updateInfo.status = 'finished'
+ if (windows) windows.webContents.send('hot-update-status', updateInfo);
+ resolve('success')
+
+
+
+ } catch (error) {
+ updateInfo.status = 'failed'
+ updateInfo.message = error
+ if (windows) windows.webContents.send('hot-update-status', updateInfo)
+ }
+}
+
+export const getUpdateInfo = () => updateInfo
\ No newline at end of file
diff --git a/src/main/services/HotUpdaterTest.ts b/src/main/services/HotUpdaterTest.ts
new file mode 100644
index 0000000..320c1cb
--- /dev/null
+++ b/src/main/services/HotUpdaterTest.ts
@@ -0,0 +1,40 @@
+import { app, BrowserWindow } from "electron";
+import { updateElectron, UpdateInfo, UpdateJson } from "electron_updater_node_core"
+import { dirname, join } from "path";
+import { version } from '../../../package.json'
+import { Readable } from "stream";
+import axios from 'axios'
+const request = axios.create()
+import updateConfig from "../../../updateConfig.json";
+/**
+ * 增量更新
+ *
+ * @export
+ * @param {BrowserWindow} [windows]
+ */
+export async function updater(windows?: BrowserWindow) {
+ const statusCallback = (status: UpdateInfo) => {
+ if (windows) windows.webContents.send('hot-update-status', status);
+ }
+ const downloadFn = async (url: string):Promise => {
+ const response = await request({
+ method: 'get',
+ url: url,
+ responseType: 'stream',
+ });
+ return response.data;
+ }
+ const dirDirectory = join(app.getAppPath(), '..', '..');
+ const tempDirectory = join(dirDirectory, updateConfig.tempDirectory);
+ try {
+ const res = await request({ url: `${updateConfig.url}/${updateConfig.updateJsonName}.json?time=${new Date().getTime()}`, })
+ const updateJson: UpdateJson = res.data;
+ return await updateElectron(statusCallback, updateConfig.updaterName || "updater", version, app.getPath('exe'), tempDirectory, updateConfig.updateJsonName, updateJson, `${updateConfig.url}/${updateConfig.target + updateJson.version}`, downloadFn);
+ } catch (error) {
+ console.log(error);
+ const updateInfo = new UpdateInfo();
+ updateInfo.status = 'failed'
+ updateInfo.message = error
+ if (windows) windows.webContents.send('hot-update-status', updateInfo)
+ }
+}
diff --git a/src/main/services/checkupdate.ts b/src/main/services/checkupdate.ts
new file mode 100644
index 0000000..5a5f715
--- /dev/null
+++ b/src/main/services/checkupdate.ts
@@ -0,0 +1,76 @@
+import { autoUpdater } from 'electron-updater'
+import { BrowserWindow } from 'electron'
+/**
+ * -1 检查更新失败 0 正在检查更新 1 检测到新版本,准备下载 2 未检测到新版本 3 下载中 4 下载完成
+ **/
+class Update {
+ public mainWindow: BrowserWindow
+ constructor() {
+ // 设置url
+ autoUpdater.setFeedURL('http://127.0.0.1:25565/')
+
+ // 当更新发生错误的时候触发。
+ autoUpdater.on('error', (err) => {
+ console.log('更新出现错误', err.message)
+ if (err.message.includes('sha512 checksum mismatch')) {
+ this.Message(this.mainWindow, -1, 'sha512校验失败')
+ } else {
+ this.Message(this.mainWindow, -1, '错误信息请看主进程控制台')
+
+ }
+ })
+
+ // 当开始检查更新的时候触发
+ autoUpdater.on('checking-for-update', (event, arg) => {
+ console.log('开始检查更新')
+ this.Message(this.mainWindow, 0)
+ })
+
+ // 发现可更新数据时
+ autoUpdater.on('update-available', (event, arg) => {
+ console.log('有更新')
+ this.Message(this.mainWindow, 1)
+ })
+
+ // 没有可更新数据时
+ autoUpdater.on('update-not-available', (event, arg) => {
+ console.log('没有更新')
+ this.Message(this.mainWindow, 2)
+ })
+
+ // 下载监听
+ autoUpdater.on('download-progress', (progressObj) => {
+ this.Message(this.mainWindow, 3, progressObj)
+ })
+
+ // 下载完成
+ autoUpdater.on('update-downloaded', () => {
+ console.log('下载完成')
+ this.Message(this.mainWindow, 4)
+ })
+
+
+ }
+ // 负责向渲染进程发送信息
+ Message(mainWindow: BrowserWindow, type: Number, data?: String) {
+ const senddata = {
+ state: type,
+ msg: data || ''
+ }
+ mainWindow.webContents.send('UpdateMsg', senddata)
+ }
+
+ // 执行自动更新检查
+ checkUpdate(mainWindow: BrowserWindow) {
+ this.mainWindow = mainWindow
+ autoUpdater.checkForUpdates().catch(err => {
+ console.log('网络连接问题', err)
+ })
+ }
+ // 退出并安装
+ quitAndInstall() {
+ autoUpdater.quitAndInstall()
+ }
+}
+
+export default Update
diff --git a/src/main/services/downloadFile.ts b/src/main/services/downloadFile.ts
new file mode 100644
index 0000000..4c1a1ff
--- /dev/null
+++ b/src/main/services/downloadFile.ts
@@ -0,0 +1,77 @@
+import { app, ipcMain, BrowserWindow, dialog } from 'electron'
+import { join } from 'path'
+import { arch, platform } from 'os'
+import { stat, remove } from 'fs-extra'
+import packageInfo from '../../../package.json'
+
+
+/**
+ *
+ * @description
+ * @returns {void} 下载类
+ * @param {mainWindow} 主窗口
+ * @param {downloadUrl} 下载地址,当未传入时则会使用预先设置好的baseUrl拼接名称
+ * @author Sky
+ * @date 2020-08-12
+ */
+
+class Main {
+
+ public mainWindow: BrowserWindow = null
+ public downloadUrl: string = ""
+ public version: string = packageInfo.version
+ public baseUrl: string = ''
+ public Sysarch: string = arch().includes('64') ? 'win64' : 'win32'
+ public HistoryFilePath = join(app.getPath('downloads'), platform().includes('win32') ? `electron_${this.version}_${this.Sysarch}.exe` : `electron_${this.version}_mac.dmg`)
+
+
+ constructor(mainWindow: BrowserWindow, downloadUrl?: string) {
+ this.mainWindow = mainWindow
+ this.downloadUrl = downloadUrl || platform().includes('win32') ? this.baseUrl + `electron_${this.version}_${this.Sysarch}.exe?${new Date().getTime()}` : this.baseUrl + `electron_${this.version}_mac.dmg?${new Date().getTime()}`
+ }
+
+ start() {
+ // 更新时检查有无同名文件,若有就删除,若无就开始下载
+ stat(this.HistoryFilePath, async (err, stats) => {
+ try {
+ if (stats) {
+ await remove(this.HistoryFilePath)
+ }
+ this.mainWindow.webContents.downloadURL(this.downloadUrl)
+ } catch (error) { console.log(error) }
+ })
+ this.mainWindow.webContents.session.on('will-download', (event: any, item: any, webContents: any) => {
+ const filePath = join(app.getPath('downloads'), item.getFilename())
+ item.setSavePath(filePath)
+ item.on('updated', (event: any, state: String) => {
+ switch (state) {
+ case 'progressing':
+ this.mainWindow.webContents.send('download-progress', (item.getReceivedBytes() / item.getTotalBytes() * 100).toFixed(0))
+ break
+ default:
+ this.mainWindow.webContents.send('download-error', true)
+ dialog.showErrorBox('下载出错', '由于网络或其他未知原因导致下载出错')
+ break
+ }
+ })
+ item.once('done', (event: any, state: String) => {
+ switch (state) {
+ case 'completed':
+ const data = {
+ filePath
+ }
+ this.mainWindow.webContents.send('download-done', data)
+ break
+ case 'interrupted':
+ this.mainWindow.webContents.send('download-error', true)
+ dialog.showErrorBox('下载出错', '由于网络或其他未知原因导致下载出错.')
+ break
+ default:
+ break
+ }
+ })
+ })
+ }
+}
+
+export default Main
diff --git a/src/main/services/ipcMain.ts b/src/main/services/ipcMain.ts
new file mode 100644
index 0000000..158335b
--- /dev/null
+++ b/src/main/services/ipcMain.ts
@@ -0,0 +1,139 @@
+import { ipcMain, dialog, BrowserWindow, app } from 'electron'
+import config from '@config/index'
+import Server from '../server'
+import { winURL, preloadURL, staticPaths } from '../config/StaticPath'
+import { updater } from './HotUpdater'
+import { updater as updaterTest } from './HotUpdaterTest'
+import DownloadFile from './downloadFile'
+import Update from './checkupdate';
+import { otherWindowConfig } from "../config/windowsConfig"
+import { usePrintHandle } from './printHandle'
+import { UpdateStatus } from 'electron_updater_node_core'
+
+export default {
+ Mainfunc() {
+ usePrintHandle()
+ const allUpdater = new Update();
+ ipcMain.handle('IsUseSysTitle', async () => {
+ return config.IsUseSysTitle
+ })
+ ipcMain.handle('windows-mini', (event, args) => {
+ BrowserWindow.fromWebContents(event.sender)?.minimize()
+ })
+ ipcMain.handle('window-max', async (event, args) => {
+ if (BrowserWindow.fromWebContents(event.sender)?.isMaximized()) {
+ BrowserWindow.fromWebContents(event.sender)?.restore()
+ return { status: false }
+ } else {
+ BrowserWindow.fromWebContents(event.sender)?.maximize()
+ return { status: true }
+ }
+ })
+ ipcMain.handle('window-close', (event, args) => {
+ BrowserWindow.fromWebContents(event.sender)?.close()
+ })
+ ipcMain.handle('check-update', (event) => {
+ allUpdater.checkUpdate(BrowserWindow.fromWebContents(event.sender))
+ })
+ ipcMain.handle('confirm-update', () => {
+ allUpdater.quitAndInstall()
+ })
+ ipcMain.handle('app-close', (event, args) => {
+ app.quit()
+ })
+ ipcMain.handle('get-static-path', (event, args) => {
+ return staticPaths
+ })
+ ipcMain.handle('open-messagebox', async (event, arg) => {
+ const res = await dialog.showMessageBox(BrowserWindow.fromWebContents(event.sender), {
+ type: arg.type || 'info',
+ title: arg.title || '',
+ buttons: arg.buttons || [],
+ message: arg.message || '',
+ noLink: arg.noLink || true
+ })
+ return res
+ })
+ ipcMain.handle('open-errorbox', (event, arg) => {
+ dialog.showErrorBox(
+ arg.title,
+ arg.message
+ )
+ })
+ ipcMain.handle('start-server', async () => {
+ try {
+ const serveStatus = await Server.StatrServer()
+ console.log(serveStatus)
+ return serveStatus
+ } catch (error) {
+ dialog.showErrorBox(
+ '错误',
+ error
+ )
+ }
+ })
+ ipcMain.handle('stop-server', async (event, arg) => {
+ try {
+ const serveStatus = await Server.StopServer()
+ return serveStatus
+ } catch (error) {
+ dialog.showErrorBox(
+ '错误',
+ error
+ )
+ }
+ })
+ ipcMain.handle('hot-update', (event, arg) => {
+ updater(BrowserWindow.fromWebContents(event.sender))
+ })
+ ipcMain.handle('hot-update-test', async (event, arg) => {
+ console.log('hot-update-test')
+ try {
+ let updateInfo = await updaterTest(BrowserWindow.fromWebContents(event.sender));
+ if (updateInfo === UpdateStatus.Success) {
+ app.quit();
+ } else if (updateInfo === UpdateStatus.HaveNothingUpdate) {
+ console.log('不需要更新');
+ } else if (updateInfo === UpdateStatus.Failed) {
+ console.error('更新出错');
+ }
+ } catch (error) {
+ // 更新出错
+ console.error('更新出错');
+ }
+ })
+ ipcMain.handle('start-download', (event, msg) => {
+ new DownloadFile(BrowserWindow.fromWebContents(event.sender), msg.downloadUrl).start()
+ })
+ ipcMain.handle('open-win', (event, arg) => {
+ const ChildWin = new BrowserWindow({
+ titleBarStyle: config.IsUseSysTitle ? 'default' : 'hidden',
+ ...Object.assign(otherWindowConfig, {})
+ })
+ // 开发模式下自动开启devtools
+ if (process.env.NODE_ENV === 'development') {
+ ChildWin.webContents.openDevTools({ mode: 'undocked', activate: true })
+ }
+ ChildWin.loadURL(winURL + `#${arg.url}`)
+ ChildWin.once('ready-to-show', () => {
+ ChildWin.show()
+ if (arg.IsPay) {
+ // 检查支付时候自动关闭小窗口
+ const testUrl = setInterval(() => {
+ const Url = ChildWin.webContents.getURL()
+ if (Url.includes(arg.PayUrl)) {
+ ChildWin.close()
+ }
+ }, 1200)
+ ChildWin.on('close', () => {
+ clearInterval(testUrl)
+ })
+ }
+ })
+ // 渲染进程显示时触发
+ ChildWin.once("show", () => {
+ ChildWin.webContents.send('send-data-test', arg.sendData)
+ })
+ })
+ }
+}
diff --git a/src/main/services/printHandle.ts b/src/main/services/printHandle.ts
new file mode 100644
index 0000000..3755150
--- /dev/null
+++ b/src/main/services/printHandle.ts
@@ -0,0 +1,41 @@
+import { BrowserWindow, ipcMain, WebContentsPrintOptions } from 'electron'
+import config from '@config/index'
+import { otherWindowConfig } from "../config/windowsConfig"
+import { printURL } from '@main/config/StaticPath'
+
+export function usePrintHandle() {
+ ipcMain.handle('getPrinters', async event => {
+ return await event.sender.getPrintersAsync()
+ })
+
+ ipcMain.handle('printHandlePrint', async (event, options: WebContentsPrintOptions) => {
+ return new Promise(resolve => {
+ event.sender.print(options, (success: boolean, failureReason: string) => {
+ resolve({ success, failureReason })
+ })
+ })
+ })
+
+ ipcMain.handle('openPrintDemoWindow', () => {
+ openPrintDemoWindow()
+ })
+}
+
+let win: BrowserWindow
+export function openPrintDemoWindow() {
+ if (win) {
+ win.show()
+ return
+ }
+ win = new BrowserWindow({
+ titleBarStyle: config.IsUseSysTitle ? 'default' : 'hidden',
+ ...Object.assign(otherWindowConfig, {})
+ })
+ win.loadURL(printURL)
+ win.on('ready-to-show', () => {
+ win.show()
+ })
+ win.on('closed', () => {
+ win = null
+ })
+}
\ No newline at end of file
diff --git a/src/main/services/windowManager.ts b/src/main/services/windowManager.ts
new file mode 100644
index 0000000..cefb610
--- /dev/null
+++ b/src/main/services/windowManager.ts
@@ -0,0 +1,186 @@
+import setIpc from './ipcMain'
+import config from '@config/index'
+import menuconfig from '../config/menu'
+import { app, BrowserWindow, Menu, dialog } from 'electron'
+import { winURL, loadingURL } from '../config/StaticPath'
+import { mainWindowConfig } from "../config/windowsConfig"
+
+class MainInit {
+
+ public winURL: string = ''
+ public shartURL: string = ''
+ public loadWindow: BrowserWindow = null
+ public mainWindow: BrowserWindow = null
+
+ constructor() {
+ this.winURL = winURL
+ this.shartURL = loadingURL
+ if (process.env.NODE_ENV === 'development') {
+ menuconfig.push({
+ label: '开发者设置',
+ submenu: [{
+ label: '切换到开发者模式',
+ accelerator: 'CmdOrCtrl+I',
+ role: 'toggledevtools'
+ }]
+ })
+ }
+ // 启用协议
+ setIpc.Mainfunc()
+ }
+ // 主窗口函数
+ createMainWindow() {
+ this.mainWindow = new BrowserWindow({
+ titleBarStyle: config.IsUseSysTitle ? 'default' : 'hidden',
+ ...Object.assign(mainWindowConfig, {})
+ })
+ // 赋予模板
+ const menu = Menu.buildFromTemplate(menuconfig as any)
+ // 加载模板
+ Menu.setApplicationMenu(menu)
+ // 加载主窗口
+ this.mainWindow.loadURL(this.winURL)
+ // ready-to-show之后显示界面
+ this.mainWindow.once('ready-to-show', () => {
+ this.mainWindow.show()
+ // 开发模式下自动开启devtools
+ if (process.env.NODE_ENV === 'development') {
+ this.mainWindow.webContents.openDevTools({ mode: 'undocked', activate: true })
+ }
+ if (config.UseStartupChart) this.loadWindow.destroy()
+ })
+ // 当确定渲染进程卡死时,分类型进行告警操作
+ app.on('render-process-gone', (event, webContents, details) => {
+ const message = {
+ title: "",
+ buttons: [],
+ message: '',
+ }
+ switch (details.reason) {
+ case 'crashed':
+ message.title = "警告"
+ message.buttons = ['确定', '退出']
+ message.message = "图形化进程崩溃,是否进行软重启操作?"
+ break;
+ case 'killed':
+ message.title = "警告"
+ message.buttons = ['确定', '退出']
+ message.message = "由于未知原因导致图形化进程被终止,是否进行软重启操作?"
+ break;
+ case 'oom':
+ message.title = "警告"
+ message.buttons = ['确定', '退出']
+ message.message = "内存不足,是否软重启释放内存?"
+ break;
+
+ default:
+ break;
+ }
+ dialog.showMessageBox(this.mainWindow, {
+ type: 'warning',
+ title: message.title,
+ buttons: message.buttons,
+ message: message.message,
+ noLink: true
+ }).then(res => {
+ if (res.response === 0) this.mainWindow.reload()
+ else this.mainWindow.close()
+ })
+ })
+ // 不知道什么原因,反正就是这个窗口里的页面触发了假死时执行
+ this.mainWindow.on('unresponsive', () => {
+ dialog.showMessageBox(this.mainWindow, {
+ type: 'warning',
+ title: '警告',
+ buttons: ['重载', '退出'],
+ message: '图形化进程失去响应,是否等待其恢复?',
+ noLink: true
+ }).then(res => {
+ if (res.response === 0) this.mainWindow.reload()
+ else this.mainWindow.close()
+ })
+ })
+ /**
+ * 新的gpu崩溃检测,详细参数详见:http://www.electronjs.org/docs/api/app
+ * @returns {void}
+ * @author zmr (umbrella22)
+ * @date 2020-11-27
+ */
+ app.on('child-process-gone', (event, details) => {
+ const message = {
+ title: "",
+ buttons: [],
+ message: '',
+ }
+ switch (details.type) {
+ case 'GPU':
+ switch (details.reason) {
+ case 'crashed':
+ message.title = "警告";
+ message.buttons = ['确定', '退出'];
+ message.message = "硬件加速进程已崩溃,是否关闭硬件加速并重启?";
+ break;
+ case 'killed':
+ message.title = "警告";
+ message.buttons = ['确定', '退出'];
+ message.message = "硬件加速进程被意外终止,是否关闭硬件加速并重启?";
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ dialog.showMessageBox(this.mainWindow, {
+ type: 'warning',
+ title: message.title,
+ buttons: message.buttons,
+ message: message.message,
+ noLink: true
+ }).then(res => {
+ // 当显卡出现崩溃现象时使用该设置禁用显卡加速模式。
+ if (res.response === 0) {
+ if (details.type === 'GPU') app.disableHardwareAcceleration();
+ this.mainWindow.reload()
+ } else {
+ this.mainWindow.close()
+ }
+ })
+ })
+ this.mainWindow.on('closed', () => {
+ this.mainWindow = null
+ })
+ }
+ // 加载窗口函数
+ loadingWindow(loadingURL: string) {
+ this.loadWindow = new BrowserWindow({
+ width: 400,
+ height: 600,
+ // frame: false,
+ skipTaskbar: true,
+ transparent: true,
+ resizable: false,
+ webPreferences: { experimentalFeatures: true }
+ })
+
+ this.loadWindow.loadURL(loadingURL)
+ this.loadWindow.show()
+ this.loadWindow.setAlwaysOnTop(true)
+ // 延迟两秒可以根据情况后续调快,= =,就相当于个,sleep吧,就那种。 = =。。。
+ setTimeout(() => {
+ this.createMainWindow()
+ }, 1500)
+ }
+ // 初始化窗口函数
+ initWindow() {
+ if (config.UseStartupChart) {
+ return this.loadingWindow(this.shartURL)
+ } else {
+ return this.createMainWindow()
+ }
+
+ }
+}
+export default MainInit
diff --git a/src/renderer/App.vue b/src/renderer/App.vue
new file mode 100644
index 0000000..59f62a2
--- /dev/null
+++ b/src/renderer/App.vue
@@ -0,0 +1,312 @@
+
+
+
+
+
+
+
diff --git a/src/renderer/assets/404_images/404.png b/src/renderer/assets/404_images/404.png
new file mode 100644
index 0000000..3d8e230
Binary files /dev/null and b/src/renderer/assets/404_images/404.png differ
diff --git a/src/renderer/assets/404_images/404_cloud.png b/src/renderer/assets/404_images/404_cloud.png
new file mode 100644
index 0000000..c6281d0
Binary files /dev/null and b/src/renderer/assets/404_images/404_cloud.png differ
diff --git a/src/renderer/assets/icons/svg/close.svg b/src/renderer/assets/icons/svg/close.svg
new file mode 100644
index 0000000..32de3b6
--- /dev/null
+++ b/src/renderer/assets/icons/svg/close.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/assets/icons/svg/electron-logo.svg b/src/renderer/assets/icons/svg/electron-logo.svg
new file mode 100644
index 0000000..1d7ca60
--- /dev/null
+++ b/src/renderer/assets/icons/svg/electron-logo.svg
@@ -0,0 +1,529 @@
+
+
+
diff --git a/src/renderer/assets/icons/svg/mini.svg b/src/renderer/assets/icons/svg/mini.svg
new file mode 100644
index 0000000..0268802
--- /dev/null
+++ b/src/renderer/assets/icons/svg/mini.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/assets/icons/svg/mix.svg b/src/renderer/assets/icons/svg/mix.svg
new file mode 100644
index 0000000..b88133f
--- /dev/null
+++ b/src/renderer/assets/icons/svg/mix.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/assets/icons/svg/reduction.svg b/src/renderer/assets/icons/svg/reduction.svg
new file mode 100644
index 0000000..c219e30
--- /dev/null
+++ b/src/renderer/assets/icons/svg/reduction.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/assets/logo.png b/src/renderer/assets/logo.png
new file mode 100644
index 0000000..63736e2
Binary files /dev/null and b/src/renderer/assets/logo.png differ
diff --git a/src/renderer/components/mars-work/mars-map.vue b/src/renderer/components/mars-work/mars-map.vue
new file mode 100644
index 0000000..431a918
--- /dev/null
+++ b/src/renderer/components/mars-work/mars-map.vue
@@ -0,0 +1,222 @@
+
+
+
+
+
+
diff --git a/src/renderer/error.ts b/src/renderer/error.ts
new file mode 100644
index 0000000..ebf748f
--- /dev/null
+++ b/src/renderer/error.ts
@@ -0,0 +1,20 @@
+import type { App } from 'vue'
+import { nextTick } from "vue"
+export const errorHandler = (App: App) => {
+ App.config.errorHandler = (err, vm, info) => {
+ nextTick(() => {
+ if (process.env.NODE_ENV === 'development') {
+ console.group('%c >>>>>> 错误信息 >>>>>>', 'color:red')
+ console.log(`%c ${info}`, 'color:blue')
+ console.groupEnd()
+ console.group('%c >>>>>> 发生错误的Vue 实例对象 >>>>>>', 'color:green')
+ console.log(vm)
+ console.groupEnd()
+ console.group('%c >>>>>> 发生错误的原因及位置 >>>>>>', 'color:red')
+ console.error(err)
+ console.groupEnd()
+ }
+ })
+ }
+
+}
\ No newline at end of file
diff --git a/src/renderer/index.css b/src/renderer/index.css
new file mode 100644
index 0000000..3987869
--- /dev/null
+++ b/src/renderer/index.css
@@ -0,0 +1,20 @@
+body,
+html {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+}
+
+#app {
+ font-family: Avenir, Helvetica, Arial, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ /* text-align: center; */
+ color: #2c3e50;
+ /* margin-top: 60px; */
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/src/renderer/index.html b/src/renderer/index.html
new file mode 100644
index 0000000..9258945
--- /dev/null
+++ b/src/renderer/index.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+ Mars3D三维地球 桌面程序
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/renderer/main.ts b/src/renderer/main.ts
new file mode 100644
index 0000000..2abb8e2
--- /dev/null
+++ b/src/renderer/main.ts
@@ -0,0 +1,10 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+import './index.css'
+import { errorHandler } from './error'
+
+const app = createApp(App)
+errorHandler(app)
+
+app.mount("#app")
+
diff --git a/static/config/config.json b/static/config/config.json
new file mode 100644
index 0000000..91564c4
--- /dev/null
+++ b/static/config/config.json
@@ -0,0 +1,1103 @@
+{
+ "map3d": {
+ "scene": {
+ "center": { "lat": 31.686288, "lng": 117.229619, "alt": 11333.9, "heading": 359.2, "pitch": -39.5 },
+ "scene3DOnly": false,
+ "shadows": false,
+ "removeDblClick": true,
+ "sceneMode": 3,
+ "showSun": true,
+ "showMoon": true,
+ "showSkyBox": true,
+ "showSkyAtmosphere": true,
+ "fog": true,
+ "fxaa": true,
+ "requestRenderMode": true,
+ "globe": {
+ "depthTestAgainstTerrain": false,
+ "baseColor": "#546a53",
+ "showGroundAtmosphere": true,
+ "enableLighting": false
+ },
+ "cameraController": {
+ "zoomFactor": 3.0,
+ "minimumZoomDistance": 1,
+ "maximumZoomDistance": 50000000,
+ "enableRotate": true,
+ "enableTranslate": true,
+ "enableTilt": true,
+ "enableZoom": true,
+ "enableCollisionDetection": true,
+ "minimumCollisionTerrainHeight": 15000
+ }
+ },
+ "control": {
+ "homeButton": true,
+ "baseLayerPicker": true,
+ "sceneModePicker": true,
+ "vrButton": false,
+ "fullscreenButton": true,
+ "navigationHelpButton": true,
+ "animation": false,
+ "timeline": false,
+ "infoBox": false,
+ "geocoder": false,
+ "selectionIndicator": false,
+
+ "defaultContextMenu": true,
+ "mouseDownView": true,
+ "zoom": { "insertIndex": 1 },
+ "compass": { "bottom": "toolbar", "left": "5px" },
+ "distanceLegend": { "left": "100px", "bottom": "2px" },
+ "locationBar": {
+ "fps": true,
+ "crs": "CGCS2000_GK_Zone_3",
+ "crsDecimal": 0,
+ "template": "经度:{lng}
纬度:{lat}
横{crsx} 纵{crsy}
海拔:{alt}米
层级:{level}
方向:{heading}°
俯仰角:{pitch}°
视高:{cameraHeight}米
"
+ }
+ },
+ "templateValues": {
+ "dataServer": "//data.mars3d.cn",
+ "gltfServerUrl": "//data.mars3d.cn/gltf"
+ },
+ "terrain": {
+ "url": "//data.mars3d.cn/terrain",
+ "show": true
+ },
+ "basemaps": [
+ { "id": 10, "name": "地图底图", "type": "group" },
+ {
+ "id": 2021,
+ "pid": 10,
+ "name": "天地图影像",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/tdt_img.png",
+ "type": "group",
+ "layers": [
+ { "name": "底图", "type": "tdt", "layer": "img_d" },
+ { "name": "注记", "type": "tdt", "layer": "img_z" }
+ ],
+ "show": true
+ },
+ {
+ "pid": 10,
+ "name": "天地图电子",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/tdt_vec.png",
+ "type": "group",
+ "layers": [
+ { "name": "底图", "type": "tdt", "layer": "vec_d" },
+ { "name": "注记", "type": "tdt", "layer": "vec_z" }
+ ]
+ },
+ {
+ "pid": 10,
+ "name": "高德影像",
+ "type": "group",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/gaode_img.png",
+ "layers": [
+ { "name": "底图", "type": "gaode", "layer": "img_d" },
+ { "name": "注记", "type": "gaode", "layer": "img_z" }
+ ]
+ },
+ {
+ "pid": 10,
+ "name": "高德电子",
+ "type": "gaode",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/gaode_vec.png",
+ "layer": "vec"
+ },
+ {
+ "pid": 10,
+ "name": "百度影像",
+ "type": "group",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/bd-img.png",
+ "layers": [
+ { "name": "底图", "type": "baidu", "layer": "img_d" },
+ { "name": "注记", "type": "baidu", "layer": "img_z" }
+ ]
+ },
+ {
+ "pid": 10,
+ "name": "百度电子",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/bd-vec.png",
+ "type": "baidu",
+ "layer": "vec"
+ },
+ {
+ "pid": 10,
+ "name": "腾讯影像",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/gaode_img.png",
+ "type": "group",
+ "layers": [
+ { "name": "底图", "type": "tencent", "layer": "img_d" },
+ { "name": "注记", "type": "tencent", "layer": "img_z" }
+ ]
+ },
+ {
+ "pid": 10,
+ "name": "腾讯电子",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/gaode_vec.png",
+ "type": "tencent",
+ "layer": "vec"
+ },
+ {
+ "pid": 10,
+ "name": "ArcGIS影像",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/esriWorldImagery.png",
+ "type": "xyz",
+ "url": "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
+ "enablePickFeatures": false
+ },
+ {
+ "pid": 10,
+ "name": "微软影像",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/bingAerial.png",
+ "type": "bing",
+ "layer": "Aerial"
+ },
+ {
+ "pid": 10,
+ "name": "OSM地图",
+ "type": "xyz",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/osm.png",
+ "url": "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
+ "subdomains": "abc"
+ },
+ {
+ "id": 2017,
+ "pid": 10,
+ "name": "暗色底图",
+ "type": "gaode",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/blackMarble.png",
+ "layer": "vec",
+ "invertColor": true,
+ "filterColor": "#4e70a6",
+ "brightness": 0.6,
+ "contrast": 1.8,
+ "gamma": 0.3,
+ "hue": 1,
+ "saturation": 0
+ },
+ {
+ "pid": 10,
+ "name": "蓝色底图",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/bd-c-midnight.png",
+ "type": "xyz",
+ "url": "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}",
+ "chinaCRS": "GCJ02",
+ "enablePickFeatures": false
+ },
+ {
+ "pid": 10,
+ "name": "黑色底图",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/bd-c-dark.png",
+ "type": "tencent",
+ "layer": "custom",
+ "style": "4"
+ },
+ {
+ "pid": 10,
+ "name": "离线地图 (供参考)",
+ "type": "group",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/google_img.png",
+ "layers": [
+ {
+ "name": "全球",
+ "type": "xyz",
+ "url": "{dataServer}/tile/googleImg/{z}/{x}/{y}.jpg",
+ "minimumLevel": 0,
+ "maximumLevel": 9
+ },
+ {
+ "name": "中国",
+ "type": "xyz",
+ "url": "{dataServer}/tile/googleImg/{z}/{x}/{y}.jpg",
+ "minimumTerrainLevel": 10,
+ "minimumLevel": 10,
+ "maximumLevel": 12,
+ "rectangle": { "xmin": 69.706929, "xmax": 136.560941, "ymin": 15.831038, "ymax": 52.558005 }
+ },
+ {
+ "name": "具体项目(如合肥)",
+ "type": "xyz",
+ "url": "{dataServer}/tile/googleImg/{z}/{x}/{y}.jpg",
+ "minimumTerrainLevel": 12,
+ "minimumLevel": 12,
+ "maximumLevel": 18,
+ "rectangle": { "xmin": 116.33236, "xmax": 118.183557, "ymin": 31.143784, "ymax": 32.565035 }
+ }
+ ]
+ },
+ {
+ "pid": 10,
+ "name": "单张图片 (本地离线)",
+ "icon": "https://muyao1987.gitee.io/cdn/mars3d.cn/img/basemaps/offline.png",
+ "type": "image",
+ "url": "//data.mars3d.cn/file/img/world/world.jpg"
+ }
+ ],
+ "layers": [
+ { "id": 50, "name": "辅助图层", "type": "group" },
+ { "pid": 50, "type": "graticule", "name": "经纬网" },
+ {
+ "pid": 50,
+ "name": "行政区划界线",
+ "type": "tdt",
+ "url": "https://t{s}.tianditu.gov.cn/DataServer?T=ibo_w&x={x}&y={y}&l={z}",
+ "maximumLevel": 10,
+ "mapSplit": false
+ },
+ {
+ "pid": 50,
+ "name": "高德实时路况",
+ "type": "gaode",
+ "layer": "time",
+ "minimumTerrainLevel": 4,
+ "minimumLevel": 4,
+ "proxy": "//server.mars3d.cn/proxy/",
+ "mapSplit": false
+ },
+ {
+ "pid": 50,
+ "name": "百度实时路况",
+ "type": "baidu",
+ "layer": "time",
+ "mapSplit": false
+ },
+
+ { "id": 60, "name": "地形", "type": "group" },
+ { "pid": 60, "type": "terrain", "name": "Cesium地形", "terrain": { "type": "ion" }, "radio": true },
+ { "pid": 60, "type": "terrain", "name": "Mars3D地形", "terrain": { "type": "xyz", "url": "{dataServer}/terrain" }, "radio": true },
+ {
+ "pid": 60,
+ "type": "terrain",
+ "name": "ArcGIS地形",
+ "terrain": { "type": "arcgis", "url": "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer" },
+ "radio": true
+ },
+ { "pid": 60, "type": "terrain", "name": "无地形", "terrain": { "type": "none" }, "radio": true },
+
+ { "id": 40, "name": "栅格数据", "type": "group" },
+ { "id": 4020, "pid": 40, "name": "OGC WMS服务", "type": "group" },
+ {
+ "pid": 4020,
+ "name": "教育设施点",
+ "type": "wms",
+ "url": "//server.mars3d.cn/geoserver/mars/wms",
+ "layers": "mars:hfjy",
+ "crs": "EPSG:4326",
+ "parameters": { "transparent": "true", "format": "image/png" },
+ "popup": "名称:{项目名称}
类型:{设施类型}
面积:{用地面积}亩
位置:{具体位置}",
+ "mapSplit": false,
+ "show": false,
+ "flyTo": true
+ },
+ {
+ "pid": 4020,
+ "name": "道路线",
+ "type": "wms",
+ "url": "//server.mars3d.cn/geoserver/mars/wms",
+ "layers": "mars:hfdl",
+ "crs": "EPSG:4326",
+ "parameters": { "transparent": "true", "format": "image/png" },
+ "center": { "lat": 31.743214, "lng": 117.277097, "alt": 47197.7, "heading": 0.3, "pitch": -78.8 },
+ "popup": "all",
+ "mapSplit": false,
+ "show": false,
+ "flyTo": true
+ },
+ {
+ "pid": 4020,
+ "name": "建筑物面",
+ "type": "wms",
+ "url": "//server.mars3d.cn/geoserver/mars/wms",
+ "layers": "mars:hfjzw",
+ "crs": "EPSG:4326",
+ "parameters": { "transparent": "true", "format": "image/png" },
+ "highlight": {
+ "showTime": 5000,
+ "fill": true,
+ "color": "#2deaf7",
+ "opacity": 0.6,
+ "outline": true,
+ "outlineWidth": 3,
+ "outlineColor": "#e000d9",
+ "outlineOpacity": 1.0,
+ "clampToGround": true
+ },
+ "center": { "lat": 31.79513, "lng": 117.236172, "alt": 3784.6, "heading": 0.7, "pitch": -42.2 },
+ "popup": "all",
+ "show": false,
+ "flyTo": true
+ },
+ {
+ "pid": 4020,
+ "name": "规划面",
+ "type": "wms",
+ "url": "//server.mars3d.cn/geoserver/mars/wms",
+ "layers": "mars:hfgh",
+ "crs": "EPSG:4326",
+ "parameters": { "transparent": "true", "format": "image/png" },
+ "center": { "lat": 31.743214, "lng": 117.277097, "alt": 47197.7, "heading": 0.3, "pitch": -78.8 },
+ "popup": "all",
+ "show": false,
+ "flyTo": true
+ },
+ { "id": 4030, "pid": 40, "name": "ArcGIS 瓦片", "type": "group" },
+ {
+ "pid": 4030,
+ "name": "合肥规划图",
+ "type": "arcgis_cache",
+ "url": "{dataServer}/arcgis_cache/hfgh/_alllayers/{z}/{y}/{x}.png",
+ "minimumLevel": 1,
+ "maximumLevel": 17,
+ "minimumTerrainLevel": 1,
+ "maximumTerrainLevel": 17,
+ "rectangle": { "xmin": 116.846, "xmax": 117.642, "ymin": 31.533, "ymax": 32.185 }
+ },
+ { "id": 4010, "pid": 40, "name": "ArcGIS Dynamic", "type": "group" },
+ {
+ "id": 401085,
+ "pid": 4010,
+ "type": "arcgis",
+ "name": "主要道路",
+ "url": "//server.mars3d.cn/arcgis/rest/services/mars/hefei/MapServer",
+ "layers": "24",
+ "highlight": { "type": "polyline", "color": "#2deaf7", "width": 4, "clampToGround": true },
+ "center": { "lat": 31.814176, "lng": 117.225362, "alt": 5105.3, "heading": 359.2, "pitch": -83.1 },
+ "popup": "all",
+ "onWidget": "layer-picture-heatmap",
+ "mapSplit": false
+ },
+ {
+ "id": 401086,
+ "pid": 4010,
+ "type": "arcgis",
+ "name": "建筑物",
+ "url": "//server.mars3d.cn/arcgis/rest/services/mars/hefei/MapServer",
+ "layers": "35,36,37,39",
+ "highlight": {
+ "fill": true,
+ "color": "#2deaf7",
+ "opacity": 0.6,
+ "outline": true,
+ "outlineWidth": 3,
+ "outlineColor": "#e000d9",
+ "outlineOpacity": 1.0,
+ "clampToGround": true
+ },
+ "center": { "lat": 31.816951, "lng": 117.22898, "alt": 2916.7, "heading": 0.3, "pitch": -78.8 },
+ "popup": "名称:{NAME}
层数:{floor}",
+ "onWidget": "layer-picture-heatmap"
+ },
+ {
+ "id": 401087,
+ "pid": 4010,
+ "type": "arcgis",
+ "name": "规划",
+ "url": "//server.mars3d.cn/arcgis/rest/services/mars/guihua/MapServer",
+ "highlight": {
+ "showTime": 5000,
+ "fill": true,
+ "color": "#2deaf7",
+ "opacity": 0.6,
+ "outline": true,
+ "outlineWidth": 3,
+ "outlineColor": "#e000d9",
+ "outlineOpacity": 1.0,
+ "clampToGround": true
+ },
+ "center": { "lat": 31.816951, "lng": 117.22898, "alt": 2916.7, "heading": 0.3, "pitch": -78.8 },
+ "popup": [
+ { "field": "用地名称", "name": "名称" },
+ { "field": "用地编号", "name": "编号" },
+ { "field": "规划用地", "name": "规划" },
+ { "type": "html", "html": "数据仅供参考
" }
+ ],
+ "popupNoTitle": true,
+ "onWidget": "layer-picture-guihua"
+ },
+ { "id": 30, "name": "矢量数据", "type": "group" },
+ { "id": 3040, "pid": 30, "name": "平台标绘JSON", "type": "group" },
+ {
+ "id": 303011,
+ "pid": 3040,
+ "type": "geojson",
+ "name": "示例数据",
+ "url": "{dataServer}/file/geojson/mars3d-draw.json",
+ "popup": "{type}{name}",
+ "show": false,
+ "flyTo": true
+ },
+ { "id": 3030, "pid": 30, "name": "GeoJSON数据", "type": "group" },
+ {
+ "pid": 3030,
+ "type": "geojson",
+ "name": "用地规划",
+ "url": "{dataServer}/file/geojson/guihua.json",
+ "symbol": {
+ "styleOptions": { "opacity": 0.6, "color": "#0000FF", "width": 3, "clampToGround": true },
+ "styleField": "类型",
+ "styleFieldOptions": {
+ "一类居住用地": { "color": "#FFDF7F" },
+ "二类居住用地": { "color": "#FFFF00" },
+ "社区服务用地": { "color": "#FF6A38" },
+ "幼托用地": { "color": "#FF6A38" },
+ "商住混合用地": { "color": "#FF850A" },
+ "行政办公用地": { "color": "#FF00FF" },
+ "文化设施用地": { "color": "#FF00FF" },
+ "小学用地": { "color": "#FF7FFF" },
+ "初中用地": { "color": "#FF7FFF" },
+ "体育场用地": { "color": "#00A57C" },
+ "医院用地": { "color": "#A5527C" },
+ "社会福利用地": { "color": "#FF7F9F" },
+ "商业用地": { "color": "#FF0000" },
+ "商务用地": { "color": "#7F0000" },
+ "营业网点用地": { "color": "#FF7F7F" },
+ "一类工业用地": { "color": "#A57C52" },
+ "社会停车场用地": { "color": "#C0C0C0" },
+ "通信用地": { "color": "#007CA5" },
+ "排水用地": { "color": "#00BFFF" },
+ "公园绿地": { "color": "#00FF00" },
+ "防护绿地": { "color": "#007F00" },
+ "河流水域": { "color": "#7FFFFF" },
+ "配建停车场": { "color": "#ffffff" },
+ "道路用地": { "color": "#ffffff" }
+ }
+ },
+ "popup": "{类型}",
+ "show": false,
+ "flyTo": true
+ },
+ {
+ "pid": 3030,
+ "type": "geojson",
+ "name": "建筑物面",
+ "url": "{dataServer}/file/geojson/buildings-demo.json",
+ "symbol": { "styleOptions": { "color": "#0d3685", "outlineColor": "#0d3685", "opacity": 0.8 } },
+ "buildings": { "cloumn": "floors", "height": "flo_height" },
+ "popup": "all",
+ "flyTo": true
+ },
+ {
+ "pid": 3030,
+ "type": "geojson",
+ "name": "安徽各市",
+ "url": "{dataServer}/file/geojson/areas/340000_full.json",
+ "symbol": {
+ "type": "polygon",
+ "styleOptions": {
+ "materialType": "PolyGradient",
+ "color": "rgb(15,176,255)",
+ "opacity": 0.7,
+ "alphaPower": 1.3,
+ "diffHeight": "{gdp}",
+ "label": {
+ "text": "{name}",
+ "opacity": 1,
+ "font_size": 25,
+ "color": "#ffffff",
+ "outline": true,
+ "outlineColor": "#000000",
+ "outlineWidth": 3,
+ "scaleByDistance": true,
+ "scaleByDistance_far": 2743804,
+ "scaleByDistance_farValue": 0.3,
+ "scaleByDistance_near": 10000,
+ "scaleByDistance_nearValue": 1,
+ "distanceDisplayCondition": true,
+ "distanceDisplayCondition_far": 2743804,
+ "distanceDisplayCondition_near": 0
+ }
+ }
+ },
+ "popup": "{name}",
+ "show": false,
+ "flyTo": true
+ },
+ {
+ "pid": 3030,
+ "type": "geojson",
+ "name": "中国省界",
+ "url": "{dataServer}/file/geojson/areas/100000_full.json",
+ "symbol": {
+ "type": "polylineP",
+ "styleOptions": {
+ "color": "#ffffff",
+ "width": 2,
+ "opacity": 0.8,
+ "label": {
+ "text": "{name}",
+ "position": "center",
+ "font_size": 30,
+ "color": "#ffffff",
+ "outline": true,
+ "outlineColor": "#000000",
+ "scaleByDistance": true,
+ "scaleByDistance_far": 60000000,
+ "scaleByDistance_farValue": 0.2,
+ "scaleByDistance_near": 1000000,
+ "scaleByDistance_nearValue": 1,
+ "distanceDisplayCondition": true,
+ "distanceDisplayCondition_far": 12000000,
+ "distanceDisplayCondition_near": 0
+ }
+ }
+ },
+ "show": false,
+ "flyTo": true
+ },
+ {
+ "pid": 3030,
+ "type": "geojson",
+ "name": "西藏垭口",
+ "url": "{dataServer}/file/geojson/xizangyakou.json",
+ "symbol": {
+ "styleOptions": {
+ "image": "img/marker/mark1.png",
+ "scale": 1,
+ "scaleByDistance": true,
+ "scaleByDistance_far": 5000000,
+ "scaleByDistance_farValue": 0.5,
+ "scaleByDistance_near": 1000,
+ "scaleByDistance_nearValue": 1,
+ "verticalOrigin": 1,
+ "horizontalOrigin": 0,
+ "clampToGround": true,
+ "label": {
+ "text": "{NAME}",
+ "font_size": 25,
+ "color": "#ffff00",
+ "font_family": "微软雅黑",
+ "outline": true,
+ "outlineColor": "#000000",
+ "pixelOffsetY": -40,
+ "scaleByDistance": true,
+ "scaleByDistance_far": 1000000,
+ "scaleByDistance_farValue": 0.5,
+ "scaleByDistance_near": 1000,
+ "scaleByDistance_nearValue": 1,
+ "distanceDisplayCondition": true,
+ "distanceDisplayCondition_far": 1000000,
+ "distanceDisplayCondition_near": 0,
+ "visibleDepth": true
+ }
+ }
+ },
+ "popup": [
+ { "field": "NAME", "name": "名称" },
+ { "type": "details", "callback": "showPopupDetails", "field": "图片", "className": "mars3d-popup-btn-custom" }
+ ],
+ "show": false,
+ "flyTo": true
+ },
+ {
+ "pid": 3030,
+ "type": "geojson",
+ "name": "体育设施点",
+ "url": "{dataServer}/file/geojson/hfty-point.json",
+ "symbol": {
+ "styleOptions": {
+ "image": "img/marker/mark1.png",
+ "scale": 1,
+ "scaleByDistance": true,
+ "scaleByDistance_far": 20000,
+ "scaleByDistance_farValue": 0.5,
+ "scaleByDistance_near": 1000,
+ "scaleByDistance_nearValue": 1,
+ "verticalOrigin": 1,
+ "horizontalOrigin": 0,
+ "clampToGround": true,
+ "label": {
+ "text": "{项目名称}",
+ "font_size": 25,
+ "color": "#ffffff",
+ "outline": true,
+ "outlineColor": "#000000",
+ "pixelOffsetY": -25,
+ "scaleByDistance": true,
+ "scaleByDistance_far": 80000,
+ "scaleByDistance_farValue": 0.5,
+ "scaleByDistance_near": 1000,
+ "scaleByDistance_nearValue": 1,
+ "distanceDisplayCondition": true,
+ "distanceDisplayCondition_far": 80000,
+ "distanceDisplayCondition_near": 0
+ }
+ }
+ },
+ "popup": [
+ { "field": "项目名称", "name": "项目名称" },
+ { "field": "建设性质", "name": "建设性质" },
+ { "field": "设施级别", "name": "设施级别" },
+ { "field": "所属区县", "name": "所属区县" },
+ { "field": "建筑内容及", "name": "建筑内容" },
+ { "field": "新增用地(", "name": "新增用地" },
+ { "field": "开工", "name": "开工" },
+ { "field": "总投资(万", "name": "总投资" },
+ { "field": "资金来源", "name": "资金来源" },
+ { "field": "初步选址", "name": "初步选址" },
+ { "field": "设施类型", "name": "设施类型" },
+ { "field": "设施等级", "name": "设施等级" },
+ { "field": "所在区县", "name": "所在区县" },
+ { "field": "具体位置", "name": "具体位置" },
+ { "field": "建设内容(", "name": "建设内容" },
+ { "field": "用地面积(", "name": "用地面积", "format": "mars3d.MeasureUtil.formatArea" },
+ { "field": "设施规模(", "name": "设施规模" },
+ { "field": "举办者类型", "name": "举办者类型" },
+ { "field": "开工时间", "name": "开工时间" },
+ { "field": "总投资额(", "name": "总投资额", "unit": "亿元" },
+ { "field": "项目推进主", "name": "项目推进主体" },
+ { "field": "项目进度", "name": "项目进度" },
+ { "field": "项目来源", "name": "项目来源" },
+ { "field": "备注", "name": "备注" }
+ ],
+ "show": false,
+ "flyTo": true
+ },
+ { "id": 3070, "pid": 30, "name": "GeoServer WFS", "type": "group" },
+ {
+ "pid": 3070,
+ "type": "wfs",
+ "name": "建筑物面",
+ "url": "//server.mars3d.cn/geoserver/mars/ows",
+ "layer": "mars:hfjzw",
+ "parameters": { "maxFeatures": 500 },
+ "minimumLevel": 15,
+ "symbol": {
+ "type": "polygonP",
+ "styleOptions": { "color": "#00469c", "outline": false, "opacity": 1 }
+ },
+ "buildings": { "cloumn": "floor" },
+ "center": { "lat": 31.818396, "lng": 117.229083, "alt": 2554.4, "heading": 359.2, "pitch": -83.1 },
+ "popup": "名称:{NAME}
层数:{floor}"
+ },
+ {
+ "pid": 3070,
+ "name": "教育设施点",
+ "type": "wfs",
+ "url": "//server.mars3d.cn/geoserver/mars/ows",
+ "layer": "mars:hfjy",
+ "parameters": { "maxFeatures": 500 },
+ "minimumLevel": 13,
+ "symbol": {
+ "type": "billboardP",
+ "styleOptions": {
+ "image": "img/marker/mark1.png",
+ "scale": 0.7,
+ "scaleByDistance": true,
+ "scaleByDistance_far": 20000,
+ "scaleByDistance_farValue": 0.5,
+ "scaleByDistance_near": 1000,
+ "scaleByDistance_nearValue": 1,
+ "clampToGround": true,
+ "label": {
+ "text": "{项目名称}",
+ "font_size": 15,
+ "color": "#ffffff",
+ "outline": true,
+ "outlineColor": "#000000",
+ "pixelOffsetY": -30,
+ "distanceDisplayCondition": true,
+ "distanceDisplayCondition_far": 2000,
+ "distanceDisplayCondition_near": 0
+ }
+ }
+ },
+ "center": { "lat": 31.812256, "lng": 117.229873, "alt": 4683.91, "heading": 357.4, "pitch": -65.4 },
+ "popup": "all"
+ },
+ { "id": 3010, "pid": 30, "name": "ArcGIS WFS", "type": "group" },
+ {
+ "pid": 3010,
+ "type": "arcgis_wfs",
+ "name": "兴趣点",
+ "url": "//server.mars3d.cn/arcgis/rest/services/mars/hefei/MapServer/1",
+ "where": " 1=1 ",
+ "minimumLevel": 15,
+ "center": { "lat": 31.818396, "lng": 117.229083, "alt": 2554.4, "heading": 359.2, "pitch": -83.1 },
+ "symbol": {
+ "type": "billboardP",
+ "styleOptions": {
+ "image": "img/marker/mark3.png",
+ "scale": 0.7,
+ "scaleByDistance": true,
+ "scaleByDistance_far": 20000,
+ "scaleByDistance_farValue": 0.5,
+ "scaleByDistance_near": 1000,
+ "scaleByDistance_nearValue": 1,
+ "clampToGround": true,
+ "label": {
+ "text": "{NAME}",
+ "font_size": 15,
+ "color": "#ffffff",
+ "outline": true,
+ "outlineColor": "#000000",
+ "pixelOffsetY": -30,
+ "distanceDisplayCondition": true,
+ "distanceDisplayCondition_far": 3000,
+ "distanceDisplayCondition_near": 0
+ }
+ },
+ "styleField": "address",
+ "styleFieldOptions": {
+ "AB03": { "image": "img/marker/mark1.png" },
+ "A980": { "image": "img/marker/mark2.png" },
+ "A900": { "image": "img/marker/mark4.png" }
+ }
+ },
+ "popup": "名称:{NAME}
地址:{address}",
+ "show": false
+ },
+ {
+ "pid": 3010,
+ "type": "arcgis_wfs",
+ "name": "道路",
+ "url": "//server.mars3d.cn/arcgis/rest/services/mars/hefei/MapServer/28",
+ "minimumLevel": 14,
+ "symbol": {
+ "type": "polylineP",
+ "styleOptions": { "color": "#3388ff", "width": 3, "clampToGround": true },
+ "styleField": "NAME",
+ "styleFieldOptions": {
+ "祁门路": { "color": "#8744c0", "width": 3 },
+ "东流路": { "color": "#f7ba2a", "width": 3 },
+ "翡翠路": { "color": "#20a0ff", "width": 3 },
+ "岳西路": { "color": "#50bfff", "width": 3 }
+ }
+ },
+ "popup": "名称:{NAME}",
+ "center": { "lat": 31.814176, "lng": 117.225362, "alt": 5105.3, "heading": 359.2, "pitch": -83.1 }
+ },
+ {
+ "pid": 3010,
+ "type": "arcgis_wfs",
+ "name": "建筑物面",
+ "url": "//server.mars3d.cn/arcgis/rest/services/mars/hefei/MapServer/37",
+ "minimumLevel": 15,
+ "symbol": { "styleOptions": { "color": "#0d3685", "outlineColor": "#0d3685", "opacity": 0.8 } },
+ "buildings": { "cloumn": "floor" },
+ "debuggerTileInfo": false,
+ "center": { "lat": 31.816951, "lng": 117.22898, "alt": 1916.7, "heading": 0.3, "pitch": -78.8 },
+ "popup": "名称:{NAME}
层数:{floor}"
+ },
+ { "id": 3060, "pid": 30, "name": "CZML数据", "type": "group" },
+ {
+ "id": 306010,
+ "pid": 3060,
+ "type": "czml",
+ "name": "汽车",
+ "url": "{dataServer}/file/czml/car.czml",
+ "center": { "lat": 40.894745, "lng": 121.920252, "alt": 904, "heading": 64, "pitch": -67 },
+ "onWidget": "control-clock",
+ "radio": true,
+ "flyTo": true
+ },
+ {
+ "id": 306011,
+ "pid": 3060,
+ "type": "czml",
+ "name": "飞行编队",
+ "url": "{dataServer}/file/czml/flight.czml",
+ "popup": "all",
+ "onWidget": "control-clock",
+ "radio": true,
+ "flyTo": true
+ },
+ {
+ "id": 306012,
+ "pid": 3060,
+ "type": "czml",
+ "name": "船舶编队",
+ "url": "{dataServer}/file/czml/ship.czml",
+ "popup": "all",
+ "onWidget": "control-clock",
+ "radio": true,
+ "flyTo": true
+ },
+ { "id": 3050, "pid": 30, "name": "KML数据", "type": "group" },
+ { "pid": 3050, "type": "kml", "name": "海上安全警告", "url": "{dataServer}/file/kml/NAVWARN.kmz", "popup": "all" },
+ {
+ "pid": 3050,
+ "type": "kml",
+ "name": "国境线",
+ "url": "{dataServer}/file/kml/countryboundary.kml",
+ "symbol": { "styleOptions": { "color": "#FED976", "width": 2 } }
+ },
+ {
+ "pid": 3050,
+ "type": "kml",
+ "name": "省界线",
+ "url": "{dataServer}/file/kml/province.kml",
+ "symbol": { "styleOptions": { "color": "#00FF00", "width": 2 } }
+ },
+ { "id": 20, "name": "三维模型", "type": "group" },
+ { "id": 2010, "pid": 20, "name": "gltf模型", "type": "group" },
+ {
+ "pid": 2010,
+ "type": "gltf",
+ "name": "风力发电机",
+ "url": "{dataServer}/gltf/mars/fengche.gltf",
+ "position": { "lng": 117.219071, "lat": 31.828783, "alt": 39.87 },
+ "style": { "scale": 50, "heading": -93 },
+ "popup": "示例信息,这是一个风力发电机",
+ "center": { "lat": 31.821083, "lng": 117.21832, "alt": 832.64, "heading": 2.3, "pitch": -39.2 }
+ },
+ {
+ "pid": 2010,
+ "type": "gltf",
+ "name": "起重车",
+ "url": "{dataServer}/gltf/mars/qzcar/GKZY_anim.gltf",
+ "position": { "lng": 117.217458, "lat": 31.815349, "alt": 35.03 },
+ "style": { "scale": 2, "heading": -95, "clampToGround": true },
+ "center": { "lat": 31.815363, "lng": 117.215958, "alt": 107.35, "heading": 90.7, "pitch": -26.1 }
+ },
+ { "id": 2040, "pid": 20, "name": "城市白模", "type": "group" },
+ {
+ "id": 204011,
+ "pid": 2040,
+ "type": "3dtiles",
+ "name": "合肥市区",
+ "url": "{dataServer}/3dtiles/jzw-hefei/tileset.json",
+ "maximumScreenSpaceError": 1,
+ "maximumMemoryUsage": 1024,
+ "marsJzwStyle": true,
+ "highlight": { "type": "click", "color": "#FFFF00" },
+ "popup": [
+ { "field": "objectid", "name": "编号" },
+ { "field": "name", "name": "名称" },
+ { "field": "height", "name": "楼高", "unit": "米" }
+ ],
+ "center": { "lat": 31.786281, "lng": 117.223716, "alt": 3718, "heading": 2, "pitch": -45 }
+ },
+ {
+ "id": 204012,
+ "pid": 2040,
+ "type": "3dtiles",
+ "name": "上海市区",
+ "url": "{dataServer}/3dtiles/jzw-shanghai/tileset.json",
+ "maximumScreenSpaceError": 4,
+ "maximumMemoryUsage": 1024,
+ "style": {
+ "color": {
+ "conditions": [
+ ["${floor} >= 200", "rgba(45, 0, 75, 0.5)"],
+ ["${floor} >= 100", "rgb(170, 162, 204)"],
+ ["${floor} >= 50", "rgb(224, 226, 238)"],
+ ["${floor} >= 25", "rgb(252, 230, 200)"],
+ ["${floor} >= 10", "rgb(248, 176, 87)"],
+ ["${floor} >= 5", "rgb(198, 106, 11)"],
+ ["true", "rgb(127, 59, 8)"]
+ ]
+ }
+ },
+ "highlight": { "type": "click", "color": "#FFFF00" },
+ "popup": [
+ { "field": "name", "name": "名称" },
+ { "field": "floor", "name": "楼层" }
+ ],
+ "center": { "lat": 31.257341, "lng": 121.466139, "alt": 2170.8, "heading": 122.2, "pitch": -31.8 }
+ },
+
+ { "id": 2050, "pid": 20, "name": "点云", "type": "group" },
+ {
+ "pid": 2050,
+ "type": "3dtiles",
+ "name": "高压线塔杆",
+ "url": "//data.mars3d.cn/3dtiles/pnts-ganta/tileset.json",
+ "maximumScreenSpaceError": 1,
+ "position": { "alt": 31 },
+ "style": {
+ "color": {
+ "conditions": [
+ ["(${Classification} >= 4) && (${Classification} < 5) ", "color('#DC143C')"],
+ ["(${Classification} >= 7) && (${Classification} < 8) ", "color('#7B68EE')"],
+ ["(${Classification} >= 16) && (${Classification} < 17) ", "color('#00CED1')"],
+ ["(${Classification} >= 17) && (${Classification} < 18) ", "color('#3CB371')"],
+ ["(${Classification} >= 18) && (${Classification} < 19) ", "color('#FFFF00')"],
+ ["(${Classification} >= 19) && (${Classification} < 20) ", "color('#FFA500')"],
+ ["(${Classification} >= 20) && (${Classification} < 21) ", "color('#FF6347')"]
+ ]
+ }
+ },
+ "center": { "lat": 31.504746, "lng": 118.264278, "alt": 580, "heading": 29, "pitch": -49 }
+ },
+ { "id": 2060, "pid": 20, "name": "BIM模型", "type": "group" },
+ {
+ "id": 20601121,
+ "pid": 2060,
+ "type": "3dtiles",
+ "name": "大学教学楼",
+ "url": "{dataServer}/3dtiles/bim-daxue/tileset.json",
+ "position": { "lng": 117.251229, "lat": 31.844015, "alt": 31.2 },
+ "highlight": { "type": "click", "color": "#FFFF00" },
+ "popup": "all",
+ "scenetree": "scenetree.json",
+ "center": { "lat": 31.842516, "lng": 117.25107, "alt": 145, "heading": 8, "pitch": -39 }
+ },
+ {
+ "pid": 2060,
+ "type": "3dtiles",
+ "name": "轻轨地铁站",
+ "url": "{dataServer}/3dtiles/bim-ditiezhan/tileset.json",
+ "position": { "lng": 117.203994, "lat": 31.857999, "alt": 28.9 },
+ "rotation": { "z": 168.1 },
+ "highlight": { "type": "click", "color": "#00FF00" },
+ "popup": "all",
+ "scenetree": "scenetree.json",
+ "center": { "lat": 31.856125, "lng": 117.204513, "alt": 155, "heading": 350, "pitch": -31 }
+ },
+ {
+ "id": 206012,
+ "pid": 2060,
+ "type": "3dtiles",
+ "name": "桥梁",
+ "url": "{dataServer}/3dtiles/bim-qiaoliang/tileset.json",
+ "position": { "lng": 117.096906, "lat": 31.851564, "alt": 45 },
+ "rotation": { "z": 17.5 },
+ "maximumScreenSpaceError": 16,
+ "maximumMemoryUsage": 1024,
+ "skipLevelOfDetail": true,
+ "loadSiblings": true,
+ "cullRequestsWhileMoving": true,
+ "cullRequestsWhileMovingMultiplier": 10,
+ "preferLeaves": true,
+ "progressiveResolutionHeightFraction": 0.5,
+ "dynamicScreenSpaceError": true,
+ "preloadWhenHidden": true,
+ "center": { "lat": 31.849357, "lng": 117.099194, "alt": 306.2, "heading": 327.1, "pitch": -30.9 },
+ "scenetree": "scenetree.json",
+ "highlight": { "type": "click", "color": "#00FF00" },
+ "popup": "all"
+ },
+ { "id": 2020, "pid": 20, "name": "人工建模", "type": "group" },
+ {
+ "id": 202013,
+ "pid": 2020,
+ "type": "3dtiles",
+ "name": "地下管网",
+ "url": "{dataServer}/3dtiles/max-piping/tileset.json",
+ "position": { "lng": 117.215457, "lat": 31.843363, "alt": -3.6 },
+ "rotation": { "z": 336.7 },
+ "maximumScreenSpaceError": 2,
+ "maximumMemoryUsage": 1024,
+ "highlight": { "type": "click", "color": "#00FF00" },
+ "popup": "all",
+ "center": { "lat": 31.838821, "lng": 117.216402, "alt": 461, "heading": 0, "pitch": -46 },
+ "msg": "演示数据,地下数据拖动时会在地面漂移"
+ },
+ {
+ "id": 202012,
+ "pid": 2020,
+ "type": "3dtiles",
+ "name": "石化工厂",
+ "url": "{dataServer}/3dtiles/max-shihua/tileset.json",
+ "position": { "lng": 117.077158, "lat": 31.659116, "alt": 24.6 },
+ "maximumScreenSpaceError": 1,
+ "maximumMemoryUsage": 1024,
+ "highlight": { "type": "click", "color": "#00FF00" },
+ "popup": "all",
+ "scenetree": "scenetree.json",
+ "center": { "lat": 31.654916, "lng": 117.08278, "alt": 279, "heading": 316, "pitch": -29 }
+ },
+ {
+ "id": 202030,
+ "pid": 2020,
+ "name": "水利闸门",
+ "type": "group",
+ "center": { "lat": 29.794301, "lng": 121.47998, "alt": 262, "heading": 191, "pitch": -35 }
+ },
+ {
+ "pid": 202030,
+ "type": "gltf",
+ "name": "闸门",
+ "url": "{dataServer}/gltf/mars/zhamen.glb",
+ "position": { "lng": 121.479813, "lat": 29.791278, "alt": 16 },
+ "style": { "heading": 105 },
+ "center": { "lat": 29.791607, "lng": 121.479925, "alt": 27, "heading": 198, "pitch": -18 }
+ },
+ {
+ "id": 202011,
+ "pid": 202030,
+ "type": "3dtiles",
+ "name": "整体",
+ "url": "{dataServer}/3dtiles/max-fsdzm/tileset.json",
+ "position": { "alt": 15.2 },
+ "maximumScreenSpaceError": 1,
+ "maximumMemoryUsage": 1024,
+ "center": { "lat": 29.792675, "lng": 121.480207, "alt": 190.8, "heading": 196.1, "pitch": -49 }
+ },
+ { "id": 2030, "pid": 20, "name": "倾斜摄影", "type": "group" },
+ {
+ "id": 203014,
+ "pid": 2030,
+ "type": "3dtiles",
+ "name": "县城社区",
+ "url": "{dataServer}/3dtiles/qx-shequ/tileset.json",
+ "position": { "alt": 11.5 },
+ "maximumScreenSpaceError": 2,
+ "maximumMemoryUsage": 2048,
+ "dynamicScreenSpaceError": true,
+ "cullWithChildrenBounds": false,
+ "center": { "lat": 28.440864, "lng": 119.486477, "alt": 588.23, "heading": 268.6, "pitch": -37.8 },
+ "show": false,
+ "flyTo": false
+ },
+ {
+ "id": 203015,
+ "pid": 2030,
+ "name": "合肥天鹅湖",
+ "type": "3dtiles",
+ "url": "{dataServer}/3dtiles/qx-teh/tileset.json",
+ "position": { "lng": 117.218434, "lat": 31.81807, "alt": 163 },
+ "maximumScreenSpaceError": 16,
+ "maximumMemoryUsage": 1024,
+ "dynamicScreenSpaceError": true,
+ "cullWithChildrenBounds": false,
+ "skipLevelOfDetail": true,
+ "preferLeaves": true,
+ "center": { "lat": 31.795308, "lng": 117.21948, "alt": 1820, "heading": 0, "pitch": -39 }
+ },
+ {
+ "id": 203013,
+ "pid": 2030,
+ "type": "geojson",
+ "name": "文庙-单体化",
+ "url": " {dataServer}/file/geojson/dth-wm.json",
+ "symbol": {
+ "type": "polygonP",
+ "styleOptions": {
+ "color": "rgba(255, 255, 255, 0.01)",
+ "clampToGround": true,
+ "classification": true,
+ "buffer": 1,
+ "highlight": {
+ "color": "rgba(255,255,0,0.5)"
+ }
+ }
+ },
+ "popup": [
+ { "field": "name", "name": "房屋名称" },
+ { "field": "jznf", "name": "建造年份" },
+ { "field": "ssdw", "name": "所属单位" },
+ { "field": "remark", "name": "备注信息" }
+ ]
+ },
+ {
+ "id": 203012,
+ "pid": 2030,
+ "type": "3dtiles",
+ "name": "文庙",
+ "url": "{dataServer}/3dtiles/qx-simiao/tileset.json",
+ "position": { "alt": 80.6 },
+ "maximumScreenSpaceError": 2,
+ "maximumMemoryUsage": 2048,
+ "dynamicScreenSpaceError": true,
+ "cullWithChildrenBounds": false,
+ "skipLevelOfDetail": true,
+ "preferLeaves": true,
+ "center": { "lat": 33.589536, "lng": 119.032216, "alt": 145.08, "heading": 3.1, "pitch": -22.9 }
+ },
+ { "id": 99, "name": "数据图层", "type": "group" }
+ ]
+ }
+}
diff --git a/static/loader.html b/static/loader.html
new file mode 100644
index 0000000..3d070c3
--- /dev/null
+++ b/static/loader.html
@@ -0,0 +1,175 @@
+
+
+
+
+
+ Mars3D三维地球 桌面程序
+
+
+
+
+
+
+
+
+
+
+
+
+
+
正在准备资源中...
+
+
+
+
+
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..6a9bcc4
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "resolveJsonModule": true,
+ "baseUrl": ".",
+ "outDir": "./dist/electron/main",
+ "sourceMap": true,
+ "declaration": false,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "allowSyntheticDefaultImports": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "importHelpers": true,
+ "target": "es2017",
+ "types": ["vite/client"],
+ "paths": {
+ "@config/*": ["config/*"],
+ "@renderer/*": ["src/renderer/*"],
+ "@main/*": ["src/main/*"],
+ "@store/*": ["src/renderer/store/modules/*"]
+ },
+ "typeRoots": ["node_modules/@types"],
+ "lib": ["es2018", "dom"]
+ },
+ "include": ["src/**/*", "customTypes/*"],
+ "exclude": ["node_modules"]
+}
diff --git a/updateConfig.json b/updateConfig.json
new file mode 100644
index 0000000..c4c852d
--- /dev/null
+++ b/updateConfig.json
@@ -0,0 +1,11 @@
+{
+ "output": "./build",
+ "target": "gzip",
+ "input": "./build/win-unpacked",
+ "updateJsonName": "update-config",
+ "version": "1.0.0",
+ "tempDirectory": "update_temp",
+ "oldDirectory": "old_temp",
+ "url": "http://localhost:5000",
+ "updaterName": "updater"
+}