This commit is contained in:
zhaozilong12 2025-08-19 18:16:17 +08:00
commit c4e2b3e741
18 changed files with 497 additions and 382 deletions

View File

@ -14,8 +14,8 @@ a = Analysis(
('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库 ('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库
('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库 ('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库
('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库 ('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库
('dll/smitsense/Wrapper.dll', 'dll/smitsense'), # Wrapper ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSenseUsb库
('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # Wrapper ('dll/smitsense/Wrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类
], ],
hiddenimports=[ hiddenimports=[
'flask', 'flask',

View File

@ -42,8 +42,8 @@ a = Analysis(
('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库 ('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库
('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库 ('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库
('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库 ('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库
('dll/smitsense/Wrapper.dll', 'dll/smitsense'), # Wrapper ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSenseUsb库
('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # Wrapper ('dll/smitsense/Wrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类
], ],
hiddenimports=[ hiddenimports=[
'flask', 'flask',

View File

@ -1,242 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
设备配置HTTP API接口
提供通过HTTP方式设置设备参数的功能
"""
import json
import logging
import sys
import os
from flask import Flask, request, jsonify
from typing import Dict, Any
# 添加路径以支持导入
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from utils.config_manager import ConfigManager
class ConfigAPI:
"""配置API类"""
def __init__(self, app: Flask = None):
"""
初始化配置API
Args:
app: Flask应用实例
"""
self.logger = logging.getLogger(f"{__name__}.ConfigAPI")
self.config_manager = ConfigManager()
if app:
self.init_app(app)
def init_app(self, app: Flask):
"""
初始化Flask应用
Args:
app: Flask应用实例
"""
self.app = app
self._register_routes()
def _register_routes(self):
"""
注册路由
"""
# 获取所有设备配置
@self.app.route('/api/config/devices', methods=['GET'])
def get_all_device_configs():
"""获取所有设备配置"""
try:
configs = self.config_manager.get_all_device_configs()
return jsonify({
'success': True,
'data': configs
})
except Exception as e:
self.logger.error(f"获取设备配置失败: {e}")
return jsonify({
'success': False,
'message': f'获取设备配置失败: {str(e)}'
}), 500
# 获取单个设备配置
@self.app.route('/api/config/devices/<device_name>', methods=['GET'])
def get_device_config(device_name: str):
"""获取单个设备配置"""
try:
if device_name not in ['imu', 'pressure', 'camera', 'femtobolt']:
return jsonify({
'success': False,
'message': f'不支持的设备类型: {device_name}'
}), 400
config = self.config_manager.get_device_config(device_name)
return jsonify({
'success': True,
'data': config
})
except Exception as e:
self.logger.error(f"获取{device_name}配置失败: {e}")
return jsonify({
'success': False,
'message': f'获取{device_name}配置失败: {str(e)}'
}), 500
# 设置IMU配置
@self.app.route('/api/config/devices/imu', methods=['POST'])
def set_imu_config():
"""设置IMU配置"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'message': '请求数据不能为空'
}), 400
result = self.config_manager.set_imu_config(data)
status_code = 200 if result['success'] else 400
return jsonify(result), status_code
except Exception as e:
self.logger.error(f"设置IMU配置失败: {e}")
return jsonify({
'success': False,
'message': f'设置IMU配置失败: {str(e)}'
}), 500
# 设置压力板配置
@self.app.route('/api/config/devices/pressure', methods=['POST'])
def set_pressure_config():
"""设置压力板配置"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'message': '请求数据不能为空'
}), 400
result = self.config_manager.set_pressure_config(data)
status_code = 200 if result['success'] else 400
return jsonify(result), status_code
except Exception as e:
self.logger.error(f"设置压力板配置失败: {e}")
return jsonify({
'success': False,
'message': f'设置压力板配置失败: {str(e)}'
}), 500
# 设置相机配置
@self.app.route('/api/config/devices/camera', methods=['POST'])
def set_camera_config():
"""设置相机配置"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'message': '请求数据不能为空'
}), 400
result = self.config_manager.set_camera_config(data)
status_code = 200 if result['success'] else 400
return jsonify(result), status_code
except Exception as e:
self.logger.error(f"设置相机配置失败: {e}")
return jsonify({
'success': False,
'message': f'设置相机配置失败: {str(e)}'
}), 500
# 设置FemtoBolt配置
@self.app.route('/api/config/devices/femtobolt', methods=['POST'])
def set_femtobolt_config():
"""设置FemtoBolt配置"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'message': '请求数据不能为空'
}), 400
result = self.config_manager.set_femtobolt_config(data)
status_code = 200 if result['success'] else 400
return jsonify(result), status_code
except Exception as e:
self.logger.error(f"设置FemtoBolt配置失败: {e}")
return jsonify({
'success': False,
'message': f'设置FemtoBolt配置失败: {str(e)}'
}), 500
# 重新加载配置
@self.app.route('/api/config/reload', methods=['POST'])
def reload_config():
"""重新加载配置"""
try:
self.config_manager.reload_config()
return jsonify({
'success': True,
'message': '配置重新加载成功'
})
except Exception as e:
self.logger.error(f"重新加载配置失败: {e}")
return jsonify({
'success': False,
'message': f'重新加载配置失败: {str(e)}'
}), 500
# 验证配置
@self.app.route('/api/config/validate', methods=['GET'])
def validate_config():
"""验证配置"""
try:
result = self.config_manager.validate_config()
return jsonify({
'success': True,
'data': result
})
except Exception as e:
self.logger.error(f"验证配置失败: {e}")
return jsonify({
'success': False,
'message': f'验证配置失败: {str(e)}'
}), 500
# 创建独立的Flask应用用于测试
def create_config_app():
"""
创建配置API应用
Returns:
Flask: Flask应用实例
"""
app = Flask(__name__)
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 初始化配置API
config_api = ConfigAPI(app)
return app
if __name__ == '__main__':
# 创建并运行应用
app = create_config_app()
app.run(host='0.0.0.0', port=5002, debug=True)

View File

@ -30,7 +30,7 @@ depth_range_max = 1700
[DEVICES] [DEVICES]
imu_device_type = real imu_device_type = real
imu_port = COM3 imu_port = COM3
imu_baudrate = 9600 imu_baudrate = 115200
pressure_device_type = real pressure_device_type = real
pressure_use_mock = False pressure_use_mock = False
pressure_port = COM5 pressure_port = COM5

View File

@ -43,25 +43,38 @@ class ConfigManager:
Returns: Returns:
str: 配置文件路径 str: 配置文件路径
""" """
import sys
# 可能的配置文件路径 # 可能的配置文件路径
possible_paths = [ possible_paths = []
# os.path.join(os.path.dirname(__file__), 'config.ini')
] # 如果是打包后的exe文件从exe文件同级目录获取
# 配置文件路径
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
# 打包后的可执行文件 # 打包后的exe文件路径
possible_paths.append(os.path.join(os.path.dirname(sys.executable), 'config.ini')) exe_dir = os.path.dirname(sys.executable)
else: possible_paths.append(os.path.join(exe_dir, 'config.ini'))
# 开发环境
possible_paths.append(os.path.join(os.path.dirname(__file__), 'config.ini')) # 开发环境下的路径
possible_paths.extend([
os.path.join(os.path.dirname(__file__), 'config.ini'),
os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'config.ini'), # backend/config.ini
os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), 'config.ini') # 项目根目录/config.ini
])
for path in possible_paths: for path in possible_paths:
abs_path = os.path.abspath(path) abs_path = os.path.abspath(path)
if os.path.exists(abs_path): if os.path.exists(abs_path):
self.logger.info(f"找到配置文件: {abs_path}") self.logger.info(f"找到配置文件: {abs_path}")
return abs_path return abs_path
# 如果都找不到返回默认路径exe同级目录或backend目录
if getattr(sys, 'frozen', False):
default_path = os.path.join(os.path.dirname(sys.executable), 'config.ini')
else:
default_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'config.ini')
self.logger.warning(f"未找到配置文件,使用默认路径: {default_path}")
return default_path
def _load_config(self): def _load_config(self):
""" """
加载配置文件 加载配置文件
@ -515,4 +528,74 @@ class ConfigManager:
'pressure': self.get_device_config('pressure'), 'pressure': self.get_device_config('pressure'),
'camera': self.get_device_config('camera'), 'camera': self.get_device_config('camera'),
'femtobolt': self.get_device_config('femtobolt') 'femtobolt': self.get_device_config('femtobolt')
} }
def set_all_device_configs(self, configs: Dict[str, Dict[str, Any]]) -> Dict[str, Any]:
"""
批量设置所有设备配置
Args:
configs: 所有设备配置数据
{
'imu': {'device_type': 'real', 'port': 'COM7', 'baudrate': 9600},
'pressure': {'device_type': 'real', 'port': 'COM8', 'baudrate': 115200},
'camera': {'device_index': 0, 'width': 1280, 'height': 720, 'fps': 30},
'femtobolt': {'color_resolution': '1080P', 'depth_mode': 'NFOV_UNBINNED', 'fps': 15}
}
Returns:
Dict[str, Any]: 设置结果
"""
try:
results = {}
errors = []
# 逐个设置每个设备的配置
if 'imu' in configs:
result = self.set_imu_config(configs['imu'])
results['imu'] = result
if not result['success']:
errors.append(f"IMU: {result['message']}")
if 'pressure' in configs:
result = self.set_pressure_config(configs['pressure'])
results['pressure'] = result
if not result['success']:
errors.append(f"压力板: {result['message']}")
if 'camera' in configs:
result = self.set_camera_config(configs['camera'])
results['camera'] = result
if not result['success']:
errors.append(f"相机: {result['message']}")
if 'femtobolt' in configs:
result = self.set_femtobolt_config(configs['femtobolt'])
results['femtobolt'] = result
if not result['success']:
errors.append(f"FemtoBolt: {result['message']}")
# 如果有错误,返回部分成功的结果
if errors:
self.logger.warning(f"部分设备配置设置失败: {'; '.join(errors)}")
return {
'success': False,
'message': f'部分设备配置设置失败: {"; ".join(errors)}',
'results': results,
'updated_configs': self.get_all_device_configs()
}
else:
self.logger.info("所有设备配置设置成功")
return {
'success': True,
'message': '所有设备配置设置成功',
'results': results,
'updated_configs': self.get_all_device_configs()
}
except Exception as e:
self.logger.error(f"批量设置设备配置失败: {e}")
return {
'success': False,
'message': f'批量设置设备配置失败: {str(e)}'
}

View File

@ -223,9 +223,9 @@ class AppServer:
self.logger.info('设备协调器初始化完成') self.logger.info('设备协调器初始化完成')
# 启动Flask应用 # 启动Flask应用
host = app_config.get('host', self.host) host = self.host
port = app_config.get('port', self.port) port = self.port
debug = app_config.get('debug', self.debug) debug = self.debug
self.logger.info(f'启动Flask应用 - Host: {host}, Port: {port}, Debug: {debug}') self.logger.info(f'启动Flask应用 - Host: {host}, Port: {port}, Debug: {debug}')
@ -559,15 +559,25 @@ class AppServer:
if not data.get(field): if not data.get(field):
return jsonify({'success': False, 'error': f'{field}不能为空'}), 400 return jsonify({'success': False, 'error': f'{field}不能为空'}), 400
patient_id = self.db_manager.create_patient( patient_data = {
name=data['name'], 'name': data['name'],
gender=data['gender'], 'gender': data['gender'],
age=data['age'], 'age': data['age'],
height=data.get('height'), 'birth_date': data.get('birth_date'),
weight=data.get('weight'), 'nationality': data.get('nationality'),
medical_history=data.get('medical_history', ''), 'residence': data.get('residence'),
notes=data.get('notes', '') 'height': data.get('height'),
) 'weight': data.get('weight'),
'shoe_size': data.get('shoe_size'),
'phone': data.get('phone'),
'email': data.get('email'),
'occupation': data.get('occupation'),
'workplace': data.get('workplace'),
'medical_history': data.get('medical_history', ''),
'notes': data.get('notes', '')
}
patient_id = self.db_manager.create_patient(patient_data)
if patient_id: if patient_id:
return jsonify({ return jsonify({
@ -603,16 +613,26 @@ class AppServer:
try: try:
data = flask_request.get_json() data = flask_request.get_json()
result = self.db_manager.update_patient( patient_data = {
patient_id=patient_id, 'name': data.get('name'),
name=data.get('name'), 'gender': data.get('gender'),
gender=data.get('gender'), 'age': data.get('age'),
age=data.get('age'), 'birth_date': data.get('birth_date'),
height=data.get('height'), 'nationality': data.get('nationality'),
weight=data.get('weight'), 'residence': data.get('residence'),
medical_history=data.get('medical_history'), 'height': data.get('height'),
notes=data.get('notes') 'weight': data.get('weight'),
) 'shoe_size': data.get('shoe_size'),
'phone': data.get('phone'),
'email': data.get('email'),
'occupation': data.get('occupation'),
'workplace': data.get('workplace'),
'medical_history': data.get('medical_history'),
'notes': data.get('notes')
}
self.db_manager.update_patient(patient_id, patient_data)
result = True
if result: if result:
return jsonify({'success': True, 'message': '患者信息更新成功'}) return jsonify({'success': True, 'message': '患者信息更新成功'})
@ -666,6 +686,79 @@ class AppServer:
self.logger.error(f'刷新设备失败: {e}') self.logger.error(f'刷新设备失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500 return jsonify({'success': False, 'error': str(e)}), 500
# ==================== 设备配置API ====================
@self.app.route('/api/config/devices', methods=['GET'])
def get_all_device_configs():
"""获取所有设备配置"""
try:
if self.config_manager:
configs = self.config_manager.get_all_device_configs()
return jsonify({
'success': True,
'data': configs
})
else:
return jsonify({'success': False, 'error': '配置管理器未初始化'}), 500
except Exception as e:
self.logger.error(f"获取设备配置失败: {e}")
return jsonify({
'success': False,
'message': f'获取设备配置失败: {str(e)}'
}), 500
@self.app.route('/api/config/devices/all', methods=['POST'])
def set_all_device_configs():
"""批量设置所有设备配置"""
try:
if not self.config_manager:
return jsonify({'success': False, 'error': '配置管理器未初始化'}), 500
data = flask_request.get_json()
if not data:
return jsonify({
'success': False,
'message': '请求数据不能为空'
}), 400
# 验证数据格式
supported_devices = ['imu', 'pressure', 'camera', 'femtobolt']
for device_name in data.keys():
if device_name not in supported_devices:
return jsonify({
'success': False,
'message': f'不支持的设备类型: {device_name},支持的设备类型: {", ".join(supported_devices)}'
}), 400
result = self.config_manager.set_all_device_configs(data)
# 如果配置设置成功,重启设备数据推送
if result['success']:
try:
self.logger.info("设备配置更新成功,重启设备数据推送...")
# 先停止当前的数据推送
if self.is_pushing_data:
self.stop_device_push_data()
time.sleep(1) # 等待停止完成
# 重新启动设备数据推送
self.start_device_push_data()
result['message'] = result.get('message', '') + ' 设备已重启数据推送。'
self.logger.info("设备配置更新并重启数据推送完成")
except Exception as restart_error:
self.logger.error(f"重启设备数据推送失败: {restart_error}")
result['message'] = result.get('message', '') + f' 但重启设备数据推送失败: {str(restart_error)}'
status_code = 200 if result['success'] else 400
return jsonify(result), status_code
except Exception as e:
self.logger.error(f"批量设置设备配置失败: {e}")
return jsonify({
'success': False,
'message': f'批量设置设备配置失败: {str(e)}'
}), 500
@self.app.route('/api/devices/calibrate', methods=['POST']) @self.app.route('/api/devices/calibrate', methods=['POST'])
def calibrate_device(): def calibrate_device():
"""校准设备""" """校准设备"""

View File

@ -1,40 +0,0 @@
[08/18 20:41:43.488456][debug][24904][Context.cpp:30] Context creating, work_dir=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend
[08/18 20:41:43.488655][debug][24904][Context.cpp:49] Config file version=1.1
[08/18 20:41:43.488739][debug][24904][FrameBufferManager.cpp:23] Max global frame buffer size updated! size=2048.000MB
[08/18 20:41:43.488782][info][24904][Context.cpp:68] Context created with config: default config!
[08/18 20:41:43.488823][info][24904][Context.cpp:73] Work directory=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend, SDK version=v1.10.11-20240724-aeaa107e5
[08/18 20:41:43.489074][debug][24904][DeviceManager.cpp:30] DeviceManager init ...
[08/18 20:41:43.489100][info][24904][MfPal.cpp:105] createObPal: create WinPal!
[08/18 20:41:43.489124][debug][24904][MfPal.cpp:110] WmfPal init ...
[08/18 20:41:43.520661][debug][24904][MfPal.cpp:117] WmfPal created!
[08/18 20:41:43.520721][debug][24904][DeviceManager.cpp:34] Enable USB Device Enumerator ...
[08/18 20:41:43.553521][debug][24904][EnumeratorLibusb.cpp:321] queryDevicesInfo done!
[08/18 20:41:43.553940][debug][24904][MfPal.cpp:216] Create WinEventDeviceWatcher!
[08/18 20:41:43.554220][debug][24904][UsbDeviceEnumerator.cpp:78] No matched usb device found!
[08/18 20:41:43.554276][info][24904][DeviceManager.cpp:15] Current found device(s): (0)
[08/18 20:41:43.554305][debug][24904][DeviceManager.cpp:52] DeviceManager construct done!
[08/18 20:41:43.554347][debug][24904][Context.cpp:81] Context destroying ...
[08/18 20:41:43.554360][debug][24904][DeviceManager.cpp:56] DeviceManager destroy ...
[08/18 20:41:43.554371][debug][24904][DeviceManager.cpp:64] DeviceManager Destructors done
[08/18 20:41:43.558317][debug][24904][MfPal.cpp:128] WmfPal destroyed!
[08/18 20:41:43.558735][info][24904][Context.cpp:84] Context destroyed
[08/18 20:43:03.299275][debug][29780][Context.cpp:30] Context creating, work_dir=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend
[08/18 20:43:03.299338][debug][29780][Context.cpp:49] Config file version=1.1
[08/18 20:43:03.299358][debug][29780][FrameBufferManager.cpp:23] Max global frame buffer size updated! size=2048.000MB
[08/18 20:43:03.299375][info][29780][Context.cpp:68] Context created with config: default config!
[08/18 20:43:03.299399][info][29780][Context.cpp:73] Work directory=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend, SDK version=v1.10.11-20240724-aeaa107e5
[08/18 20:43:03.299416][debug][29780][DeviceManager.cpp:30] DeviceManager init ...
[08/18 20:43:03.299428][info][29780][MfPal.cpp:105] createObPal: create WinPal!
[08/18 20:43:03.299441][debug][29780][MfPal.cpp:110] WmfPal init ...
[08/18 20:43:03.340690][debug][29780][MfPal.cpp:117] WmfPal created!
[08/18 20:43:03.340727][debug][29780][DeviceManager.cpp:34] Enable USB Device Enumerator ...
[08/18 20:43:03.367003][debug][29780][EnumeratorLibusb.cpp:321] queryDevicesInfo done!
[08/18 20:43:03.367322][debug][29780][MfPal.cpp:216] Create WinEventDeviceWatcher!
[08/18 20:43:03.367577][debug][29780][UsbDeviceEnumerator.cpp:78] No matched usb device found!
[08/18 20:43:03.367605][info][29780][DeviceManager.cpp:15] Current found device(s): (0)
[08/18 20:43:03.367629][debug][29780][DeviceManager.cpp:52] DeviceManager construct done!
[08/18 20:43:03.367646][debug][29780][Context.cpp:81] Context destroying ...
[08/18 20:43:03.367657][debug][29780][DeviceManager.cpp:56] DeviceManager destroy ...
[08/18 20:43:03.367667][debug][29780][DeviceManager.cpp:64] DeviceManager Destructors done
[08/18 20:43:03.369012][debug][29780][MfPal.cpp:128] WmfPal destroyed!
[08/18 20:43:03.369386][info][29780][Context.cpp:84] Context destroyed

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1755591402762" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7849" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M512 512a256 256 0 1 0-257.28-256A256 256 0 0 0 512 512z m0 128c-171.52 0-512 82.651429-512 256v128h1024v-128c2.742857-173.348571-337.737143-256-512-256z" fill="#ffffff" p-id="7850"></path></svg>

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -13,7 +13,7 @@ api.interceptors.request.use(
if (window.electronAPI) { if (window.electronAPI) {
config.baseURL = window.electronAPI.getBackendUrl() config.baseURL = window.electronAPI.getBackendUrl()
} else { } else {
config.baseURL = 'http://localhost:5000' config.baseURL = 'http://192.168.1.58:5000'
} }
// 只为需要发送数据的请求设置Content-Type // 只为需要发送数据的请求设置Content-Type
@ -607,7 +607,7 @@ export const getBackendUrl = () => {
if (window.electronAPI) { if (window.electronAPI) {
return window.electronAPI.getBackendUrl() return window.electronAPI.getBackendUrl()
} else { } else {
return 'http://localhost:5000' return 'http://192.168.1.58:5000'
} }
} }

View File

@ -7,8 +7,8 @@
起始页 起始页
</div> </div>
<div class="main-dashboard-top-info"> <div class="main-dashboard-top-info">
<div>测试时间2025-08-03 17:13:18<span></span></div> <!-- <div>测试时间2025-08-03 17:13:18<span></span></div>
<div style="margin-left: 15px;">测试医生<span>李医生</span></div> <div style="margin-left: 15px;">测试医生<span>李医生</span></div> -->
</div> </div>
</div> </div>
<!-- 左侧功能导航 --> <!-- 左侧功能导航 -->
@ -29,7 +29,7 @@
</div> </div>
<div class="search-box"> <div class="search-box">
<el-input v-model="searchKeyword" placeholder="搜索患者姓名" prefix-icon="Search" clearable <el-input v-model="searchKeyword" placeholder="搜索患者姓名" prefix-icon="Search" clearable
@input="handleSearch" /> @input="handleSearch" class="search-input"/>
</div> </div>
</div> </div>
<el-table ref="tableRef" :data="filteredPatients" style="width: 100%" border @cell-click="selectPatient" <el-table ref="tableRef" :data="filteredPatients" style="width: 100%" border @cell-click="selectPatient"
@ -62,7 +62,7 @@
<div class="module-title-text">基础信息</div> <div class="module-title-text">基础信息</div>
</div> </div>
<el-button link @click="editPatient"> <el-button link @click="editPatient">
<el-icon> <el-icon class="edit-icon" style="width: 28px;height: 28px;font-size: 28px;cursor: pointer;color: #CCCCCC;">
<Edit /> <Edit />
</el-icon> </el-icon>
</el-button> </el-button>
@ -139,7 +139,8 @@
<!-- 无选中患者时的提示 --> <!-- 无选中患者时的提示 -->
<div class="no-selection" v-else> <div class="no-selection" v-else>
<el-empty description="请选择一个患者查看详情"> <el-empty description="请选择一个患者查看详情">
<el-button type="primary" @click="createNewPatient">新建患者档案</el-button> <!-- <el-button type="primary" @click="createNewPatient">新建患者档案</el-button> -->
<div class="action-view-buttons" style="width:338px;" @click="createNewPatient">+新患者建档</div>
</el-empty> </el-empty>
</div> </div>
</div> </div>
@ -183,8 +184,8 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="民族" prop="nationality"> <el-form-item label="民族" prop="nationality">
<el-select v-model="patientForm.nationality" placeholder="请选择"> <el-select v-model="patientForm.nationality" placeholder="请选择">
<el-option v-for="item in nationalityOptions" :key="item.value" :label="item.label" <el-option v-for="item in nationalityOptions" :key="item" :label="item"
:value="item.value" /> :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -228,8 +229,8 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="职业" prop="occupation"> <el-form-item label="职业" prop="occupation">
<el-select v-model="patientForm.occupation" placeholder="请选择"> <el-select v-model="patientForm.occupation" placeholder="请选择">
<el-option v-for="item in occupationOptions" :key="item.value" :label="item.label" <el-option v-for="item in occupationOptions" :key="item" :label="item"
:value="item.value" /> :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -294,15 +295,23 @@ const patientForm = ref({
workplace: '', workplace: '',
email: '' email: ''
}) })
const occupationOptions = ref([ const occupationOptions = ref(["学生", "教师", "医生", "护士", "工程师", "程序员", "设计师",
{ label: '学生', value: '学生' } "会计师", "律师", "警察", "消防员", "军人", "公务员", "销售", "市场营销",
]) "人力资源", "行政", "财务", "咨询师", "建筑师", "科研人员", "记者", "编辑",
"作家", "艺术家", "音乐家", "演员", "导演", "摄影师", "厨师", "服务员",
"司机", "快递员", "外卖员", "农民", "工人", "电工", "焊工", "机械师",
"飞行员", "空乘", "船员", "导游", "翻译", "心理咨询师", "社会工作者",
"运动员", "教练", "经纪人", "投资人", "企业家", "自由职业者"])
const residenceOptions = ref([ const residenceOptions = ref([
{ label: '北京', value: '北京' } { label: '北京', value: '北京' }
]) ])
const nationalityOptions = ref([ const nationalityOptions = ref(["汉族", "满族", "蒙古族", "回族", "藏族", "维吾尔族", "苗族", "彝族", "壮族",
{ label: '汉族', value: '汉族' } "布依族", "朝鲜族", "侗族", "瑶族", "白族", "土家族", "哈尼族", "哈萨克族", "傣族",
]) "黎族", "傈僳族", "佤族", "畲族", "高山族", "拉祜族", "水族", "东乡族", "纳西族",
"景颇族", "柯尔克孜族", "土族", "达斡尔族", "仫佬族", "羌族", "布朗族", "撒拉族",
"毛南族", "仡佬族", "锡伯族", "阿昌族", "普米族", "塔吉克族", "怒族", "乌孜别克族",
"俄罗斯族", "鄂温克族", "德昂族", "保安族", "裕固族", "京族", "塔塔尔族", "独龙族",
"鄂伦春族", "赫哲族", "门巴族", "珞巴族", "基诺族"])
// //
const formRules = { const formRules = {
name: [ name: [
@ -1073,10 +1082,12 @@ function delClick(id) {
:deep(.el-input__inner) { :deep(.el-input__inner) {
color: #ffffff; color: #ffffff;
font-size: 16px;
} }
:deep(.el-select__placeholder) { :deep(.el-select__placeholder) {
color: #ffffff; color: #ffffff;
font-size: 16px;
} }
:deep(.el-input.is-disabled .el-input__wrapper) { :deep(.el-input.is-disabled .el-input__wrapper) {
@ -1191,4 +1202,7 @@ function delClick(id) {
font-size: 20px; font-size: 20px;
font-family: 'Arial Normal', 'Arial', sans-serif; font-family: 'Arial Normal', 'Arial', sans-serif;
} }
</style> .search-input{
font-size: 16px;
}
</style>

View File

@ -11,33 +11,36 @@
<!-- <el-icon class="back-icon" @click="handleBack"><ArrowLeft /></el-icon> --> <!-- <el-icon class="back-icon" @click="handleBack"><ArrowLeft /></el-icon> -->
<span class="page-title">实时检测</span> <span class="page-title">实时检测</span>
</div> </div>
<img src="@/assets/sz.png" alt="" title="编辑相机参数" style="margin-left: 20px;cursor: pointer; width: 24px;height: 24px;"
@click="cameraUpdate">
<!-- 录制时间 --> <!-- 录制时间 -->
<div v-if="isRecording" class="icon-container"> <div v-if="isRecording" class="icon-container">
<img src="@/assets/record.png" class="blink-icon" :class="{ blinking: isRunning }" alt=""> <img src="@/assets/record.png" class="blink-icon" :class="{ blinking: isRunning }" alt="">
<div style="font-size: 18px;">{{ formattedTime }}</div> <div style="font-size: 18px;">{{ formattedTime }}</div>
</div> </div>
<el-button v-if="!isRecording" @click="handleStartStop" :disabled="!isConnected" type="primary" <el-button v-if="!isRecording" @click="handleStartStop" :disabled="!isConnected" type="primary"
class="start-btn" style="background-image: linear-gradient(to right, rgb(236, 50, 166), rgb(160, 5, 216)); class="start-title-btn" style="background-image: linear-gradient(to right, rgb(236, 50, 166), rgb(160, 5, 216));
--el-button-border-color: #409EFF; --el-button-border-color: #409EFF;
--el-button-border-color: transparent;width: 120px;height: 30px;font-size: 20px;"> --el-button-border-color: transparent;width: 120px;height: 30px;font-size: 20px;">
{{ isConnected ? '开始' : '连接中...' }} {{ isConnected ? '开始' : '连接中...' }}
</el-button> </el-button>
<!-- handleStartStop --> <!-- handleStartStop -->
<el-button v-if="isRecording" @click="handleStartStop" type="primary" class="start-btn" style="background-image: linear-gradient(to right, rgb(236, 50, 166), rgb(160, 5, 216)); <el-button v-if="isRecording" @click="handleStartStop" type="primary" class="start-title-btn" style="background-image: linear-gradient(to right, rgb(236, 50, 166), rgb(160, 5, 216));
--el-button-border-color: #409EFF; --el-button-border-color: #409EFF;
--el-button-border-color: transparent;width: 120px;height: 30px;font-size: 20px;"> --el-button-border-color: transparent;width: 120px;height: 30px;font-size: 20px;">
结束 结束
</el-button> </el-button>
<el-button v-if="isStart && isConnected" @click="saveDetectionData" type="primary" class="start-btn" style="background-image: linear-gradient(to right, #FBB106, #A817C6); <el-button v-if="isStart && isConnected" @click="saveDetectionData" type="primary" class="start-title-btn" style="background-image: linear-gradient(to right, #FBB106, #A817C6);
--el-button-border-color: #409EFF; --el-button-border-color: #409EFF;
--el-button-border-color: transparent;width: 120px;height: 30px;font-size: 20px;"> --el-button-border-color: transparent;width: 120px;height: 30px;font-size: 20px;">
保存数据 保存数据
</el-button> </el-button>
</div> </div>
<div class="top-bar-right"> <div class="top-bar-right">
<span class="info-item">测试时间2025/5/28 下午14:38</span>
<span class="info-item">测试医生李四</span>
<el-icon class="top-icon"> <el-icon class="top-icon">
<Clock /> <Clock />
</el-icon> </el-icon>
@ -64,7 +67,7 @@
<div style="display: flex;justify-content: center;height: 100%;padding-top: 0px;"> <div style="display: flex;justify-content: center;height: 100%;padding-top: 0px;">
<!-- 使用深度相机视频流替换静态图片 --> <!-- 使用深度相机视频流替换静态图片 -->
<img :src="(femtoboltStatus === '已连接' && depthCameraImgSrc) ? depthCameraImgSrc : noImageSvg" alt="深度相机视频流" <img :src="(femtoboltStatus === '已连接' && depthCameraImgSrc) ? depthCameraImgSrc : noImageSvg" alt="深度相机视频流"
style="width: 100%;height: calc(100% - 10px);object-fit:contain;background:#323232;"> style="width: 100%;height: calc(100% - 40px);object-fit:contain;background:#323232;">
</div> </div>
</div> </div>
<div class="body-posture" style="width: 45%;display: flex;margin-right: 1px; <div class="body-posture" style="width: 45%;display: flex;margin-right: 1px;
@ -415,8 +418,8 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="民族" prop="nationality"> <el-form-item label="民族" prop="nationality">
<el-select v-model="patientForm.nationality" placeholder="请选择"> <el-select v-model="patientForm.nationality" placeholder="请选择">
<el-option v-for="item in nationalityOptions" :key="item.value" :label="item.label" <el-option v-for="item in nationalityOptions" :key="item" :label="item"
:value="item.value" /> :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -460,8 +463,8 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="职业" prop="occupation"> <el-form-item label="职业" prop="occupation">
<el-select v-model="patientForm.occupation" placeholder="请选择"> <el-select v-model="patientForm.occupation" placeholder="请选择">
<el-option v-for="item in occupationOptions" :key="item.value" :label="item.label" <el-option v-for="item in occupationOptions" :key="item" :label="item"
:value="item.value" /> :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -483,6 +486,63 @@
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog class="tsDialog" v-model="cameraDialogVisible" center title="诊断信息" width="600"
:before-close="cameraHandleClose">
<div class="form-box" style="margin-top: 20px;">
<el-form :model="cameraForm" label-width="100px">
<div class="cameraFormTitle">足部相机</div>
<el-form-item label="序号">
<el-radio-group v-model="cameraForm.camera.device_index">
<el-radio :value="0">0</el-radio>
<el-radio :value="1">1</el-radio>
<el-radio :value="2">2</el-radio>
<el-radio :value="3">3</el-radio>
<el-radio :value="4">4</el-radio>
<el-radio :value="5">5</el-radio>
</el-radio-group>
</el-form-item>
<div class="cameraFormTitle">深度相机</div>
<el-form-item label="距离范围">
<div >
<el-input v-model="cameraForm.femtobolt.depth_range_min" placeholder="请输入最小值" style="width: 216px;" />
<span> </span>
<el-input v-model="cameraForm.femtobolt.depth_range_max" placeholder="请输入最大值" style="width: 218px;"/>
</div>
</el-form-item>
<div class="cameraFormTitle">头部IMU</div>
<el-form-item label="IMU串口号">
<el-select v-model="cameraForm.imu.port" placeholder="请选择">
<el-option label="COM1" value="COM1" />
<el-option label="COM2" value="COM2" />
<el-option label="COM3" value="COM3" />
<el-option label="COM4" value="COM4" />
<el-option label="COM5" value="COM5" />
<el-option label="COM6" value="COM6" />
<el-option label="COM7" value="COM7" />
<el-option label="COM8" value="COM8" />
<el-option label="COM9" value="COM9" />
<el-option label="COM10" value="COM10" />
</el-select>
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button style="background: #323232;border:1px solid #787878;color: #ffffff;"
@click="cameraDialogVisible = false">取消</el-button>
<el-button type="primary" style="background:#0099ff;" @click="cameraSubmit('cameraForm')">
保存
</el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
@ -507,6 +567,7 @@ const depthCameraImgSrc = ref('') // 深度相机视频流
const screenshotLoading = ref(false) const screenshotLoading = ref(false)
const dataCollectionLoading = ref(false) const dataCollectionLoading = ref(false)
const isRecording = ref(false) const isRecording = ref(false)
const cameraDialogVisible =ref(false) //
// //
let mediaRecorder = null let mediaRecorder = null
@ -545,6 +606,9 @@ const resDialogVisible = ref(false)
const reshandleClose = () => { const reshandleClose = () => {
resDialogVisible.value = false resDialogVisible.value = false
} }
const cameraHandleClose = () => {
cameraDialogVisible.value = false
}
const dialogVisible = ref(false) const dialogVisible = ref(false)
const handleClose = () => { const handleClose = () => {
dialogVisible.value = false dialogVisible.value = false
@ -568,17 +632,38 @@ const patientForm = ref({
workplace: '', workplace: '',
email: '' email: ''
}) })
const occupationOptions = ref([ const occupationOptions = ref(["学生", "教师", "医生", "护士", "工程师", "程序员", "设计师",
{ label: '学生', value: '学生' } "会计师", "律师", "警察", "消防员", "军人", "公务员", "销售", "市场营销",
]) "人力资源", "行政", "财务", "咨询师", "建筑师", "科研人员", "记者", "编辑",
const nationalityOptions = ref([ "作家", "艺术家", "音乐家", "演员", "导演", "摄影师", "厨师", "服务员",
{ label: '汉族', value: '汉族' } "司机", "快递员", "外卖员", "农民", "工人", "电工", "焊工", "机械师",
]) "飞行员", "空乘", "船员", "导游", "翻译", "心理咨询师", "社会工作者",
"运动员", "教练", "经纪人", "投资人", "企业家", "自由职业者"])
const nationalityOptions = ref(["汉族", "满族", "蒙古族", "回族", "藏族", "维吾尔族", "苗族", "彝族", "壮族",
"布依族", "朝鲜族", "侗族", "瑶族", "白族", "土家族", "哈尼族", "哈萨克族", "傣族",
"黎族", "傈僳族", "佤族", "畲族", "高山族", "拉祜族", "水族", "东乡族", "纳西族",
"景颇族", "柯尔克孜族", "土族", "达斡尔族", "仫佬族", "羌族", "布朗族", "撒拉族",
"毛南族", "仡佬族", "锡伯族", "阿昌族", "普米族", "塔吉克族", "怒族", "乌孜别克族",
"俄罗斯族", "鄂温克族", "德昂族", "保安族", "裕固族", "京族", "塔塔尔族", "独龙族",
"鄂伦春族", "赫哲族", "门巴族", "珞巴族", "基诺族"])
const diagnosticForm = ref({ const diagnosticForm = ref({
diagnosis_info: '', diagnosis_info: '',
treatment_info: '', treatment_info: '',
suggestion_info: '' suggestion_info: ''
}) })
const cameraForm = ref({ //
camera:{
device_index: '', //
},
femtobolt:{
depth_mode: '', //
depth_range_min: '', //
depth_range_max: '', //
},
imu:{
port: '', // IMU
}
})
const calculatedAge = ref(null) const calculatedAge = ref(null)
// //
@ -782,6 +867,24 @@ const savePatient = async () => {
function routeTo(path) { function routeTo(path) {
router.push(`/`) router.push(`/`)
} }
function cameraUpdate() { //
cameraForm.value = { //
camera:{
device_index: '', //
},
femtobolt:{
depth_mode: '', //
depth_range_min: '', //
depth_range_max: '', //
},
imu:{
port: '', // IMU
}
}
//
getDevicesInit()
}
const calculateAge = (birthDate) => { const calculateAge = (birthDate) => {
if (!birthDate) return '-' if (!birthDate) return '-'
const today = new Date() const today = new Date()
@ -2152,6 +2255,59 @@ const calibrationClick = async () => {
} }
} }
const cameraSubmit = async () => {
// let data = {
// "imu": {"device_type": "real", "port": "COM7", "baudrate": 9600},
// "pressure": {"device_type": "real", "port": "COM8", "baudrate": 115200},
// "camera": {"device_index": 0, "width": 1280, "height": 720, "fps": 30},
// "femtobolt": {"color_resolution": "1080P", "depth_mode": "NFOV_UNBINNED", "fps": 15}
// }
//
const response = await fetch(`${BACKEND_URL}/api/config/devices/all`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(cameraForm.value)
})
if (response.ok) {
const result = await response.json()
if (result.success) {
ElMessage.success(result.message)
cameraDialogVisible.value = false
} else {
ElMessage.error(result.message)
}
}
}
//
const getDevicesInit = async () => {
try {
// API
const response = await fetch(`${BACKEND_URL}/api/config/devices`)
if (response.ok) {
const result = await response.json()
if (result.success) {
console.log('相机参数加载成功:', result.data)
cameraForm.value = result.data
cameraDialogVisible.value = true
// console.log(':', patientInfo.value)
} else {
throw new Error(result.message)
}
} else {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
} catch (error) {
console.error('加载相机参数失败:', error)
ElMessage.warning('加载相机参数失败,请检查网络连接')
}
}
onMounted(() => { onMounted(() => {
// //
loadPatientInfo() loadPatientInfo()
@ -2294,6 +2450,12 @@ onUnmounted(() => {
margin-left: 40px; margin-left: 40px;
} }
.start-title-btn {
font-size: 16px;
padding: 8px 24px;
margin-left: 20px;
}
.top-bar-right { .top-bar-right {
display: flex; display: flex;
align-items: center; align-items: center;
@ -2722,11 +2884,11 @@ onUnmounted(() => {
} }
:deep(.el-form-item__label) { :deep(.el-form-item__label) {
color: #ffffff !important; color: #ffffff ;
font-size: 14px !important; font-size: 16px ;
font-family: '苹方 粗体', '苹方 中等', '苹方', sans-serif !important; font-family: '苹方 粗体', '苹方 中等', '苹方', sans-serif ;
font-weight: 700 !important; /* font-weight: 700 ; */
font-style: normal !important; font-style: normal ;
} }
@ -2870,6 +3032,7 @@ onUnmounted(() => {
} }
:deep(.tsDialog.el-dialog .el-select__wrapper) { :deep(.tsDialog.el-dialog .el-select__wrapper) {
font-size: 16px;
background-color: #242424; background-color: #242424;
border: none; border: none;
box-shadow: none; box-shadow: none;
@ -2901,6 +3064,18 @@ onUnmounted(() => {
:deep(.el-dialog__footer) { :deep(.el-dialog__footer) {
padding: 0px 20px; padding: 0px 20px;
} }
:deep(.el-radio__label){
font-size: 16px;
color: #fff;
}
.cameraFormTitle{
font-size: 18px;
color: #fff;
font-weight: 700;
margin: 30px 0px;
}
</style> </style>
<style> <style>
.dashboard-container.dashboard-container-home .el-table--border .el-table__inner-wrapper:after, .dashboard-container.dashboard-container-home .el-table--border .el-table__inner-wrapper:after,
@ -2958,4 +3133,5 @@ onUnmounted(() => {
border-radius: 20px; border-radius: 20px;
width: 220px; width: 220px;
} }
</style> </style>

View File

@ -9,12 +9,15 @@
<div class="system-title">平衡体态检测系统</div> <div class="system-title">平衡体态检测系统</div>
</div> </div>
<div class="header-right"> <div class="header-right">
<div style="color:#fff;margin-right: 20px;">登录时间{{ time }} </div>
<div class="user-info"> <div class="user-info">
<el-avatar :size="40" :src="userInfo.avatar"> <img src="@/assets/svg/avatar.svg" alt="Avatar" style="width: 20px;height: 20px;">
<!-- <el-avatar :size="40" :src="userInfo.avatar">
<el-icon> <el-icon>
<User /> <User />
</el-icon> </el-icon>
</el-avatar> </el-avatar> -->
<span class="username">{{ userInfo.username }}</span> <span class="username">{{ userInfo.username }}</span>
<el-dropdown @command="handleUserCommand"> <el-dropdown @command="handleUserCommand">
<el-button link class="user-dropdown"> <el-button link class="user-dropdown">
@ -44,6 +47,7 @@
const router = useRouter() const router = useRouter()
const authStore = useAuthStore() const authStore = useAuthStore()
const time = ref("");
const userInfo = reactive({ const userInfo = reactive({
username: '医生', username: '医生',
avatar: '' avatar: ''
@ -63,7 +67,21 @@
break break
} }
} }
function dateFormat(row) {
const daterc = row;
if (daterc != null) {
var date = new Date(daterc);
var year = date.getFullYear();
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;
var day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
//
return year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds;
}
}
const handleLogout = async () => { const handleLogout = async () => {
try { try {
await ElMessageBox.confirm('确定要退出登录吗?', '提示', { await ElMessageBox.confirm('确定要退出登录吗?', '提示', {
@ -87,11 +105,11 @@
// //
if (authStore.currentUser) { if (authStore.currentUser) {
Object.assign(userInfo, { Object.assign(userInfo, {
username: authStore.currentUser.username, username: authStore.currentUser.name,
avatar: authStore.currentUser.avatar || '' avatar: authStore.currentUser.avatar || ''
}) })
} }
time.value = dateFormat(new Date())
}) })
</script> </script>

View File

@ -217,7 +217,8 @@ import { useAuthStore } from '../stores'
import { User, Lock, View, Hide, Phone } from '@element-plus/icons-vue' import { User, Lock, View, Hide, Phone } from '@element-plus/icons-vue'
import bg from '@/assets/bg.png' import bg from '@/assets/bg.png'
import { getBackendUrl } from '../services/api.js'
const BACKEND_URL = getBackendUrl()
const router = useRouter() const router = useRouter()
const authStore = useAuthStore() const authStore = useAuthStore()
@ -487,7 +488,7 @@ const handleForgotPasswordSubmit = async () => {
try { try {
// API // API
const response = await fetch('http://127.0.0.1:5000/api/auth/forgot-password', { const response = await fetch( `${BACKEND_URL}/api/auth/forgot-password`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'

View File

@ -9,8 +9,8 @@
</div> </div>
</div> </div>
<div class="nav-container-info"> <div class="nav-container-info">
<div>测试时间2025-08-03 17:13:18<span></span></div> <!-- <div>测试时间2025-08-03 17:13:18<span></span></div>
<div style="margin-left: 15px;">测试医生<span>李医生</span></div> <div style="margin-left: 15px;">测试医生<span>李医生</span></div> -->
</div> </div>
</div> </div>
<!-- 表单内容 --> <!-- 表单内容 -->
@ -59,8 +59,8 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="民族" prop="nationality"> <el-form-item label="民族" prop="nationality">
<el-select v-model="patientForm.nationality" placeholder="请选择"> <el-select v-model="patientForm.nationality" placeholder="请选择">
<el-option v-for="item in nationalityOptions" :key="item.value" :label="item.label" <el-option v-for="item in nationalityOptions" :key="item" :label="item"
:value="item.value" /> :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -104,8 +104,8 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="职业" prop="occupation"> <el-form-item label="职业" prop="occupation">
<el-select v-model="patientForm.occupation" placeholder="请选择"> <el-select v-model="patientForm.occupation" placeholder="请选择">
<el-option v-for="item in occupationOptions" :key="item.value" :label="item.label" <el-option v-for="item in occupationOptions" :key="item" :label="item"
:value="item.value" /> :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -163,15 +163,23 @@ const patientForm = reactive({
workplace: '', workplace: '',
email: '' email: ''
}) })
const occupationOptions = ref([ const occupationOptions = ref(["学生", "教师", "医生", "护士", "工程师", "程序员", "设计师",
{ label: '学生', value: '学生' } "会计师", "律师", "警察", "消防员", "军人", "公务员", "销售", "市场营销",
]) "人力资源", "行政", "财务", "咨询师", "建筑师", "科研人员", "记者", "编辑",
"作家", "艺术家", "音乐家", "演员", "导演", "摄影师", "厨师", "服务员",
"司机", "快递员", "外卖员", "农民", "工人", "电工", "焊工", "机械师",
"飞行员", "空乘", "船员", "导游", "翻译", "心理咨询师", "社会工作者",
"运动员", "教练", "经纪人", "投资人", "企业家", "自由职业者"])
const residenceOptions = ref([ const residenceOptions = ref([
{ label: '北京', value: '北京' } { label: '北京', value: '北京' }
]) ])
const nationalityOptions = ref([ const nationalityOptions = ref(["汉族", "满族", "蒙古族", "回族", "藏族", "维吾尔族", "苗族", "彝族", "壮族",
{ label: '汉族', value: '汉族' } "布依族", "朝鲜族", "侗族", "瑶族", "白族", "土家族", "哈尼族", "哈萨克族", "傣族",
]) "黎族", "傈僳族", "佤族", "畲族", "高山族", "拉祜族", "水族", "东乡族", "纳西族",
"景颇族", "柯尔克孜族", "土族", "达斡尔族", "仫佬族", "羌族", "布朗族", "撒拉族",
"毛南族", "仡佬族", "锡伯族", "阿昌族", "普米族", "塔吉克族", "怒族", "乌孜别克族",
"俄罗斯族", "鄂温克族", "德昂族", "保安族", "裕固族", "京族", "塔塔尔族", "独龙族",
"鄂伦春族", "赫哲族", "门巴族", "珞巴族", "基诺族"])
// //
const formRules = { const formRules = {
name: [ name: [
@ -504,10 +512,12 @@ const handleSaveAndDetect = async () => {
:deep(.el-input__inner) { :deep(.el-input__inner) {
color: #ffffff; color: #ffffff;
font-size: 16px;
} }
:deep(.el-select__placeholder) { :deep(.el-select__placeholder) {
color: #ffffff; color: #ffffff;
font-size: 16px;
} }
:deep(.el-input.is-disabled .el-input__wrapper) { :deep(.el-input.is-disabled .el-input__wrapper) {

View File

@ -10,8 +10,8 @@
</div> </div>
</div> </div>
<div class="nav-container-info"> <div class="nav-container-info">
<div>测试时间2025-08-03 17:13:18<span></span></div> <!-- <div>测试时间2025-08-03 17:13:18<span></span></div>
<div style="margin-left: 15px;">测试医生<span>李医生</span></div> <div style="margin-left: 15px;">测试医生<span>李医生</span></div> -->
</div> </div>
</div> </div>
<div class="main-content"> <div class="main-content">

View File

@ -1473,6 +1473,7 @@ onUnmounted(() => {
:deep(.el-input__inner) { :deep(.el-input__inner) {
color: #fff; color: #fff;
font-size: 16px;
} }
:deep(.el-textarea__inner) { :deep(.el-textarea__inner) {