all
3
apps/electron/.eslintrc-auto-import.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"globals": {}
|
||||
}
|
||||
29
apps/electron/electron-builder.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
appId: com.cslab.dcs.editor
|
||||
productName: cslab-dcs-editor
|
||||
directories:
|
||||
output: dist
|
||||
buildResources: resources
|
||||
files:
|
||||
- out/**/*
|
||||
- '!**/*.map'
|
||||
extraMetadata:
|
||||
main: out/main/index.js
|
||||
nsis:
|
||||
oneClick: false
|
||||
perMachine: false
|
||||
allowToChangeInstallationDirectory: true
|
||||
deleteAppDataOnUninstall: false
|
||||
mac:
|
||||
target: dmg
|
||||
hardenedRuntime: true
|
||||
gatekeeperAssess: false
|
||||
win:
|
||||
icon: resources/icon.ico
|
||||
target:
|
||||
- target: nsis
|
||||
arch:
|
||||
- x64
|
||||
- ia32
|
||||
artifactName: '${productName}_${version}_win7-setup.${ext}'
|
||||
linux:
|
||||
target: AppImage
|
||||
28
apps/electron/electron.vite.config.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { resolve } from 'node:path'
|
||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
||||
import { createSharedViteConfig } from '../../packages/core/vite.shared'
|
||||
|
||||
// Core 包的根目录
|
||||
const coreRoot = resolve(__dirname, '../../packages/core')
|
||||
// 使用 any 断言绕过 Vite 版本差异(electron: vite@5, core: vite@6)
|
||||
const sharedConfig = createSharedViteConfig(coreRoot) as any
|
||||
|
||||
export default defineConfig({
|
||||
main: {
|
||||
plugins: [externalizeDepsPlugin()],
|
||||
},
|
||||
preload: {
|
||||
plugins: [externalizeDepsPlugin()],
|
||||
},
|
||||
renderer: {
|
||||
...sharedConfig,
|
||||
root: resolve(__dirname, 'src/renderer'),
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: {
|
||||
index: resolve(__dirname, 'src/renderer/index.html'),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
36
apps/electron/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@cslab-dcs/electron",
|
||||
"version": "1.0.0",
|
||||
"description": "DCS Editor Electron App",
|
||||
"author": "cslab-dcs",
|
||||
"main": "./out/main/index.js",
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"dev": "electron-vite dev",
|
||||
"build": "electron-vite build",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"build:unpack": "npm run build && electron-builder --dir",
|
||||
"build:win": "npm run build && electron-builder --win",
|
||||
"build:mac": "npm run build && electron-builder --mac",
|
||||
"build:linux": "npm run build && electron-builder --linux"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cslab-dcs/bridge": "workspace:*",
|
||||
"@cslab-dcs/core": "workspace:*",
|
||||
"@electron-toolkit/preload": "2.0.0",
|
||||
"@electron-toolkit/utils": "2.0.0",
|
||||
"electron-updater": "6.3.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "6.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "4.1.1",
|
||||
"electron": "22.3.27",
|
||||
"electron-builder": "25.1.8",
|
||||
"electron-vite": "2.3.0",
|
||||
"typescript": "5.9.3",
|
||||
"vite": "5.4.11",
|
||||
"vue": "3.5.13",
|
||||
"vue-tsc": "2.2.0"
|
||||
}
|
||||
}
|
||||
BIN
apps/electron/resources/icon.ico
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
apps/electron/resources/icon.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
1
apps/electron/src/main/env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="electron-vite/node" />
|
||||
201
apps/electron/src/main/index.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
/* eslint-disable node/prefer-global/process */
|
||||
import fs from 'node:fs/promises'
|
||||
import os from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
|
||||
import { app, BrowserWindow, dialog, ipcMain, shell } from 'electron'
|
||||
import icon from '../../resources/icon.png?asset'
|
||||
|
||||
function createWindow(): void {
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 670,
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
...(process.platform === 'linux' ? { icon } : {}),
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/index.js'),
|
||||
sandbox: true,
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
},
|
||||
})
|
||||
|
||||
mainWindow.on('ready-to-show', () => {
|
||||
mainWindow.show()
|
||||
})
|
||||
|
||||
mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||
if (isUrlAllowed(details.url)) {
|
||||
shell.openExternal(details.url)
|
||||
}
|
||||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
// HMR for renderer base on electron-vite cli.
|
||||
// Load the remote URL for development or the local html file for production.
|
||||
if (is.dev && process.env.ELECTRON_RENDERER_URL) {
|
||||
mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL)
|
||||
}
|
||||
else {
|
||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
||||
}
|
||||
}
|
||||
|
||||
// ── 文件路径授权管理 ──
|
||||
// 只允许访问经用户通过原生对话框选择的文件路径
|
||||
const authorizedPaths = new Set<string>()
|
||||
const MAX_AUTHORIZED_PATHS = 200
|
||||
|
||||
function authorizePath(filePath: string) {
|
||||
if (authorizedPaths.size >= MAX_AUTHORIZED_PATHS) {
|
||||
const oldest = authorizedPaths.values().next().value
|
||||
if (oldest)
|
||||
authorizedPaths.delete(oldest)
|
||||
}
|
||||
authorizedPaths.add(filePath)
|
||||
}
|
||||
|
||||
function isPathAuthorized(filePath: string): boolean {
|
||||
return authorizedPaths.has(filePath)
|
||||
}
|
||||
|
||||
// ── URL 协议白名单 ──
|
||||
const ALLOWED_URL_PROTOCOLS = new Set(['https:', 'http:'])
|
||||
|
||||
function isUrlAllowed(url: string): boolean {
|
||||
try {
|
||||
return ALLOWED_URL_PROTOCOLS.has(new URL(url).protocol)
|
||||
}
|
||||
catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IPC Handlers
|
||||
function registerIpcHandlers() {
|
||||
// File — 仅允许经对话框授权的路径
|
||||
ipcMain.handle('file:read', async (_, path) => {
|
||||
if (!isPathAuthorized(path)) {
|
||||
throw new Error('文件访问被拒绝:路径未经用户选择授权')
|
||||
}
|
||||
return await fs.readFile(path, 'utf-8')
|
||||
})
|
||||
|
||||
ipcMain.handle('file:exists', async (_, path) => {
|
||||
if (!isPathAuthorized(path)) {
|
||||
return false
|
||||
}
|
||||
try {
|
||||
await fs.access(path)
|
||||
return true
|
||||
}
|
||||
catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('file:write', async (_, path, content) => {
|
||||
if (!isPathAuthorized(path)) {
|
||||
throw new Error('文件写入被拒绝:路径未经用户选择授权')
|
||||
}
|
||||
await fs.writeFile(path, content, 'utf-8')
|
||||
})
|
||||
|
||||
// Dialog — 记录用户选择的路径
|
||||
ipcMain.handle('dialog:open', async (_, options) => {
|
||||
const { filePaths } = await dialog.showOpenDialog({
|
||||
title: options?.title,
|
||||
defaultPath: options?.defaultPath,
|
||||
filters: options?.filters,
|
||||
properties: [
|
||||
options?.multiple ? 'multiSelections' : 'openFile',
|
||||
options?.directory ? 'openDirectory' : 'openFile',
|
||||
],
|
||||
})
|
||||
filePaths.forEach(p => authorizePath(p))
|
||||
return options?.multiple ? filePaths : filePaths[0] || null
|
||||
})
|
||||
|
||||
ipcMain.handle('dialog:save', async (_, options) => {
|
||||
const { filePath } = await dialog.showSaveDialog({
|
||||
title: options?.title,
|
||||
defaultPath: options?.defaultPath,
|
||||
filters: options?.filters,
|
||||
})
|
||||
if (filePath)
|
||||
authorizePath(filePath)
|
||||
return filePath || null
|
||||
})
|
||||
|
||||
ipcMain.handle('dialog:message', async (_, options) => {
|
||||
await dialog.showMessageBox({
|
||||
title: options?.title,
|
||||
message: options?.message,
|
||||
type: options?.type || 'info',
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.handle('dialog:confirm', async (_, options) => {
|
||||
const { response } = await dialog.showMessageBox({
|
||||
title: options?.title,
|
||||
message: options?.message,
|
||||
type: options?.type || 'info',
|
||||
buttons: [options?.okLabel || 'Yes', options?.cancelLabel || 'No'],
|
||||
defaultId: 0,
|
||||
cancelId: 1,
|
||||
})
|
||||
return response === 0
|
||||
})
|
||||
|
||||
// System
|
||||
ipcMain.handle('app:version', () => app.getVersion())
|
||||
ipcMain.handle('app:info', () => ({
|
||||
name: 'electron',
|
||||
version: process.versions.electron,
|
||||
os: process.platform,
|
||||
osVersion: os.release(),
|
||||
arch: process.arch,
|
||||
}))
|
||||
ipcMain.handle('shell:open', (_, url) => {
|
||||
if (!isUrlAllowed(url)) {
|
||||
throw new Error('不允许打开此类型的链接,仅支持 HTTP/HTTPS')
|
||||
}
|
||||
return shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(() => {
|
||||
// Set app user model id for windows
|
||||
electronApp.setAppUserModelId('com.cslab.dcs')
|
||||
|
||||
// Default open or close DevTools by F12 in development
|
||||
// and ignore CommandOrControl + R in production.
|
||||
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
||||
app.on('browser-window-created', (_, window) => {
|
||||
optimizer.watchWindowShortcuts(window)
|
||||
})
|
||||
|
||||
registerIpcHandlers()
|
||||
createWindow()
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0)
|
||||
createWindow()
|
||||
})
|
||||
})
|
||||
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
8
apps/electron/src/preload/index.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { ElectronAPI } from '@electron-toolkit/preload'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: ElectronAPI
|
||||
api: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
22
apps/electron/src/preload/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/* eslint-disable node/prefer-global/process */
|
||||
import { electronAPI } from '@electron-toolkit/preload'
|
||||
import { contextBridge } from 'electron'
|
||||
|
||||
// Custom APIs for renderer
|
||||
const api = {}
|
||||
|
||||
// Use `contextBridge` APIs to expose IPC renderer to the renderer process.
|
||||
// Read more at https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
||||
if (process.contextIsolated) {
|
||||
try {
|
||||
contextBridge.exposeInMainWorld('electron', electronAPI)
|
||||
contextBridge.exposeInMainWorld('api', api)
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
else {
|
||||
window.electron = electronAPI
|
||||
window.api = api
|
||||
}
|
||||
12
apps/electron/src/renderer/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>DCS Editor (Electron)</title>
|
||||
<!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"> -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
17
apps/electron/src/renderer/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="element-plus/global" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
|
||||
const component: DefineComponent<object, object, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_API_BASE_URL: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
5
apps/electron/src/renderer/src/main.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { bridge } from '@cslab-dcs/bridge'
|
||||
import { createDCSApp } from '@cslab-dcs/core'
|
||||
|
||||
const app = createDCSApp(bridge)
|
||||
app.mount('#app')
|
||||
8
apps/electron/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"references": [
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
{ "path": "./tsconfig.preload.json" },
|
||||
{ "path": "./tsconfig.web.json" }
|
||||
],
|
||||
"files": []
|
||||
}
|
||||
10
apps/electron/tsconfig.node.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["electron-vite/node"],
|
||||
"outDir": "out"
|
||||
},
|
||||
"include": ["src/main/**/*"],
|
||||
"exclude": ["node_modules", "out", "dist"]
|
||||
}
|
||||
10
apps/electron/tsconfig.preload.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["electron-vite/node"],
|
||||
"outDir": "out"
|
||||
},
|
||||
"include": ["src/preload/**/*"],
|
||||
"exclude": ["node_modules", "out", "dist"]
|
||||
}
|
||||
23
apps/electron/tsconfig.web.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["../../packages/core/src/*"],
|
||||
"@cslab-dcs/core": ["../../packages/core/src"],
|
||||
"@cslab-dcs/bridge": ["../../packages/bridge/src"],
|
||||
"@cslab-dcs/schema": ["../../packages/schema/src"]
|
||||
},
|
||||
"types": ["element-plus/global"]
|
||||
},
|
||||
"include": [
|
||||
"src/renderer/**/*",
|
||||
"../../packages/core/src/**/*",
|
||||
"../../packages/bridge/src/**/*",
|
||||
"../../packages/request/src/**/*",
|
||||
"../../packages/schema/src/**/*",
|
||||
"../../packages/utils/src/**/*"
|
||||
],
|
||||
"exclude": ["node_modules", "out", "dist"]
|
||||
}
|
||||
3
apps/tauri/.eslintrc-auto-import.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"globals": {}
|
||||
}
|
||||
13
apps/tauri/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>DCS Editor (Tauri)</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
30
apps/tauri/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "@cslab-dcs/tauri",
|
||||
"type": "module",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev:web": "vite dev",
|
||||
"dev": "tauri dev",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cslab-dcs/bridge": "workspace:*",
|
||||
"@cslab-dcs/core": "workspace:*",
|
||||
"@tauri-apps/api": "2.10.1",
|
||||
"@tauri-apps/plugin-dialog": "^2.2.0",
|
||||
"@tauri-apps/plugin-fs": "^2.2.0",
|
||||
"@tauri-apps/plugin-opener": "^2.2.5",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "2.10.0",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^6.0.3",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
5295
apps/tauri/src-tauri/Cargo.lock
generated
Normal file
31
apps/tauri/src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "cslab-dcs-editor"
|
||||
version = "1.0.0"
|
||||
description = "DCS Editor - CSLAB DCS 编辑器"
|
||||
authors = [ "cslab" ]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "cslab_dcs_tauri_lib"
|
||||
crate-type = [
|
||||
"staticlib",
|
||||
"cdylib",
|
||||
"rlib"
|
||||
]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.5.4", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2.10.0", features = [] }
|
||||
tauri-plugin-opener = "2.2.5"
|
||||
tauri-plugin-dialog = "2.2.0"
|
||||
tauri-plugin-fs = "2.2.0"
|
||||
tauri-plugin-os = "2"
|
||||
serde = { version = "1", features = [ "derive" ] }
|
||||
serde_json = "1"
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||
# DO NOT REMOVE!!
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
3
apps/tauri/src-tauri/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
13
apps/tauri/src-tauri/capabilities/default.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Capability for the main window",
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"dialog:default",
|
||||
"opener:default",
|
||||
"fs:default",
|
||||
"os:default"
|
||||
]
|
||||
}
|
||||
BIN
apps/tauri/src-tauri/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
apps/tauri/src-tauri/icons/128x128@2x.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
apps/tauri/src-tauri/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
apps/tauri/src-tauri/icons/Square107x107Logo.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
apps/tauri/src-tauri/icons/Square142x142Logo.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
apps/tauri/src-tauri/icons/Square150x150Logo.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
apps/tauri/src-tauri/icons/Square284x284Logo.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
apps/tauri/src-tauri/icons/Square30x30Logo.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
apps/tauri/src-tauri/icons/Square310x310Logo.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
apps/tauri/src-tauri/icons/Square44x44Logo.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
apps/tauri/src-tauri/icons/Square71x71Logo.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
apps/tauri/src-tauri/icons/Square89x89Logo.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
apps/tauri/src-tauri/icons/StoreLogo.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/tauri/src-tauri/icons/icon.icns
Normal file
BIN
apps/tauri/src-tauri/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
apps/tauri/src-tauri/icons/icon.png
Executable file
|
After Width: | Height: | Size: 291 KiB |
BIN
apps/tauri/src-tauri/icons/icon_512.png
Normal file
|
After Width: | Height: | Size: 189 KiB |
10
apps/tauri/src-tauri/src/lib.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
// #[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
6
apps/tauri/src-tauri/src/main.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
cslab_dcs_tauri_lib::run()
|
||||
}
|
||||
40
apps/tauri/src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"identifier": "com.cslab.dcs.editor",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev:web",
|
||||
"beforeBuildCommand": "pnpm build:web",
|
||||
"devUrl": "http://localhost:1420",
|
||||
"frontendDist": "../dist"
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
},
|
||||
"app": {
|
||||
"withGlobalTauri": false,
|
||||
"windows": [
|
||||
{
|
||||
"title": "DCS Editor",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"dragDropEnabled": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' https:; font-src 'self' data:"
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
|
||||
"fs": {}
|
||||
|
||||
}
|
||||
}
|
||||
9
apps/tauri/src/main.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { WebBridge } from '@cslab-dcs/bridge'
|
||||
import { TauriBridge } from '@cslab-dcs/bridge/tauri'
|
||||
import { createDCSApp } from '@cslab-dcs/core'
|
||||
|
||||
const isTauri = !!(window as any).__TAURI_INTERNALS__ || !!(window as any).__TAURI__
|
||||
const bridge = isTauri ? new TauriBridge() : new WebBridge()
|
||||
|
||||
const app = createDCSApp(bridge)
|
||||
app.mount('#app')
|
||||
23
apps/tauri/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["../../packages/core/src/*"],
|
||||
"@cslab-dcs/core": ["../../packages/core/src"],
|
||||
"@cslab-dcs/bridge": ["../../packages/bridge/src"],
|
||||
"@cslab-dcs/schema": ["../../packages/schema/src"]
|
||||
},
|
||||
"types": ["element-plus/global"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"../../packages/core/src/**/*",
|
||||
"../../packages/core/env.d.ts",
|
||||
"../../packages/bridge/src/**/*",
|
||||
"../../packages/request/src/**/*",
|
||||
"../../packages/schema/src/**/*",
|
||||
"../../packages/utils/src/**/*"
|
||||
],
|
||||
"exclude": ["node_modules", "dist", "src-tauri"]
|
||||
}
|
||||
36
apps/tauri/vite.config.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { UserConfig } from 'vite'
|
||||
import { resolve } from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { defineConfig } from 'vite'
|
||||
import { createSharedViteConfig } from '../../packages/core/vite.shared'
|
||||
|
||||
const coreRoot = resolve(__dirname, '../../packages/core')
|
||||
|
||||
const host = process.env.VITE_DEV_SERVER_HOST
|
||||
|
||||
// 使用类型断言绕过 pnpm 导致的 Vite 依赖重复安装类型不兼容问题
|
||||
export default defineConfig({
|
||||
...createSharedViteConfig(coreRoot) as UserConfig,
|
||||
root: __dirname,
|
||||
clearScreen: false,
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
host: host || false,
|
||||
hmr: host
|
||||
? {
|
||||
protocol: 'ws',
|
||||
host,
|
||||
port: 1421,
|
||||
}
|
||||
: undefined,
|
||||
watch: {
|
||||
// 3. tell vite to ignore watching `src-tauri`
|
||||
ignored: ['**/src-tauri/**'],
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
emptyOutDir: true,
|
||||
},
|
||||
})
|
||||
3
apps/web/.env
Normal file
@@ -0,0 +1,3 @@
|
||||
# 默认环境变量配置(会被 .env.development 和 .env.production 覆盖)
|
||||
# VITE_API_BASE_URL=https://cslab.oberyun.com
|
||||
VITE_API_BASE_URL=http://192.168.1.110:8001
|
||||
2
apps/web/.env.development
Normal file
@@ -0,0 +1,2 @@
|
||||
# 开发环境配置
|
||||
VITE_API_BASE_URL=http://192.168.1.110:8001
|
||||
2
apps/web/.env.production
Normal file
@@ -0,0 +1,2 @@
|
||||
# 生产环境配置
|
||||
VITE_API_BASE_URL=https://cslab.oberyun.com
|
||||
3
apps/web/.eslintrc-auto-import.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"globals": {}
|
||||
}
|
||||
13
apps/web/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>DCS Editor (Web)</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
24
apps/web/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "@cslab-dcs/web",
|
||||
"type": "module",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "vue-tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cslab-dcs/bridge": "workspace:*",
|
||||
"@cslab-dcs/core": "workspace:*",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^6.0.3",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
16
apps/web/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
|
||||
const component: DefineComponent<object, object, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_API_BASE_URL: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
5
apps/web/src/main.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { bridge } from '@cslab-dcs/bridge'
|
||||
import { createDCSApp } from '@cslab-dcs/core'
|
||||
|
||||
const app = createDCSApp(bridge)
|
||||
app.mount('#app')
|
||||
24
apps/web/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["../../packages/core/src/*"],
|
||||
"@cslab-dcs/core": ["../../packages/core/src"],
|
||||
"@cslab-dcs/bridge": ["../../packages/bridge/src"],
|
||||
"@cslab-dcs/schema": ["../../packages/schema/src"]
|
||||
},
|
||||
"types": ["element-plus/global"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"../../packages/core/src/**/*",
|
||||
"../../packages/core/src/auto-imports.d.ts",
|
||||
"../../packages/core/env.d.ts",
|
||||
"../../packages/bridge/src/**/*",
|
||||
"../../packages/request/src/**/*",
|
||||
"../../packages/schema/src/**/*",
|
||||
"../../packages/utils/src/**/*"
|
||||
],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
31
apps/web/unocss.config.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
defineConfig,
|
||||
presetAttributify,
|
||||
presetIcons,
|
||||
presetTypography,
|
||||
presetUno,
|
||||
transformerDirectives,
|
||||
transformerVariantGroup,
|
||||
} from 'unocss'
|
||||
import presetChinese from 'unocss-preset-chinese'
|
||||
import presetEase from 'unocss-preset-ease'
|
||||
|
||||
export default defineConfig({
|
||||
presets: [
|
||||
presetUno(),
|
||||
presetAttributify(),
|
||||
presetChinese(),
|
||||
presetEase(),
|
||||
presetTypography(),
|
||||
presetIcons({
|
||||
scale: 1.2,
|
||||
warn: true,
|
||||
}),
|
||||
],
|
||||
shortcuts: [
|
||||
['flex-center', 'flex items-center justify-center'],
|
||||
['flex-between', 'flex items-center justify-between'],
|
||||
['flex-end', 'flex items-end justify-between'],
|
||||
],
|
||||
transformers: [transformerDirectives(), transformerVariantGroup()],
|
||||
})
|
||||
25
apps/web/vite.config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { UserConfig } from 'vite'
|
||||
import { resolve } from 'node:path'
|
||||
import { defineConfig, loadEnv, mergeConfig } from 'vite'
|
||||
import { createSharedViteConfig } from '../../packages/core/vite.shared'
|
||||
|
||||
// Core 包的根目录
|
||||
const coreRoot = resolve(__dirname, '../../packages/core')
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, coreRoot, 'VITE_')
|
||||
|
||||
const sharedConfig = createSharedViteConfig(coreRoot, env) as UserConfig
|
||||
|
||||
return mergeConfig(sharedConfig, {
|
||||
root: __dirname,
|
||||
base: env.VITE_BASE_URL, // 确保相对路径,方便部署
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
emptyOutDir: true,
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
},
|
||||
})
|
||||
})
|
||||