309 lines
9.9 KiB
Python
309 lines
9.9 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
# -*- coding: utf-8 -*-
|
||
|
|
|
||
|
|
"""
|
||
|
|
登录对话框
|
||
|
|
用户登录验证界面
|
||
|
|
"""
|
||
|
|
|
||
|
|
from PyQt5.QtWidgets import (
|
||
|
|
QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
||
|
|
QPushButton, QComboBox, QLineEdit, QMessageBox, QWidget, QPushButton as QBtn
|
||
|
|
)
|
||
|
|
from PyQt5.QtCore import Qt, QSize
|
||
|
|
from PyQt5.QtGui import QFont, QIcon, QPixmap, QPainter, QPen, QBrush, QColor, QPolygon, QPolygonF
|
||
|
|
from PyQt5.QtCore import QPoint, QPointF
|
||
|
|
import requests
|
||
|
|
import json
|
||
|
|
|
||
|
|
# 尝试导入配置
|
||
|
|
try:
|
||
|
|
from config import USE_HTTP_API, HTTP_API_BASE_URL
|
||
|
|
except ImportError:
|
||
|
|
USE_HTTP_API = True
|
||
|
|
HTTP_API_BASE_URL = "http://127.0.0.1:5050"
|
||
|
|
|
||
|
|
|
||
|
|
class LoginDialog(QDialog):
|
||
|
|
"""登录对话框"""
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.user_info = None # 存储登录成功后的用户信息
|
||
|
|
self.setWindowTitle("登录")
|
||
|
|
self.setModal(True)
|
||
|
|
self.setFixedSize(500, 300)
|
||
|
|
|
||
|
|
# HTTP API 配置
|
||
|
|
self.use_http_api = USE_HTTP_API
|
||
|
|
self.api_base_url = HTTP_API_BASE_URL
|
||
|
|
|
||
|
|
self._build_ui()
|
||
|
|
|
||
|
|
def _create_arrow_icon(self):
|
||
|
|
"""创建下拉箭头图标"""
|
||
|
|
pixmap = QPixmap(20, 20)
|
||
|
|
pixmap.fill(Qt.transparent)
|
||
|
|
|
||
|
|
painter = QPainter(pixmap)
|
||
|
|
painter.setRenderHint(QPainter.Antialiasing)
|
||
|
|
|
||
|
|
# 绘制三角形箭头
|
||
|
|
painter.setPen(Qt.NoPen)
|
||
|
|
painter.setBrush(QBrush(QColor(102, 102, 102))) # #666
|
||
|
|
|
||
|
|
triangle = QPolygonF([
|
||
|
|
QPointF(3.75, 5.5),
|
||
|
|
QPointF(16.25, 5.5),
|
||
|
|
QPointF(10, 14.5)
|
||
|
|
])
|
||
|
|
painter.drawPolygon(triangle)
|
||
|
|
painter.end()
|
||
|
|
|
||
|
|
return pixmap
|
||
|
|
|
||
|
|
def _build_ui(self):
|
||
|
|
"""构建界面"""
|
||
|
|
layout = QVBoxLayout(self)
|
||
|
|
layout.setContentsMargins(60, 40, 60, 40)
|
||
|
|
layout.setSpacing(20)
|
||
|
|
|
||
|
|
# 创建下拉箭头图标
|
||
|
|
arrow_pixmap = self._create_arrow_icon()
|
||
|
|
|
||
|
|
# 标题
|
||
|
|
title_label = QLabel("跌落试验管理系统")
|
||
|
|
title_label.setAlignment(Qt.AlignCenter)
|
||
|
|
title_font = QFont()
|
||
|
|
title_font.setPointSize(18)
|
||
|
|
title_font.setBold(True)
|
||
|
|
title_label.setFont(title_font)
|
||
|
|
title_label.setStyleSheet("color: #333; margin-bottom: 20px;")
|
||
|
|
layout.addWidget(title_label)
|
||
|
|
|
||
|
|
# 用户名行
|
||
|
|
username_layout = QHBoxLayout()
|
||
|
|
username_label = QLabel("用户名")
|
||
|
|
username_label.setFixedWidth(80)
|
||
|
|
username_label.setStyleSheet("font-size: 14px; color: #666;")
|
||
|
|
username_layout.addWidget(username_label)
|
||
|
|
|
||
|
|
# 用户名下拉框(可编辑)
|
||
|
|
self.username_combo = QComboBox()
|
||
|
|
self.username_combo.setEditable(True)
|
||
|
|
self.username_combo.addItems(['test', 'admin', 'topAdmin123'])
|
||
|
|
self.username_combo.setCurrentText('') # 默认为空
|
||
|
|
|
||
|
|
# 保存箭头图标以便在样式表中引用
|
||
|
|
arrow_path = "e:/WORK/DTM-PY-ALL/UI/temp_arrow.png"
|
||
|
|
arrow_pixmap.save(arrow_path, "PNG")
|
||
|
|
|
||
|
|
# 设置样式,使用图片作为箭头
|
||
|
|
self.username_combo.setStyleSheet(f"""
|
||
|
|
QComboBox {{
|
||
|
|
padding: 8px;
|
||
|
|
padding-right: 30px;
|
||
|
|
border: 1px solid #ddd;
|
||
|
|
border-radius: 4px;
|
||
|
|
font-size: 18px;
|
||
|
|
background: white;
|
||
|
|
min-height: 24px;
|
||
|
|
}}
|
||
|
|
QComboBox:hover {{
|
||
|
|
border: 1px solid #3A84FF;
|
||
|
|
}}
|
||
|
|
QComboBox:editable {{
|
||
|
|
background: white;
|
||
|
|
}}
|
||
|
|
QComboBox::drop-down {{
|
||
|
|
subcontrol-origin: padding;
|
||
|
|
subcontrol-position: center right;
|
||
|
|
width: 30px;
|
||
|
|
border-left: 1px solid #ddd;
|
||
|
|
background: #fafafa;
|
||
|
|
}}
|
||
|
|
QComboBox::drop-down:hover {{
|
||
|
|
background: #f0f0f0;
|
||
|
|
}}
|
||
|
|
QComboBox::down-arrow {{
|
||
|
|
image: url({arrow_path.replace(chr(92), '/')});
|
||
|
|
width: 16px;
|
||
|
|
height: 16px;
|
||
|
|
}}
|
||
|
|
""")
|
||
|
|
self.username_combo.lineEdit().setPlaceholderText("请输入用户名")
|
||
|
|
username_layout.addWidget(self.username_combo)
|
||
|
|
layout.addLayout(username_layout)
|
||
|
|
|
||
|
|
# 密码行
|
||
|
|
password_layout = QHBoxLayout()
|
||
|
|
password_label = QLabel("密码")
|
||
|
|
password_label.setFixedWidth(80)
|
||
|
|
password_label.setStyleSheet("font-size: 14px; color: #666;")
|
||
|
|
password_layout.addWidget(password_label)
|
||
|
|
|
||
|
|
self.password_input = QLineEdit()
|
||
|
|
self.password_input.setEchoMode(QLineEdit.Password)
|
||
|
|
self.password_input.setPlaceholderText("*")
|
||
|
|
self.password_input.setStyleSheet("""
|
||
|
|
QLineEdit {
|
||
|
|
padding: 8px;
|
||
|
|
border: 1px solid #ddd;
|
||
|
|
border-radius: 4px;
|
||
|
|
font-size: 14px;
|
||
|
|
background: white;
|
||
|
|
}
|
||
|
|
QLineEdit:hover {
|
||
|
|
border: 1px solid #3A84FF;
|
||
|
|
}
|
||
|
|
QLineEdit:focus {
|
||
|
|
border: 1px solid #3A84FF;
|
||
|
|
}
|
||
|
|
""")
|
||
|
|
# 按回车键提交
|
||
|
|
self.password_input.returnPressed.connect(self._on_login)
|
||
|
|
password_layout.addWidget(self.password_input)
|
||
|
|
layout.addLayout(password_layout)
|
||
|
|
|
||
|
|
layout.addStretch()
|
||
|
|
|
||
|
|
# 按钮行
|
||
|
|
btn_layout = QHBoxLayout()
|
||
|
|
btn_layout.addStretch()
|
||
|
|
|
||
|
|
# 登录按钮
|
||
|
|
self.login_btn = QPushButton("登录")
|
||
|
|
self.login_btn.setFixedSize(100, 40)
|
||
|
|
self.login_btn.setStyleSheet("""
|
||
|
|
QPushButton {
|
||
|
|
background-color: #3A84FF;
|
||
|
|
color: white;
|
||
|
|
border: none;
|
||
|
|
border-radius: 4px;
|
||
|
|
font-size: 14px;
|
||
|
|
font-weight: bold;
|
||
|
|
}
|
||
|
|
QPushButton:hover {
|
||
|
|
background-color: #2B6FE6;
|
||
|
|
}
|
||
|
|
QPushButton:pressed {
|
||
|
|
background-color: #1F5BD1;
|
||
|
|
}
|
||
|
|
""")
|
||
|
|
self.login_btn.clicked.connect(self._on_login)
|
||
|
|
btn_layout.addWidget(self.login_btn)
|
||
|
|
|
||
|
|
# 取消按钮
|
||
|
|
self.cancel_btn = QPushButton("取消")
|
||
|
|
self.cancel_btn.setFixedSize(100, 40)
|
||
|
|
self.cancel_btn.setStyleSheet("""
|
||
|
|
QPushButton {
|
||
|
|
background-color: #f5f5f5;
|
||
|
|
color: #666;
|
||
|
|
border: 1px solid #ddd;
|
||
|
|
border-radius: 4px;
|
||
|
|
font-size: 14px;
|
||
|
|
}
|
||
|
|
QPushButton:hover {
|
||
|
|
background-color: #e8e8e8;
|
||
|
|
}
|
||
|
|
QPushButton:pressed {
|
||
|
|
background-color: #d8d8d8;
|
||
|
|
}
|
||
|
|
""")
|
||
|
|
self.cancel_btn.clicked.connect(self.reject)
|
||
|
|
btn_layout.addWidget(self.cancel_btn)
|
||
|
|
|
||
|
|
layout.addLayout(btn_layout)
|
||
|
|
|
||
|
|
def _on_login(self):
|
||
|
|
"""登录按钮点击事件"""
|
||
|
|
username = self.username_combo.currentText().strip()
|
||
|
|
password = self.password_input.text().strip()
|
||
|
|
|
||
|
|
if not username:
|
||
|
|
QMessageBox.warning(self, "提示", "请输入用户名")
|
||
|
|
return
|
||
|
|
|
||
|
|
if not password:
|
||
|
|
QMessageBox.warning(self, "提示", "请输入密码")
|
||
|
|
return
|
||
|
|
|
||
|
|
# 调用后端 API 验证登录
|
||
|
|
try:
|
||
|
|
result = self._call_login_api(username, password)
|
||
|
|
|
||
|
|
if result.get("status") == "success":
|
||
|
|
self.user_info = {
|
||
|
|
'username': result.get('username'),
|
||
|
|
'userid': result.get('userid'),
|
||
|
|
'role': result.get('role'),
|
||
|
|
'auth': result.get('auth')
|
||
|
|
}
|
||
|
|
print(f"[登录成功] 用户: {self.user_info['username']}, 角色: {self.user_info['role']}")
|
||
|
|
self.accept() # 关闭对话框并返回成功
|
||
|
|
else:
|
||
|
|
error_msg = result.get("error", "登录失败")
|
||
|
|
QMessageBox.warning(self, "登录失败", f"用户名或密码错误\n{error_msg}")
|
||
|
|
# 不关闭对话框,允许重新输入
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
import traceback
|
||
|
|
traceback.print_exc()
|
||
|
|
QMessageBox.critical(
|
||
|
|
self,
|
||
|
|
"错误",
|
||
|
|
f"登录请求失败:{str(e)}\n\n请确保后端服务已启动"
|
||
|
|
)
|
||
|
|
|
||
|
|
def _call_login_api(self, username, password):
|
||
|
|
"""
|
||
|
|
调用后端登录 API
|
||
|
|
|
||
|
|
Args:
|
||
|
|
username (str): 用户名
|
||
|
|
password (str): 密码
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
dict: API 响应结果
|
||
|
|
"""
|
||
|
|
url = f"{self.api_base_url}/login"
|
||
|
|
params = {
|
||
|
|
"username": username,
|
||
|
|
"password": password
|
||
|
|
}
|
||
|
|
|
||
|
|
print(f"\n[登录请求] URL: {url}")
|
||
|
|
print(f"[登录请求] 参数: {json.dumps(params, ensure_ascii=False)}")
|
||
|
|
|
||
|
|
try:
|
||
|
|
response = requests.post(
|
||
|
|
url,
|
||
|
|
json=params,
|
||
|
|
timeout=10
|
||
|
|
)
|
||
|
|
response.raise_for_status()
|
||
|
|
result = response.json()
|
||
|
|
|
||
|
|
print(f"[登录响应] {json.dumps(result, ensure_ascii=False)}")
|
||
|
|
return result
|
||
|
|
|
||
|
|
except requests.exceptions.RequestException as e:
|
||
|
|
print(f"[登录错误] HTTP 请求失败: {e}")
|
||
|
|
return {
|
||
|
|
"status": "error",
|
||
|
|
"error": f"网络请求失败: {str(e)}"
|
||
|
|
}
|
||
|
|
except Exception as e:
|
||
|
|
print(f"[登录错误] 未知错误: {e}")
|
||
|
|
import traceback
|
||
|
|
traceback.print_exc()
|
||
|
|
return {
|
||
|
|
"status": "error",
|
||
|
|
"error": f"未知错误: {str(e)}"
|
||
|
|
}
|
||
|
|
|
||
|
|
def get_user_info(self):
|
||
|
|
"""获取登录成功后的用户信息"""
|
||
|
|
return self.user_info
|