增加了前后端打包验证功能
This commit is contained in:
parent
c9c2e6cd16
commit
aaf6047b81
10097
.gitignore
vendored
10097
.gitignore
vendored
File diff suppressed because it is too large
Load Diff
@ -85,7 +85,7 @@ exe = EXE(
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='BodyBalanceBackend_Full',
|
||||
name='BodyBalanceBackend',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
@ -113,7 +113,7 @@ exe = EXE(
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='BodyBalanceBackend_Full',
|
||||
name='BodyBalanceBackend',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
@ -171,7 +171,7 @@ echo - 管理界面: http://localhost:5000
|
||||
echo.
|
||||
echo 按Ctrl+C停止服务
|
||||
echo.
|
||||
"BodyBalanceBackend_Full.exe"
|
||||
"BodyBalanceBackend.exe"
|
||||
if %errorlevel% neq 0 (
|
||||
echo.
|
||||
echo 服务启动失败,请检查错误信息
|
||||
@ -183,10 +183,10 @@ if %errorlevel% neq 0 (
|
||||
if not os.path.exists(dist_dir):
|
||||
os.makedirs(dist_dir)
|
||||
|
||||
with open(os.path.join(dist_dir, 'start_backend_full.bat'), 'w', encoding='utf-8') as f:
|
||||
with open(os.path.join(dist_dir, 'start_backend.bat'), 'w', encoding='utf-8') as f:
|
||||
f.write(startup_script)
|
||||
|
||||
print("✓ 创建启动脚本: dist/start_backend_full.bat")
|
||||
print("✓ 创建启动脚本: dist/start_backend.bat")
|
||||
|
||||
def create_directories():
|
||||
"""创建必要的目录结构"""
|
||||
@ -215,30 +215,28 @@ def copy_dll_files():
|
||||
# 查找所有DLL文件
|
||||
dll_files = []
|
||||
|
||||
# 从当前目录查找DLL文件
|
||||
for file in os.listdir('.'):
|
||||
if file.endswith('.dll'):
|
||||
dll_files.append(file)
|
||||
|
||||
# 从子目录查找DLL文件
|
||||
for root, dirs, files in os.walk('.'):
|
||||
if 'dist' in root or 'build' in root or '__pycache__' in root:
|
||||
continue
|
||||
for file in files:
|
||||
if file.endswith('.dll'):
|
||||
dll_files.append(os.path.join(root, file))
|
||||
|
||||
if not dll_files:
|
||||
print("⚠️ 未找到DLL文件")
|
||||
# 复制dll目录下的所有文件夹和文件到目标目录的dll目录下
|
||||
source_dll_dir = 'dll'
|
||||
if not os.path.exists(source_dll_dir):
|
||||
print("⚠️ 未找到dll目录")
|
||||
return
|
||||
|
||||
for dll_file in dll_files:
|
||||
if os.path.exists(dll_file):
|
||||
try:
|
||||
shutil.copy2(dll_file, dll_dir)
|
||||
print(f"✓ 已复制 {os.path.basename(dll_file)}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 复制 {dll_file} 失败: {e}")
|
||||
try:
|
||||
# 遍历dll目录下的所有内容
|
||||
for item in os.listdir(source_dll_dir):
|
||||
source_path = os.path.join(source_dll_dir, item)
|
||||
target_path = os.path.join(dll_dir, item)
|
||||
|
||||
if os.path.isdir(source_path):
|
||||
# 如果是目录,递归复制整个目录
|
||||
shutil.copytree(source_path, target_path, dirs_exist_ok=True)
|
||||
print(f"✓ 已复制目录 {item}")
|
||||
else:
|
||||
# 如果是文件,直接复制
|
||||
shutil.copy2(source_path, target_path)
|
||||
print(f"✓ 已复制文件 {item}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 复制dll目录内容失败: {e}")
|
||||
|
||||
def copy_data_files():
|
||||
"""复制数据库文件到data目录"""
|
||||
@ -309,7 +307,7 @@ def main():
|
||||
print("后处理...")
|
||||
|
||||
# 检查生成的exe文件
|
||||
exe_path = 'dist/BodyBalanceBackend_Full.exe'
|
||||
exe_path = 'dist/BodyBalanceBackend.exe'
|
||||
if os.path.exists(exe_path):
|
||||
print(f"✓ exe文件位置: {exe_path}")
|
||||
|
||||
@ -337,7 +335,7 @@ def main():
|
||||
print()
|
||||
print("输出文件:")
|
||||
print(f"- 可执行文件: {exe_path}")
|
||||
print("- 启动脚本: dist/start_backend_full.bat")
|
||||
print("- 启动脚本: dist/start_backend.bat")
|
||||
print("- 配置文件: dist/config.ini")
|
||||
print()
|
||||
print("目录结构:")
|
||||
@ -346,8 +344,8 @@ def main():
|
||||
print("- logs/ - 日志文件")
|
||||
print()
|
||||
print("使用方式:")
|
||||
print("1. 直接运行: dist/BodyBalanceBackend_Full.exe")
|
||||
print("2. 使用脚本: dist/start_backend_full.bat")
|
||||
print("1. 直接运行: dist/BodyBalanceBackend.exe")
|
||||
print("2. 使用脚本: dist/start_backend.bat")
|
||||
print()
|
||||
print("服务地址: http://localhost:5000")
|
||||
print("SocketIO: ws://localhost:5000")
|
||||
|
41
config.ini
41
config.ini
@ -1,41 +0,0 @@
|
||||
[APP]
|
||||
name = Body Balance Evaluation System
|
||||
version = 1.0.0
|
||||
debug = false
|
||||
log_level = INFO
|
||||
|
||||
[SERVER]
|
||||
host = 0.0.0.0
|
||||
port = 5000
|
||||
cors_origins = *
|
||||
|
||||
[DATABASE]
|
||||
path = backend/data/body_balance.db
|
||||
backup_interval = 24
|
||||
max_backups = 7
|
||||
|
||||
[DEVICES]
|
||||
camera_index = 0
|
||||
camera_width = 640
|
||||
camera_height = 480
|
||||
camera_fps = 30
|
||||
imu_port = COM3
|
||||
pressure_port = COM4
|
||||
|
||||
[DETECTION]
|
||||
default_duration = 60
|
||||
sampling_rate = 30
|
||||
balance_threshold = 0.2
|
||||
posture_threshold = 5.0
|
||||
|
||||
[DATA_PROCESSING]
|
||||
filter_window = 5
|
||||
outlier_threshold = 2.0
|
||||
chart_dpi = 300
|
||||
export_format = csv
|
||||
|
||||
[SECURITY]
|
||||
secret_key = 4bfd3d60ddecef23973b64d761371610130034f23ce90dd6f405756ad2f89495
|
||||
session_timeout = 3600
|
||||
max_login_attempts = 5
|
||||
|
@ -1,169 +0,0 @@
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
const axios = require('axios');
|
||||
const log = require('electron-log');
|
||||
|
||||
let mainWindow;
|
||||
let pythonProcess;
|
||||
const BACKEND_PORT = 5000;
|
||||
const BACKEND_HOST = process.env.BACKEND_HOST || '127.0.0.1';
|
||||
const BACKEND_URL = `http://${BACKEND_HOST}:${BACKEND_PORT}`;
|
||||
|
||||
// 配置日志
|
||||
log.transports.file.resolvePathFn = () => path.join(app.getPath('userData'), 'logs', 'main.log');
|
||||
|
||||
function createWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
},
|
||||
icon: path.join(__dirname, '../../assets/icon.ico'),
|
||||
show: false
|
||||
});
|
||||
|
||||
// 开发环境加载本地服务器,生产环境加载打包后的文件
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
if (isDev) {
|
||||
mainWindow.loadURL('http://localhost:3000');
|
||||
mainWindow.webContents.openDevTools();
|
||||
} else {
|
||||
mainWindow.loadFile(path.join(__dirname, '../renderer/dist/index.html'));
|
||||
}
|
||||
|
||||
mainWindow.once('ready-to-show', () => {
|
||||
mainWindow.show();
|
||||
startPythonBackend();
|
||||
});
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null;
|
||||
stopPythonBackend();
|
||||
});
|
||||
}
|
||||
|
||||
function startPythonBackend() {
|
||||
try {
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
let scriptPath;
|
||||
|
||||
if (isDev) {
|
||||
// 开发环境直接运行Python脚本
|
||||
scriptPath = path.join(__dirname, '../../backend/app.py');
|
||||
pythonProcess = spawn('python', [scriptPath], {
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
} else {
|
||||
// 生产环境运行打包后的exe
|
||||
scriptPath = path.join(process.resourcesPath, 'backend', 'app.exe');
|
||||
pythonProcess = spawn(scriptPath, {
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
}
|
||||
|
||||
log.info('启动Python后端服务:', scriptPath);
|
||||
|
||||
pythonProcess.stdout.on('data', (data) => {
|
||||
log.info('Python输出:', data.toString());
|
||||
});
|
||||
|
||||
pythonProcess.stderr.on('data', (data) => {
|
||||
log.error('Python错误:', data.toString());
|
||||
});
|
||||
|
||||
pythonProcess.on('close', (code) => {
|
||||
log.info(`Python进程退出,代码: ${code}`);
|
||||
});
|
||||
|
||||
// 等待后端启动
|
||||
setTimeout(() => {
|
||||
checkBackendHealth();
|
||||
}, 3000);
|
||||
|
||||
} catch (error) {
|
||||
log.error('启动Python后端失败:', error);
|
||||
dialog.showErrorBox('启动失败', '无法启动后端服务,请检查安装是否完整');
|
||||
}
|
||||
}
|
||||
|
||||
function stopPythonBackend() {
|
||||
if (pythonProcess) {
|
||||
pythonProcess.kill();
|
||||
pythonProcess = null;
|
||||
log.info('Python后端服务已停止');
|
||||
}
|
||||
}
|
||||
|
||||
async function checkBackendHealth() {
|
||||
try {
|
||||
const response = await axios.get(`${BACKEND_URL}/health`, { timeout: 5000 });
|
||||
if (response.status === 200) {
|
||||
log.info('后端服务健康检查通过');
|
||||
mainWindow.webContents.send('backend-ready', true);
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('后端服务健康检查失败:', error.message);
|
||||
mainWindow.webContents.send('backend-ready', false);
|
||||
|
||||
// 尝试重启后端
|
||||
setTimeout(() => {
|
||||
log.info('尝试重启后端服务');
|
||||
stopPythonBackend();
|
||||
startPythonBackend();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// IPC 通信处理
|
||||
ipcMain.handle('get-backend-url', () => {
|
||||
return BACKEND_URL;
|
||||
});
|
||||
|
||||
ipcMain.handle('check-backend-health', async () => {
|
||||
try {
|
||||
const response = await axios.get(`${BACKEND_URL}/health`, { timeout: 3000 });
|
||||
return response.status === 200;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('restart-backend', () => {
|
||||
stopPythonBackend();
|
||||
setTimeout(() => {
|
||||
startPythonBackend();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// 应用事件处理
|
||||
app.whenReady().then(createWindow);
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
stopPythonBackend();
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('before-quit', () => {
|
||||
stopPythonBackend();
|
||||
});
|
||||
|
||||
// 处理未捕获的异常
|
||||
process.on('uncaughtException', (error) => {
|
||||
log.error('未捕获的异常:', error);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
log.error('未处理的Promise拒绝:', reason);
|
||||
});
|
@ -1,32 +0,0 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
// 向渲染进程暴露安全的API
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
// 获取后端服务URL
|
||||
getBackendUrl: () => ipcRenderer.invoke('get-backend-url'),
|
||||
|
||||
// 检查后端服务健康状态
|
||||
checkBackendHealth: () => ipcRenderer.invoke('check-backend-health'),
|
||||
|
||||
// 重启后端服务
|
||||
restartBackend: () => ipcRenderer.invoke('restart-backend'),
|
||||
|
||||
// 监听后端就绪状态
|
||||
onBackendReady: (callback) => {
|
||||
ipcRenderer.on('backend-ready', (event, isReady) => {
|
||||
callback(isReady);
|
||||
});
|
||||
},
|
||||
|
||||
// 移除监听器
|
||||
removeAllListeners: (channel) => {
|
||||
ipcRenderer.removeAllListeners(channel);
|
||||
}
|
||||
});
|
||||
|
||||
// 暴露日志API
|
||||
contextBridge.exposeInMainWorld('logAPI', {
|
||||
info: (message) => console.log('[INFO]', message),
|
||||
error: (message) => console.error('[ERROR]', message),
|
||||
warn: (message) => console.warn('[WARN]', message)
|
||||
});
|
79
frontend/src/renderer/electron-dev.js
Normal file
79
frontend/src/renderer/electron-dev.js
Normal file
@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Electron开发调试启动脚本
|
||||
* 用于在renderer目录下直接启动electron开发环境
|
||||
*/
|
||||
|
||||
const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
// 检查main.js是否存在
|
||||
const mainPath = path.join(__dirname, '../main/main.js');
|
||||
if (!fs.existsSync(mainPath)) {
|
||||
console.error('❌ main.js文件不存在:', mainPath);
|
||||
console.log('请确保main进程文件存在于 ../main/main.js');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 检查preload.js是否存在
|
||||
const preloadPath = path.join(__dirname, '../main/preload.js');
|
||||
if (!fs.existsSync(preloadPath)) {
|
||||
console.error('❌ preload.js文件不存在:', preloadPath);
|
||||
console.log('请确保preload文件存在于 ../main/preload.js');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('🚀 启动Electron开发环境...');
|
||||
console.log('📁 Main进程文件:', mainPath);
|
||||
console.log('📁 Preload文件:', preloadPath);
|
||||
|
||||
// 设置环境变量
|
||||
process.env.NODE_ENV = 'development';
|
||||
process.env.ELECTRON_IS_DEV = '1';
|
||||
|
||||
// 启动Vite开发服务器
|
||||
console.log('\n🔧 启动Vite开发服务器...');
|
||||
const viteProcess = spawn('npm', ['run', 'dev'], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
cwd: __dirname
|
||||
});
|
||||
|
||||
// 等待Vite服务器启动后启动Electron
|
||||
setTimeout(() => {
|
||||
console.log('\n⚡ 启动Electron应用...');
|
||||
const electronProcess = spawn('npx', ['electron', mainPath], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
cwd: __dirname
|
||||
});
|
||||
|
||||
// 处理Electron进程退出
|
||||
electronProcess.on('close', (code) => {
|
||||
console.log(`\n🔚 Electron进程退出,代码: ${code}`);
|
||||
viteProcess.kill();
|
||||
process.exit(code);
|
||||
});
|
||||
|
||||
// 处理Vite进程退出
|
||||
viteProcess.on('close', (code) => {
|
||||
console.log(`\n🔚 Vite进程退出,代码: ${code}`);
|
||||
electronProcess.kill();
|
||||
process.exit(code);
|
||||
});
|
||||
|
||||
}, 3000); // 等待3秒让Vite服务器启动
|
||||
|
||||
// 处理进程信号
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n🛑 收到中断信号,正在关闭...');
|
||||
viteProcess.kill();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
console.log('\n🛑 收到终止信号,正在关闭...');
|
||||
viteProcess.kill();
|
||||
process.exit(0);
|
||||
});
|
237
frontend/src/renderer/main/main.js
Normal file
237
frontend/src/renderer/main/main.js
Normal file
@ -0,0 +1,237 @@
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
const path = require('path');
|
||||
const http = require('http');
|
||||
const fs = require('fs');
|
||||
const url = require('url');
|
||||
const { spawn } = require('child_process');
|
||||
let mainWindow;
|
||||
let localServer;
|
||||
let backendProcess;
|
||||
|
||||
function startBackendService() {
|
||||
// 在打包后的应用中,使用process.resourcesPath获取resources目录
|
||||
const resourcesPath = process.resourcesPath || path.join(__dirname, '../..');
|
||||
const backendPath = path.join(resourcesPath, 'backend/BodyBalanceBackend.exe');
|
||||
const backendDir = path.join(resourcesPath, 'backend/');
|
||||
|
||||
console.log('Resources path:', resourcesPath);
|
||||
console.log('Backend path:', backendPath);
|
||||
console.log('Backend directory:', backendDir);
|
||||
|
||||
// 检查后端可执行文件是否存在
|
||||
if (!fs.existsSync(backendPath)) {
|
||||
console.error('Backend executable not found:', backendPath);
|
||||
// 尝试备用路径
|
||||
const fallbackPath = path.join(__dirname, 'resources/backend/BodyBalanceBackend.exe');
|
||||
console.log('Trying fallback path:', fallbackPath);
|
||||
if (!fs.existsSync(fallbackPath)) {
|
||||
console.error('Fallback backend executable not found:', fallbackPath);
|
||||
return;
|
||||
}
|
||||
// 使用备用路径
|
||||
const fallbackDir = path.join(__dirname, 'resources/backend/');
|
||||
backendProcess = spawn(fallbackPath, [], {
|
||||
cwd: fallbackDir,
|
||||
stdio: ['ignore', 'pipe', 'pipe']
|
||||
});
|
||||
console.log('Starting backend service with fallback path:', fallbackPath);
|
||||
} else {
|
||||
console.log('Starting backend service:', backendPath);
|
||||
|
||||
// 启动后端进程
|
||||
backendProcess = spawn(backendPath, [], {
|
||||
cwd: backendDir,
|
||||
stdio: ['ignore', 'pipe', 'pipe']
|
||||
});
|
||||
}
|
||||
|
||||
backendProcess.stdout.on('data', (data) => {
|
||||
console.log('Backend stdout:', data.toString());
|
||||
});
|
||||
|
||||
backendProcess.stderr.on('data', (data) => {
|
||||
console.error('Backend stderr:', data.toString());
|
||||
});
|
||||
|
||||
backendProcess.on('close', (code) => {
|
||||
console.log('Backend process exited with code:', code);
|
||||
backendProcess = null;
|
||||
});
|
||||
|
||||
backendProcess.on('error', (err) => {
|
||||
console.error('Failed to start backend process:', err);
|
||||
backendProcess = null;
|
||||
});
|
||||
}
|
||||
|
||||
function stopBackendService() {
|
||||
if (backendProcess) {
|
||||
console.log('Stopping backend service...');
|
||||
backendProcess.kill('SIGTERM');
|
||||
backendProcess = null;
|
||||
}
|
||||
|
||||
// 强制杀死所有BodyBalanceBackend.exe进程
|
||||
try {
|
||||
const { exec } = require('child_process');
|
||||
exec('taskkill /f /im BodyBalanceBackend.exe', (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
// 如果没有找到进程,taskkill会返回错误,这是正常的
|
||||
if (!error.message.includes('not found')) {
|
||||
console.error('Error killing BodyBalanceBackend processes:', error.message);
|
||||
}
|
||||
} else {
|
||||
console.log('Successfully killed all BodyBalanceBackend processes:', stdout);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Failed to execute taskkill command:', err);
|
||||
}
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
},
|
||||
icon: path.join(__dirname, '../../assets/icon.ico'),
|
||||
show: false,
|
||||
fullscreen: false,
|
||||
frame: true,
|
||||
autoHideMenuBar: true,
|
||||
backgroundColor: '#000000'
|
||||
});
|
||||
|
||||
// 开发环境加载本地服务器,生产环境加载打包后的文件
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
if (isDev) {
|
||||
mainWindow.loadURL('http://localhost:3000');
|
||||
// mainWindow.webContents.openDevTools(); // 如需调试,请手动打开或在 did-finish-load 之后打开
|
||||
} else {
|
||||
// 启动后端服务
|
||||
startBackendService();
|
||||
// 延迟1秒后启动本地HTTP服务器
|
||||
setTimeout(() => {
|
||||
startLocalServer(() => {
|
||||
mainWindow.loadURL('http://localhost:3000');
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 窗口就绪后再显示,避免白屏/闪烁
|
||||
mainWindow.once('ready-to-show', () => {
|
||||
mainWindow.show();
|
||||
});
|
||||
|
||||
// 监听页面加载完成事件
|
||||
mainWindow.webContents.once('did-finish-load', () => {
|
||||
console.log('Page loaded completely');
|
||||
});
|
||||
|
||||
|
||||
|
||||
// 添加加载失败的处理
|
||||
mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL) => {
|
||||
console.log('Failed to load:', errorDescription, 'URL:', validatedURL);
|
||||
mainWindow.show(); // 即使加载失败也显示窗口
|
||||
});
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null;
|
||||
if (localServer) {
|
||||
localServer.close();
|
||||
}
|
||||
// 关闭后端服务
|
||||
stopBackendService();
|
||||
});
|
||||
}
|
||||
|
||||
function startLocalServer(callback) {
|
||||
const staticPath = path.join(__dirname, '../dist/');
|
||||
|
||||
localServer = http.createServer((req, res) => {
|
||||
const parsedUrl = url.parse(req.url);
|
||||
let pathname = parsedUrl.pathname;
|
||||
|
||||
// 默认加载index.html
|
||||
if (pathname === '/') {
|
||||
pathname = '/index.html';
|
||||
}
|
||||
|
||||
const filePath = path.join(staticPath, pathname);
|
||||
|
||||
fs.readFile(filePath, (err, data) => {
|
||||
if (err) {
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.end('File not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置正确的Content-Type
|
||||
const ext = path.extname(filePath);
|
||||
let contentType = 'text/html';
|
||||
|
||||
switch (ext) {
|
||||
case '.js':
|
||||
contentType = 'application/javascript';
|
||||
break;
|
||||
case '.css':
|
||||
contentType = 'text/css';
|
||||
break;
|
||||
case '.json':
|
||||
contentType = 'application/json';
|
||||
break;
|
||||
case '.png':
|
||||
contentType = 'image/png';
|
||||
break;
|
||||
case '.jpg':
|
||||
case '.jpeg':
|
||||
contentType = 'image/jpeg';
|
||||
break;
|
||||
case '.svg':
|
||||
contentType = 'image/svg+xml';
|
||||
break;
|
||||
}
|
||||
|
||||
res.writeHead(200, { 'Content-Type': contentType });
|
||||
res.end(data);
|
||||
});
|
||||
});
|
||||
|
||||
localServer.listen(3000, 'localhost', () => {
|
||||
console.log('Local server started on http://localhost:3000');
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
// 应用事件处理
|
||||
// 关闭硬件加速以规避 GPU 进程异常导致的闪烁
|
||||
app.disableHardwareAcceleration();
|
||||
app.whenReady().then(createWindow);
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
if (localServer) {
|
||||
localServer.close();
|
||||
}
|
||||
// 关闭后端服务
|
||||
stopBackendService();
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// 应用退出前清理资源
|
||||
app.on('before-quit', () => {
|
||||
stopBackendService();
|
||||
});
|
2
frontend/src/renderer/main/preload.js
Normal file
2
frontend/src/renderer/main/preload.js
Normal file
@ -0,0 +1,2 @@
|
||||
const { contextBridge } = require('electron');
|
||||
|
5471
frontend/src/renderer/package-lock.json
generated
5471
frontend/src/renderer/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,17 +2,23 @@
|
||||
"name": "body-balance-renderer",
|
||||
"version": "1.0.0",
|
||||
"description": "平衡体态检测系统前端界面",
|
||||
"main": "main/main.js",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"dev": "concurrently \"npm run dev:renderer\" \"wait-on http://localhost:3000 && npm run dev:electron\"",
|
||||
"dev:renderer": "vite",
|
||||
"dev:electron": "electron .",
|
||||
"build": "npm run build:renderer && npm run build:electron",
|
||||
"build:renderer": "vite build",
|
||||
"build:electron": "electron-builder",
|
||||
"pack": "electron-packager . electron-browser --platform=win32 --arch=x64 --out=dist --overwrite",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"author": "Zhengsl",
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"axios": "^1.5.0",
|
||||
"echarts": "^5.4.3",
|
||||
"element-plus": "^2.3.9",
|
||||
"html2canvas": "^1.4.1",
|
||||
"pinia": "^2.1.6",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"vue": "^3.3.4",
|
||||
@ -20,8 +26,45 @@
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.3.4",
|
||||
"sass": "^1.66.1",
|
||||
"vite": "^4.4.9"
|
||||
"@vitejs/plugin-vue": "^4.4.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"electron": "^27.3.11",
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-packager": "^17.1.2",
|
||||
"vite": "^4.4.9",
|
||||
"wait-on": "^7.0.1"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.bodybalance.evaluation",
|
||||
"productName": "平衡体态检测系统",
|
||||
"electronVersion": "27.3.11",
|
||||
"electronDownload": {
|
||||
"mirror": "https://npmmirror.com/mirrors/electron/"
|
||||
},
|
||||
"directories": {
|
||||
"output": "dist-electron"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"main/**/*",
|
||||
"package.json"
|
||||
],
|
||||
"extraFiles": [
|
||||
{
|
||||
"from": "../../../backend/dist",
|
||||
"to": "resources/backend",
|
||||
"filter": ["**/*"]
|
||||
}
|
||||
],
|
||||
"win": {
|
||||
"target": "nsis",
|
||||
"icon": "../../../install/assets/icon.ico"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ api.interceptors.request.use(
|
||||
config.baseURL = window.electronAPI.getBackendUrl()
|
||||
} else {
|
||||
config.baseURL = 'http://localhost:5000'
|
||||
|
||||
}
|
||||
|
||||
// 添加时间戳防止缓存
|
||||
@ -73,7 +72,7 @@ api.interceptors.response.use(
|
||||
message = error.message || '未知错误'
|
||||
}
|
||||
|
||||
ElMessage.error(message)
|
||||
// ElMessage.error(message)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
@ -33,6 +33,7 @@
|
||||
:type="passwordVisible ? 'text' : 'password'"
|
||||
placeholder="请输入密码"
|
||||
class="custom-input"
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
<template #suffix>
|
||||
<el-icon
|
||||
@ -414,11 +415,12 @@ const handleLogin = async () => {
|
||||
router.push('/')
|
||||
} else {
|
||||
// 根据错误类型显示具体提示
|
||||
if (result.error && result.error.includes('用户不存在')) {
|
||||
const errorMsg = String(result.error || '')
|
||||
if (errorMsg.includes('用户不存在')) {
|
||||
showError('用户名或密码错误,请重新输入!')
|
||||
} else if (result.error && result.error.includes('密码错误')) {
|
||||
} else if (errorMsg.includes('密码错误')) {
|
||||
showError('用户名或密码错误,请重新输入!')
|
||||
} else if (result.error && result.error.includes('用户未激活')) {
|
||||
} else if (errorMsg.includes('用户未激活')) {
|
||||
showError('账户未激活,请联系管理员激活后再登录')
|
||||
} else {
|
||||
showError('用户名或密码错误,请重新输入!')
|
||||
|
117
frontend/src/renderer/test-electron.js
Normal file
117
frontend/src/renderer/test-electron.js
Normal file
@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Electron功能测试脚本
|
||||
* 用于验证electron环境是否正确配置
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
console.log('🔍 Electron环境检测...');
|
||||
console.log('='.repeat(50));
|
||||
|
||||
// 检查package.json配置
|
||||
const packagePath = path.join(__dirname, 'package.json');
|
||||
if (fs.existsSync(packagePath)) {
|
||||
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
||||
console.log('✅ package.json 存在');
|
||||
console.log('📦 项目名称:', pkg.name);
|
||||
console.log('📦 版本:', pkg.version);
|
||||
|
||||
// 检查electron相关脚本
|
||||
if (pkg.scripts && pkg.scripts['electron:dev']) {
|
||||
console.log('✅ electron:dev 脚本已配置');
|
||||
} else {
|
||||
console.log('❌ electron:dev 脚本未配置');
|
||||
}
|
||||
|
||||
// 检查electron依赖
|
||||
const hasElectron = pkg.devDependencies && pkg.devDependencies.electron;
|
||||
if (hasElectron) {
|
||||
console.log('✅ electron 依赖已配置:', pkg.devDependencies.electron);
|
||||
} else {
|
||||
console.log('❌ electron 依赖未配置');
|
||||
}
|
||||
|
||||
// 检查electron-builder
|
||||
const hasBuilder = pkg.devDependencies && pkg.devDependencies['electron-builder'];
|
||||
if (hasBuilder) {
|
||||
console.log('✅ electron-builder 依赖已配置:', pkg.devDependencies['electron-builder']);
|
||||
} else {
|
||||
console.log('❌ electron-builder 依赖未配置');
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log('❌ package.json 不存在');
|
||||
}
|
||||
|
||||
// 检查main进程文件
|
||||
const mainPath = path.join(__dirname, '../main/main.js');
|
||||
if (fs.existsSync(mainPath)) {
|
||||
console.log('✅ main.js 存在:', mainPath);
|
||||
} else {
|
||||
console.log('❌ main.js 不存在:', mainPath);
|
||||
}
|
||||
|
||||
// 检查preload文件
|
||||
const preloadPath = path.join(__dirname, '../main/preload.js');
|
||||
if (fs.existsSync(preloadPath)) {
|
||||
console.log('✅ preload.js 存在:', preloadPath);
|
||||
} else {
|
||||
console.log('❌ preload.js 不存在:', preloadPath);
|
||||
}
|
||||
|
||||
// 检查vite配置
|
||||
const viteConfigPath = path.join(__dirname, 'vite.config.js');
|
||||
if (fs.existsSync(viteConfigPath)) {
|
||||
console.log('✅ vite.config.js 存在');
|
||||
const viteConfig = fs.readFileSync(viteConfigPath, 'utf8');
|
||||
if (viteConfig.includes('electron-renderer')) {
|
||||
console.log('✅ vite配置包含electron-renderer目标');
|
||||
} else {
|
||||
console.log('⚠️ vite配置可能需要electron-renderer目标');
|
||||
}
|
||||
} else {
|
||||
console.log('❌ vite.config.js 不存在');
|
||||
}
|
||||
|
||||
// 检查node_modules
|
||||
const nodeModulesPath = path.join(__dirname, 'node_modules');
|
||||
if (fs.existsSync(nodeModulesPath)) {
|
||||
console.log('✅ node_modules 存在');
|
||||
|
||||
// 检查electron是否安装
|
||||
const electronPath = path.join(nodeModulesPath, 'electron');
|
||||
if (fs.existsSync(electronPath)) {
|
||||
console.log('✅ electron 已安装');
|
||||
} else {
|
||||
console.log('❌ electron 未安装,请运行 npm install');
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log('❌ node_modules 不存在,请运行 npm install');
|
||||
}
|
||||
|
||||
// 检查启动脚本
|
||||
const startScriptPath = path.join(__dirname, 'start-electron.bat');
|
||||
if (fs.existsSync(startScriptPath)) {
|
||||
console.log('✅ start-electron.bat 启动脚本存在');
|
||||
} else {
|
||||
console.log('❌ start-electron.bat 启动脚本不存在');
|
||||
}
|
||||
|
||||
const devScriptPath = path.join(__dirname, 'electron-dev.js');
|
||||
if (fs.existsSync(devScriptPath)) {
|
||||
console.log('✅ electron-dev.js 开发脚本存在');
|
||||
} else {
|
||||
console.log('❌ electron-dev.js 开发脚本不存在');
|
||||
}
|
||||
|
||||
console.log('='.repeat(50));
|
||||
console.log('🎯 检测完成!');
|
||||
console.log('');
|
||||
console.log('📋 使用说明:');
|
||||
console.log('1. 确保依赖已安装: npm install');
|
||||
console.log('2. 启动开发环境: npm run electron:dev');
|
||||
console.log('3. 或使用批处理: start-electron.bat');
|
||||
console.log('4. 或使用脚本: node electron-dev.js');
|
@ -12,7 +12,10 @@ export default defineConfig({
|
||||
input: {
|
||||
main: resolve(__dirname, 'index.html')
|
||||
}
|
||||
}
|
||||
},
|
||||
// Electron环境优化
|
||||
target: 'esnext',
|
||||
minify: false
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
@ -21,6 +24,13 @@ export default defineConfig({
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
host: '0.0.0.0'
|
||||
host: '0.0.0.0',
|
||||
// 开发服务器配置
|
||||
cors: true,
|
||||
strictPort: true
|
||||
},
|
||||
// Electron环境变量
|
||||
define: {
|
||||
__IS_ELECTRON__: 'true'
|
||||
}
|
||||
});
|
@ -1,200 +1,21 @@
|
||||
# 最小功能测试框架
|
||||
# 平衡体态检测系统安装包
|
||||
|
||||
## 概述
|
||||
## 目录结构
|
||||
- `backend/` - 后端程序文件
|
||||
- `frontend/` - 前端Electron应用
|
||||
- `启动系统.bat` - 系统启动脚本
|
||||
|
||||
这是一个最小功能测试框架,用于验证 Flask + SocketIO + threading 技术栈在打包成 exe 后的可用性。
|
||||
## 使用方法
|
||||
1. 双击 `启动系统.bat` 启动系统
|
||||
2. 系统会自动启动后端服务和前端界面
|
||||
3. 如果需要单独启动,可以直接运行前端exe文件
|
||||
|
||||
## 功能特性
|
||||
## 系统要求
|
||||
- Windows 10 或更高版本
|
||||
- 至少4GB内存
|
||||
- 支持USB设备连接
|
||||
|
||||
- ✅ Flask HTTP 服务
|
||||
- ✅ SocketIO WebSocket 服务
|
||||
- ✅ 强制使用 threading 异步模式
|
||||
- ✅ 完整的前端测试界面
|
||||
- ✅ HTTP API 测试
|
||||
- ✅ WebSocket 连接测试
|
||||
- ✅ 实时消息收发测试
|
||||
- ✅ 系统信息显示
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
install/
|
||||
├── minimal_test_app.py # 主应用文件
|
||||
├── build_minimal.py # 打包脚本
|
||||
├── requirements_minimal.txt # 最小依赖列表
|
||||
└── README.md # 说明文档
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 安装依赖
|
||||
|
||||
```bash
|
||||
pip install -r requirements_minimal.txt
|
||||
```
|
||||
|
||||
### 2. 测试运行
|
||||
|
||||
```bash
|
||||
python minimal_test_app.py
|
||||
```
|
||||
|
||||
然后在浏览器中访问 http://localhost:5000 进行测试。
|
||||
|
||||
### 3. 打包成 exe
|
||||
|
||||
```bash
|
||||
python build_minimal.py
|
||||
```
|
||||
|
||||
### 4. 测试 exe
|
||||
|
||||
打包完成后,运行:
|
||||
|
||||
```bash
|
||||
# 方式1:直接运行
|
||||
dist/MinimalTestApp.exe
|
||||
|
||||
# 方式2:使用脚本
|
||||
dist/start_test.bat
|
||||
```
|
||||
|
||||
## 测试步骤
|
||||
|
||||
### HTTP API 测试
|
||||
|
||||
1. 点击「测试 HTTP API」按钮
|
||||
2. 确认返回成功响应
|
||||
3. 检查响应数据包含服务器信息
|
||||
|
||||
### WebSocket 测试
|
||||
|
||||
1. 页面加载时会自动连接 WebSocket
|
||||
2. 确认连接状态显示为「已连接」
|
||||
3. 点击「发送测试消息」
|
||||
4. 确认能收到服务器响应
|
||||
5. 测试断开和重连功能
|
||||
|
||||
### 系统信息检查
|
||||
|
||||
- 服务器时间:确认时间正确
|
||||
- SocketIO模式:确认显示为 "threading"
|
||||
- Flask版本:确认版本信息
|
||||
|
||||
## 技术要点
|
||||
|
||||
### 异步模式选择
|
||||
|
||||
```python
|
||||
# 强制使用 threading 模式,避免 eventlet/gevent 依赖问题
|
||||
socketio = SocketIO(
|
||||
app,
|
||||
cors_allowed_origins='*',
|
||||
async_mode='threading', # 关键配置
|
||||
logger=False,
|
||||
engineio_logger=False
|
||||
)
|
||||
```
|
||||
|
||||
### 打包配置
|
||||
|
||||
```python
|
||||
# PyInstaller 隐藏导入配置
|
||||
hiddenimports=[
|
||||
'flask',
|
||||
'flask_socketio',
|
||||
'socketio',
|
||||
'engineio',
|
||||
'engineio.async_drivers.threading', # 关键:threading 驱动
|
||||
'socketio.namespace',
|
||||
'dns',
|
||||
'dns.resolver',
|
||||
'dns.asyncresolver'
|
||||
],
|
||||
# 排除不需要的异步模式
|
||||
excludes=[
|
||||
'eventlet',
|
||||
'gevent',
|
||||
'gevent_uwsgi'
|
||||
]
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **"Invalid async_mode specified" 错误**
|
||||
- 确认已安装所有依赖
|
||||
- 检查 PyInstaller 隐藏导入配置
|
||||
- 确认排除了不需要的异步模式
|
||||
|
||||
2. **WebSocket 连接失败**
|
||||
- 检查防火墙设置
|
||||
- 确认端口 5000 未被占用
|
||||
- 查看控制台错误信息
|
||||
|
||||
3. **打包失败**
|
||||
- 确认 PyInstaller 版本兼容
|
||||
- 检查依赖版本冲突
|
||||
- 查看详细错误输出
|
||||
|
||||
### 调试模式
|
||||
|
||||
如需调试,可以修改 `minimal_test_app.py`:
|
||||
|
||||
```python
|
||||
# 启用调试模式
|
||||
socketio.run(
|
||||
app,
|
||||
host='0.0.0.0',
|
||||
port=5000,
|
||||
debug=True, # 启用调试
|
||||
allow_unsafe_werkzeug=True
|
||||
)
|
||||
```
|
||||
|
||||
## 扩展指南
|
||||
|
||||
### 添加新功能
|
||||
|
||||
1. **新增 HTTP 路由**
|
||||
```python
|
||||
@app.route('/api/new-endpoint')
|
||||
def new_endpoint():
|
||||
return jsonify({'message': 'New endpoint'})
|
||||
```
|
||||
|
||||
2. **新增 SocketIO 事件**
|
||||
```python
|
||||
@socketio.on('new_event')
|
||||
def handle_new_event(data):
|
||||
emit('response', {'status': 'received'})
|
||||
```
|
||||
|
||||
3. **添加依赖**
|
||||
- 更新 `requirements_minimal.txt`
|
||||
- 更新 `build_minimal.py` 中的 `hiddenimports`
|
||||
- 重新测试打包
|
||||
|
||||
### 渐进式集成
|
||||
|
||||
按照以下顺序逐步添加业务功能:
|
||||
|
||||
1. ✅ 基础 Flask + SocketIO(当前阶段)
|
||||
2. 🔄 添加数据库支持
|
||||
3. 🔄 添加文件操作
|
||||
4. 🔄 添加外部库依赖
|
||||
5. 🔄 添加硬件设备支持
|
||||
|
||||
每个阶段都要确保打包和运行正常,出现问题时更容易定位。
|
||||
|
||||
## 版本信息
|
||||
|
||||
- Flask: 2.3.3
|
||||
- Flask-SocketIO: 5.3.6
|
||||
- PyInstaller: 6.1.0
|
||||
- Python: 3.8+
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
## 注意事项
|
||||
- 首次启动可能需要较长时间
|
||||
- 确保防火墙允许程序访问网络
|
||||
- 如遇问题请查看日志文件
|
||||
|
@ -1,224 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
最小功能测试框架打包脚本
|
||||
用于将Flask + SocketIO应用打包成exe文件
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
def check_dependencies():
|
||||
"""检查必需的依赖"""
|
||||
print("检查依赖模块...")
|
||||
required_modules = [
|
||||
'flask',
|
||||
'flask_socketio',
|
||||
'socketio',
|
||||
'engineio'
|
||||
]
|
||||
|
||||
missing_modules = []
|
||||
for module in required_modules:
|
||||
try:
|
||||
__import__(module)
|
||||
print(f"✓ {module}")
|
||||
except ImportError:
|
||||
print(f"✗ {module} (缺失)")
|
||||
missing_modules.append(module)
|
||||
|
||||
if missing_modules:
|
||||
print(f"\n缺失模块: {', '.join(missing_modules)}")
|
||||
print("请运行: pip install -r requirements_minimal.txt")
|
||||
return False
|
||||
|
||||
print("✓ 所有依赖模块检查通过")
|
||||
return True
|
||||
|
||||
def create_spec_file():
|
||||
"""创建PyInstaller spec文件"""
|
||||
spec_content = '''
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
a = Analysis(
|
||||
['minimal_test_app.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[
|
||||
'flask',
|
||||
'flask_socketio',
|
||||
'socketio',
|
||||
'engineio',
|
||||
'engineio.async_drivers.threading',
|
||||
'socketio.namespace',
|
||||
'dns',
|
||||
'dns.resolver',
|
||||
'dns.asyncresolver'
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[
|
||||
'eventlet',
|
||||
'gevent',
|
||||
'gevent_uwsgi'
|
||||
],
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name='MinimalTestApp',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=None
|
||||
)
|
||||
'''
|
||||
|
||||
with open('minimal_test.spec', 'w', encoding='utf-8') as f:
|
||||
f.write(spec_content)
|
||||
|
||||
print("✓ 已创建 minimal_test.spec 文件")
|
||||
|
||||
def clean_build_dirs():
|
||||
"""清理构建目录"""
|
||||
dirs_to_clean = ['build', 'dist', '__pycache__']
|
||||
|
||||
for dir_name in dirs_to_clean:
|
||||
if os.path.exists(dir_name):
|
||||
try:
|
||||
shutil.rmtree(dir_name)
|
||||
print(f"✓ 清理目录: {dir_name}")
|
||||
except Exception as e:
|
||||
print(f"警告: 无法清理目录 {dir_name}: {e}")
|
||||
|
||||
def build_exe():
|
||||
"""构建exe文件"""
|
||||
print("\n开始构建exe文件...")
|
||||
|
||||
try:
|
||||
# 使用PyInstaller构建
|
||||
cmd = [sys.executable, '-m', 'PyInstaller', 'minimal_test.spec', '--clean']
|
||||
print(f"执行命令: {' '.join(cmd)}")
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("✓ 构建成功!")
|
||||
return True
|
||||
else:
|
||||
print(f"✗ 构建失败")
|
||||
print(f"错误输出: {result.stderr}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ 构建过程出错: {e}")
|
||||
return False
|
||||
|
||||
def create_test_script():
|
||||
"""创建测试脚本"""
|
||||
test_script = '''
|
||||
@echo off
|
||||
echo 启动最小功能测试应用...
|
||||
echo.
|
||||
echo 测试说明:
|
||||
echo 1. 应用启动后,在浏览器中访问 http://localhost:5000
|
||||
echo 2. 测试HTTP API和WebSocket功能
|
||||
echo 3. 按Ctrl+C停止应用
|
||||
echo.
|
||||
"MinimalTestApp.exe"
|
||||
pause
|
||||
'''
|
||||
|
||||
with open('dist/start_test.bat', 'w', encoding='utf-8') as f:
|
||||
f.write(test_script)
|
||||
|
||||
print("✓ 创建测试脚本: dist/start_test.bat")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("="*60)
|
||||
print("最小功能测试框架 - 打包工具")
|
||||
print("="*60)
|
||||
print()
|
||||
|
||||
# 检查当前目录
|
||||
if not os.path.exists('minimal_test_app.py'):
|
||||
print("✗ 错误: 找不到 minimal_test_app.py 文件")
|
||||
print("请确保在正确的目录下运行此脚本")
|
||||
input("按回车键退出...")
|
||||
return
|
||||
|
||||
# 检查依赖
|
||||
if not check_dependencies():
|
||||
input("按回车键退出...")
|
||||
return
|
||||
|
||||
print()
|
||||
|
||||
# 清理构建目录
|
||||
print("清理构建目录...")
|
||||
clean_build_dirs()
|
||||
print()
|
||||
|
||||
# 创建spec文件
|
||||
print("创建PyInstaller配置...")
|
||||
create_spec_file()
|
||||
print()
|
||||
|
||||
# 构建exe
|
||||
if build_exe():
|
||||
print()
|
||||
print("后处理...")
|
||||
|
||||
# 检查生成的exe文件
|
||||
exe_path = 'dist/MinimalTestApp.exe'
|
||||
if os.path.exists(exe_path):
|
||||
print(f"✓ exe文件位置: {exe_path}")
|
||||
|
||||
# 创建测试脚本
|
||||
create_test_script()
|
||||
|
||||
print()
|
||||
print("🎉 打包完成!")
|
||||
print()
|
||||
print("测试方式:")
|
||||
print("1. 直接运行: dist/MinimalTestApp.exe")
|
||||
print("2. 使用脚本: dist/start_test.bat")
|
||||
print()
|
||||
print("测试步骤:")
|
||||
print("1. 启动应用")
|
||||
print("2. 浏览器访问 http://localhost:5000")
|
||||
print("3. 测试HTTP API和WebSocket功能")
|
||||
print("4. 确认所有功能正常工作")
|
||||
|
||||
else:
|
||||
print("✗ 错误: 未找到生成的exe文件")
|
||||
else:
|
||||
print("\n✗ 打包失败")
|
||||
|
||||
print()
|
||||
input("按回车键退出...")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,53 +0,0 @@
|
||||
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
a = Analysis(
|
||||
['minimal_test_app.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[
|
||||
'flask',
|
||||
'flask_socketio',
|
||||
'socketio',
|
||||
'engineio',
|
||||
'engineio.async_drivers.threading',
|
||||
'socketio.namespace',
|
||||
'dns',
|
||||
'dns.resolver',
|
||||
'dns.asyncresolver'
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[
|
||||
'eventlet',
|
||||
'gevent',
|
||||
'gevent_uwsgi'
|
||||
],
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name='MinimalTestApp',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=None
|
||||
)
|
@ -1,274 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
最小功能测试框架 - Flask + SocketIO + threading
|
||||
用于验证打包exe后HTTP和WebSocket服务的可用性
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from flask import Flask, render_template_string, jsonify
|
||||
from flask_socketio import SocketIO, emit
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 创建Flask应用
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = 'minimal-test-secret-key-2024'
|
||||
|
||||
# 初始化SocketIO,强制使用threading模式
|
||||
try:
|
||||
logger.info("初始化SocketIO(threading模式)...")
|
||||
socketio = SocketIO(
|
||||
app,
|
||||
cors_allowed_origins='*',
|
||||
async_mode='threading',
|
||||
logger=False,
|
||||
engineio_logger=False
|
||||
)
|
||||
logger.info(f"SocketIO初始化成功,异步模式: {socketio.async_mode}")
|
||||
except Exception as e:
|
||||
logger.error(f"SocketIO初始化失败: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# HTML测试页面模板
|
||||
HTML_TEMPLATE = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>最小功能测试 - Flask + SocketIO</title>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 40px; }
|
||||
.container { max-width: 800px; margin: 0 auto; }
|
||||
.section { margin: 20px 0; padding: 20px; border: 1px solid #ddd; border-radius: 5px; }
|
||||
.success { color: green; }
|
||||
.error { color: red; }
|
||||
.info { color: blue; }
|
||||
button { padding: 10px 20px; margin: 5px; cursor: pointer; }
|
||||
#messages { height: 200px; overflow-y: auto; border: 1px solid #ccc; padding: 10px; background: #f9f9f9; }
|
||||
.message { margin: 5px 0; }
|
||||
</style>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>最小功能测试框架</h1>
|
||||
<p class="info">测试Flask HTTP服务和SocketIO WebSocket服务</p>
|
||||
|
||||
<!-- HTTP API测试 -->
|
||||
<div class="section">
|
||||
<h2>HTTP API 测试</h2>
|
||||
<button onclick="testHttpApi()">测试 HTTP API</button>
|
||||
<div id="http-result"></div>
|
||||
</div>
|
||||
|
||||
<!-- WebSocket测试 -->
|
||||
<div class="section">
|
||||
<h2>WebSocket 测试</h2>
|
||||
<button onclick="connectSocket()">连接 WebSocket</button>
|
||||
<button onclick="disconnectSocket()">断开连接</button>
|
||||
<button onclick="sendTestMessage()">发送测试消息</button>
|
||||
<div>连接状态: <span id="connection-status" class="error">未连接</span></div>
|
||||
<div id="messages"></div>
|
||||
</div>
|
||||
|
||||
<!-- 系统信息 -->
|
||||
<div class="section">
|
||||
<h2>系统信息</h2>
|
||||
<div id="system-info">
|
||||
<p>服务器时间: {{ server_time }}</p>
|
||||
<p>SocketIO模式: {{ socketio_mode }}</p>
|
||||
<p>Flask版本: {{ flask_version }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let socket = null;
|
||||
|
||||
function addMessage(message, type = 'info') {
|
||||
const messages = document.getElementById('messages');
|
||||
const div = document.createElement('div');
|
||||
div.className = `message ${type}`;
|
||||
div.innerHTML = `[${new Date().toLocaleTimeString()}] ${message}`;
|
||||
messages.appendChild(div);
|
||||
messages.scrollTop = messages.scrollHeight;
|
||||
}
|
||||
|
||||
function testHttpApi() {
|
||||
const resultDiv = document.getElementById('http-result');
|
||||
resultDiv.innerHTML = '测试中...';
|
||||
|
||||
fetch('/api/test')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
resultDiv.innerHTML = `<span class="success">✓ HTTP API 正常</span><br>响应: ${JSON.stringify(data, null, 2)}`;
|
||||
})
|
||||
.catch(error => {
|
||||
resultDiv.innerHTML = `<span class="error">✗ HTTP API 错误: ${error}</span>`;
|
||||
});
|
||||
}
|
||||
|
||||
function connectSocket() {
|
||||
if (socket && socket.connected) {
|
||||
addMessage('WebSocket已连接', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
socket = io();
|
||||
|
||||
socket.on('connect', function() {
|
||||
document.getElementById('connection-status').innerHTML = '<span class="success">已连接</span>';
|
||||
addMessage('WebSocket连接成功', 'success');
|
||||
});
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
document.getElementById('connection-status').innerHTML = '<span class="error">未连接</span>';
|
||||
addMessage('WebSocket连接断开', 'error');
|
||||
});
|
||||
|
||||
socket.on('test_response', function(data) {
|
||||
addMessage(`收到服务器响应: ${JSON.stringify(data)}`, 'success');
|
||||
});
|
||||
|
||||
socket.on('server_message', function(data) {
|
||||
addMessage(`服务器消息: ${data.message}`, 'info');
|
||||
});
|
||||
}
|
||||
|
||||
function disconnectSocket() {
|
||||
if (socket) {
|
||||
socket.disconnect();
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
|
||||
function sendTestMessage() {
|
||||
if (!socket || !socket.connected) {
|
||||
addMessage('请先连接WebSocket', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const testData = {
|
||||
message: 'Hello from client',
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
socket.emit('test_message', testData);
|
||||
addMessage(`发送消息: ${JSON.stringify(testData)}`, 'info');
|
||||
}
|
||||
|
||||
// 页面加载时自动连接
|
||||
window.onload = function() {
|
||||
connectSocket();
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
# HTTP路由
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""主页面"""
|
||||
import flask
|
||||
return render_template_string(HTML_TEMPLATE,
|
||||
server_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
socketio_mode=socketio.async_mode,
|
||||
flask_version=flask.__version__
|
||||
)
|
||||
|
||||
@app.route('/api/test')
|
||||
def api_test():
|
||||
"""HTTP API测试接口"""
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'message': 'HTTP API工作正常',
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'server_info': {
|
||||
'socketio_mode': socketio.async_mode,
|
||||
'working_directory': os.getcwd()
|
||||
}
|
||||
})
|
||||
|
||||
@app.route('/health')
|
||||
def health_check():
|
||||
"""健康检查接口"""
|
||||
return jsonify({
|
||||
'status': 'healthy',
|
||||
'services': {
|
||||
'http': 'running',
|
||||
'websocket': 'running',
|
||||
'socketio_mode': socketio.async_mode
|
||||
},
|
||||
'timestamp': datetime.now().isoformat()
|
||||
})
|
||||
|
||||
# SocketIO事件处理
|
||||
@socketio.on('connect')
|
||||
def handle_connect():
|
||||
"""客户端连接事件"""
|
||||
logger.info(f"客户端连接: {request.sid if 'request' in globals() else 'unknown'}")
|
||||
emit('server_message', {
|
||||
'message': 'WebSocket连接成功',
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'socketio_mode': socketio.async_mode
|
||||
})
|
||||
|
||||
@socketio.on('disconnect')
|
||||
def handle_disconnect():
|
||||
"""客户端断开连接事件"""
|
||||
logger.info(f"客户端断开连接: {request.sid if 'request' in globals() else 'unknown'}")
|
||||
|
||||
@socketio.on('test_message')
|
||||
def handle_test_message(data):
|
||||
"""处理测试消息"""
|
||||
logger.info(f"收到测试消息: {data}")
|
||||
|
||||
# 回复客户端
|
||||
emit('test_response', {
|
||||
'status': 'received',
|
||||
'original_message': data,
|
||||
'server_timestamp': datetime.now().isoformat(),
|
||||
'socketio_mode': socketio.async_mode
|
||||
})
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
logger.info("="*50)
|
||||
logger.info("最小功能测试框架启动")
|
||||
logger.info("="*50)
|
||||
logger.info(f"工作目录: {os.getcwd()}")
|
||||
logger.info(f"Python版本: {sys.version}")
|
||||
logger.info(f"SocketIO异步模式: {socketio.async_mode}")
|
||||
|
||||
try:
|
||||
# 启动服务器
|
||||
logger.info("启动服务器 http://localhost:5000")
|
||||
logger.info("按 Ctrl+C 停止服务器")
|
||||
|
||||
socketio.run(
|
||||
app,
|
||||
host='0.0.0.0',
|
||||
port=5000,
|
||||
debug=False,
|
||||
allow_unsafe_werkzeug=True
|
||||
)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("用户中断,正在关闭服务器...")
|
||||
except Exception as e:
|
||||
logger.error(f"服务器启动失败: {e}")
|
||||
input("按回车键退出...")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,110 +0,0 @@
|
||||
@echo off
|
||||
echo ========================================
|
||||
echo Minimal Test Framework - Quick Start
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
:menu
|
||||
echo Please select an option:
|
||||
echo 1. Install dependencies
|
||||
echo 2. Test run (development mode)
|
||||
echo 3. Build exe
|
||||
echo 4. Run built exe
|
||||
echo 5. Show help
|
||||
echo 0. Exit
|
||||
echo.
|
||||
set /p choice=Enter option (0-5):
|
||||
|
||||
if "%choice%"=="1" goto install_deps
|
||||
if "%choice%"=="2" goto test_run
|
||||
if "%choice%"=="3" goto build_exe
|
||||
if "%choice%"=="4" goto run_exe
|
||||
if "%choice%"=="5" goto show_help
|
||||
if "%choice%"=="0" goto exit
|
||||
echo Invalid option, please try again
|
||||
echo.
|
||||
goto menu
|
||||
|
||||
:install_deps
|
||||
echo.
|
||||
echo Installing dependencies...
|
||||
pip install -r requirements_minimal.txt
|
||||
if %errorlevel% equ 0 (
|
||||
echo Dependencies installed successfully
|
||||
) else (
|
||||
echo Failed to install dependencies
|
||||
)
|
||||
echo.
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:test_run
|
||||
echo.
|
||||
echo Starting test server...
|
||||
echo After server starts, visit http://localhost:5000 in your browser
|
||||
echo Press Ctrl+C to stop the server
|
||||
echo.
|
||||
python minimal_test_app.py
|
||||
echo.
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:build_exe
|
||||
echo.
|
||||
echo Starting build...
|
||||
python build_minimal.py
|
||||
echo.
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:run_exe
|
||||
echo.
|
||||
if exist "dist\MinimalTestApp.exe" (
|
||||
echo Starting built application...
|
||||
echo After app starts, visit http://localhost:5000 in your browser
|
||||
echo.
|
||||
cd dist
|
||||
MinimalTestApp.exe
|
||||
cd ..
|
||||
) else (
|
||||
echo dist\MinimalTestApp.exe not found
|
||||
echo Please run option 3 to build first
|
||||
)
|
||||
echo.
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:show_help
|
||||
echo.
|
||||
echo ========================================
|
||||
echo Usage Instructions
|
||||
echo ========================================
|
||||
echo.
|
||||
echo 1. First time: select "1. Install dependencies"
|
||||
echo 2. After installation: select "2. Test run" to verify
|
||||
echo 3. After testing: select "3. Build exe"
|
||||
echo 4. After building: select "4. Run built exe" to verify
|
||||
echo.
|
||||
echo Test Steps:
|
||||
echo - HTTP API test: Click "Test HTTP API" button on page
|
||||
echo - WebSocket test: Page auto-connects, can send test messages
|
||||
echo - System info: Check if SocketIO mode shows "threading"
|
||||
echo.
|
||||
echo Files:
|
||||
echo - minimal_test_app.py: Main application
|
||||
echo - build_minimal.py: Build script
|
||||
echo - requirements_minimal.txt: Dependencies
|
||||
echo - README.md: Detailed documentation
|
||||
echo.
|
||||
echo Troubleshooting:
|
||||
echo - If "Invalid async_mode specified" error occurs
|
||||
echo check if dependencies are fully installed
|
||||
echo - If WebSocket connection fails, check firewall settings
|
||||
echo - See README.md for detailed instructions
|
||||
echo.
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:exit
|
||||
echo Thank you for using!
|
||||
exit /b 0
|
@ -1,11 +0,0 @@
|
||||
# 最小功能测试框架依赖
|
||||
# 只包含Flask + SocketIO + threading的核心依赖
|
||||
|
||||
Flask==2.3.3
|
||||
Flask-SocketIO==5.3.6
|
||||
Werkzeug==2.3.7
|
||||
python-socketio==5.8.0
|
||||
python-engineio==4.7.1
|
||||
|
||||
# 打包工具
|
||||
PyInstaller>=6.10.0
|
6334
package-lock.json
generated
6334
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
122
package.json
122
package.json
@ -1,122 +0,0 @@
|
||||
{
|
||||
"name": "body-balance-evaluation",
|
||||
"version": "1.0.0",
|
||||
"description": "身体平衡评估系统 - 基于多传感器融合技术的专业平衡能力评估与分析系统",
|
||||
"main": "src/main/main.js",
|
||||
"scripts": {
|
||||
"dev": "electron .",
|
||||
"build": "npm run build:renderer && npm run build:main",
|
||||
"build:renderer": "cd src/renderer && npm run build",
|
||||
"build:main": "electron-builder",
|
||||
"pack": "electron-builder --dir",
|
||||
"dist": "electron-builder",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"start": "electron .",
|
||||
"start:dev": "electron . --mode development",
|
||||
"start:prod": "electron . --mode production",
|
||||
"install:backend": "pip install -r backend/requirements.txt",
|
||||
"install:frontend": "cd src/renderer && npm install",
|
||||
"install:all": "npm run install:backend && npm run install:frontend",
|
||||
"test:backend": "cd backend && python -m pytest tests/ -v",
|
||||
"test:frontend": "cd src/renderer && npm run test",
|
||||
"lint:backend": "cd backend && flake8 . --max-line-length=88 --exclude=venv,__pycache__",
|
||||
"lint:frontend": "cd src/renderer && npm run lint",
|
||||
"format:backend": "cd backend && black . --line-length=88",
|
||||
"format:frontend": "cd src/renderer && npm run format",
|
||||
"clean": "node -e \"const fs = require('fs'); const path = require('path'); ['logs', 'temp', 'node_modules/.cache'].forEach(d => { try { fs.rmSync(d, {recursive: true, force: true}); } catch(e) {} });\"",
|
||||
"clean:data": "node -e \"const fs = require('fs'); try { fs.rmSync('data', {recursive: true, force: true}); } catch(e) {}\"",
|
||||
"backup": "node -e \"const fs = require('fs'); const path = require('path'); const archiver = require('archiver'); const date = new Date().toISOString().slice(0,19).replace(/:/g,'-'); const output = fs.createWriteStream(`backup_${date}_data.zip`); const archive = archiver('zip'); archive.pipe(output); archive.directory('data/', false); archive.finalize();\"",
|
||||
"setup": "npm run install:all && node -e \"const fs = require('fs'); ['data', 'data/patients', 'data/sessions', 'data/exports', 'data/backups', 'logs', 'temp'].forEach(d => fs.mkdirSync(d, {recursive: true}));\"",
|
||||
"check": "node --version && npm --version && electron --version",
|
||||
"test": "npm run test:backend && npm run test:frontend"
|
||||
},
|
||||
"keywords": [
|
||||
"balance",
|
||||
"posture",
|
||||
"assessment",
|
||||
"healthcare",
|
||||
"rehabilitation",
|
||||
"sensors",
|
||||
"computer-vision",
|
||||
"medical-device",
|
||||
"electron",
|
||||
"vue",
|
||||
"mediapipe"
|
||||
],
|
||||
"author": {
|
||||
"name": "zheng shunli",
|
||||
"email": "dev@example.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/example/body-balance-evaluation.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/example/body-balance-evaluation/issues"
|
||||
},
|
||||
"homepage": "https://github.com/example/body-balance-evaluation#readme",
|
||||
"engines": {
|
||||
"node": ">=16.0.0",
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^7.6.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"electron": "^27.0.0",
|
||||
"electron-builder": "latest",
|
||||
"electron-packager": "^17.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.5.0",
|
||||
"electron-log": "^4.4.8",
|
||||
"element-plus": "^2.10.4",
|
||||
"html2canvas": "^1.4.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"config": {
|
||||
"backend_host": "0.0.0.0",
|
||||
"backend_port": "5000",
|
||||
"frontend_port": "5173"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.bodybalance.evaluation",
|
||||
"productName": "平衡体态检测系统",
|
||||
"electronDist": "D:\\electron-v36.4.0-win32-x64",
|
||||
"electronVersion": "36.4.0",
|
||||
"directories": {
|
||||
"output": "dist"
|
||||
},
|
||||
"files": [
|
||||
"src/main/**/*",
|
||||
"src/renderer/dist/**/*",
|
||||
"backend/dist/**/*",
|
||||
"ffmpeg/**/*",
|
||||
"node_modules/**/*"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "backend/dist",
|
||||
"to": "backend"
|
||||
},
|
||||
{
|
||||
"from": "ffmpeg",
|
||||
"to": "ffmpeg"
|
||||
}
|
||||
],
|
||||
"win": {
|
||||
"target": "nsis",
|
||||
"icon": "assets/icon.ico",
|
||||
"arch": [
|
||||
"x64"
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user