|
@@ -32,7 +32,7 @@ class MainWindow(FluentWindow):
|
|
|
self.user_info = user_info
|
|
self.user_info = user_info
|
|
|
self.heartbeat_timer = None
|
|
self.heartbeat_timer = None
|
|
|
self.expire_countdown_timer = None
|
|
self.expire_countdown_timer = None
|
|
|
- self.expire_label = None # 头部有效期标签
|
|
|
|
|
|
|
+ self.expire_label_targets = [] # 需要同步显示有效期的标签集合
|
|
|
self.is_fullscreen = False # 全屏状态
|
|
self.is_fullscreen = False # 全屏状态
|
|
|
|
|
|
|
|
# 拖动相关变量
|
|
# 拖动相关变量
|
|
@@ -53,8 +53,11 @@ class MainWindow(FluentWindow):
|
|
|
self.setMinimumSize(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT)
|
|
self.setMinimumSize(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT)
|
|
|
self.resize(WINDOW_DEFAULT_WIDTH, WINDOW_DEFAULT_HEIGHT)
|
|
self.resize(WINDOW_DEFAULT_WIDTH, WINDOW_DEFAULT_HEIGHT)
|
|
|
|
|
|
|
|
- # 创建导航界面
|
|
|
|
|
- self.navigationInterface = NavigationInterface(self, showMenuButton=False)
|
|
|
|
|
|
|
+ # 配置导航界面(使用 FluentWindow 自带的 navigationInterface,关闭返回/菜单/折叠)
|
|
|
|
|
+ self.navigationInterface.setMenuButtonVisible(False)
|
|
|
|
|
+ self.navigationInterface.setReturnButtonVisible(False)
|
|
|
|
|
+ self.navigationInterface.setCollapsible(False)
|
|
|
|
|
+ self.navigationInterface.setObjectName("MainNavigation")
|
|
|
|
|
|
|
|
# 创建各个功能界面
|
|
# 创建各个功能界面
|
|
|
self.rechargeInterface = self.create_recharge_interface()
|
|
self.rechargeInterface = self.create_recharge_interface()
|
|
@@ -119,8 +122,8 @@ class MainWindow(FluentWindow):
|
|
|
position=NavigationItemPosition.BOTTOM
|
|
position=NavigationItemPosition.BOTTOM
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- # 设置窗口布局
|
|
|
|
|
- self.hBoxLayout.addWidget(self.navigationInterface)
|
|
|
|
|
|
|
+ # 构建自定义布局结构
|
|
|
|
|
+ self.setup_layout_structure()
|
|
|
|
|
|
|
|
# 设置初始界面
|
|
# 设置初始界面
|
|
|
self.navigationInterface.setCurrentItem("充值")
|
|
self.navigationInterface.setCurrentItem("充值")
|
|
@@ -130,6 +133,37 @@ class MainWindow(FluentWindow):
|
|
|
# F11 全屏切换
|
|
# F11 全屏切换
|
|
|
fullscreen_shortcut = QShortcut(QKeySequence("F11"), self)
|
|
fullscreen_shortcut = QShortcut(QKeySequence("F11"), self)
|
|
|
fullscreen_shortcut.activated.connect(self.toggle_fullscreen)
|
|
fullscreen_shortcut.activated.connect(self.toggle_fullscreen)
|
|
|
|
|
+
|
|
|
|
|
+ def setup_layout_structure(self):
|
|
|
|
|
+ """自定义上下+左右布局结构"""
|
|
|
|
|
+ # 将导航从基础布局中移除,便于重新布置
|
|
|
|
|
+ self.hBoxLayout.removeWidget(self.navigationInterface)
|
|
|
|
|
+ self.widgetLayout.removeWidget(self.stackedWidget)
|
|
|
|
|
+ self.widgetLayout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
+
|
|
|
|
|
+ # 顶层容器(承载标题栏+主体区域)
|
|
|
|
|
+ self.mainContainer = QWidget(self)
|
|
|
|
|
+ self.mainContainerLayout = QVBoxLayout(self.mainContainer)
|
|
|
|
|
+ self.mainContainerLayout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
+ self.mainContainerLayout.setSpacing(0)
|
|
|
|
|
+
|
|
|
|
|
+ # 标题栏
|
|
|
|
|
+ self.headerBar = self.create_header_bar()
|
|
|
|
|
+ self.mainContainerLayout.addWidget(self.headerBar, 0)
|
|
|
|
|
+
|
|
|
|
|
+ # 主体区域:左侧菜单 + 右侧内容
|
|
|
|
|
+ self.bodyContainer = QWidget(self)
|
|
|
|
|
+ self.bodyLayout = QHBoxLayout(self.bodyContainer)
|
|
|
|
|
+ self.bodyLayout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
+ self.bodyLayout.setSpacing(0)
|
|
|
|
|
+
|
|
|
|
|
+ self.navigationInterface.setMinimumWidth(220)
|
|
|
|
|
+ self.navigationInterface.setMaximumWidth(260)
|
|
|
|
|
+ self.bodyLayout.addWidget(self.navigationInterface, 0)
|
|
|
|
|
+ self.bodyLayout.addWidget(self.stackedWidget, 1)
|
|
|
|
|
+
|
|
|
|
|
+ self.mainContainerLayout.addWidget(self.bodyContainer, 1)
|
|
|
|
|
+ self.widgetLayout.addWidget(self.mainContainer)
|
|
|
|
|
|
|
|
def create_logo_label(self) -> QLabel:
|
|
def create_logo_label(self) -> QLabel:
|
|
|
"""创建Logo标签"""
|
|
"""创建Logo标签"""
|
|
@@ -166,6 +200,75 @@ class MainWindow(FluentWindow):
|
|
|
logger.info("使用文字Logo")
|
|
logger.info("使用文字Logo")
|
|
|
|
|
|
|
|
return logo_label
|
|
return logo_label
|
|
|
|
|
+
|
|
|
|
|
+ def create_header_bar(self) -> QWidget:
|
|
|
|
|
+ """创建顶部标题栏"""
|
|
|
|
|
+ header = QWidget(self)
|
|
|
|
|
+ header.setObjectName("HeaderBar")
|
|
|
|
|
+ header_layout = QHBoxLayout(header)
|
|
|
|
|
+ header_layout.setContentsMargins(24, 12, 24, 12)
|
|
|
|
|
+ header_layout.setSpacing(16)
|
|
|
|
|
+
|
|
|
|
|
+ # 左侧:logo + 软件名称
|
|
|
|
|
+ left_container = QWidget(header)
|
|
|
|
|
+ left_layout = QHBoxLayout(left_container)
|
|
|
|
|
+ left_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
+ left_layout.setSpacing(12)
|
|
|
|
|
+
|
|
|
|
|
+ logo_label = self.create_logo_label()
|
|
|
|
|
+ left_layout.addWidget(logo_label, 0, Qt.AlignmentFlag.AlignVCenter)
|
|
|
|
|
+
|
|
|
|
|
+ title_label = QLabel("游铛铛 · 客户端", left_container)
|
|
|
|
|
+ title_font = QFont()
|
|
|
|
|
+ title_font.setPointSize(18)
|
|
|
|
|
+ title_font.setBold(True)
|
|
|
|
|
+ title_label.setFont(title_font)
|
|
|
|
|
+ title_label.setStyleSheet("color: #1f2933;")
|
|
|
|
|
+ left_layout.addWidget(title_label, 0, Qt.AlignmentFlag.AlignVCenter)
|
|
|
|
|
+ left_layout.addStretch(1)
|
|
|
|
|
+
|
|
|
|
|
+ header_layout.addWidget(left_container, 1)
|
|
|
|
|
+
|
|
|
|
|
+ # 右侧:倒计时 + 用户名 + 退出按钮
|
|
|
|
|
+ right_container = QWidget(header)
|
|
|
|
|
+ right_layout = QHBoxLayout(right_container)
|
|
|
|
|
+ right_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
+ right_layout.setSpacing(12)
|
|
|
|
|
+
|
|
|
|
|
+ expire_label = QLabel("卡密倒计时: 加载中...", right_container)
|
|
|
|
|
+ expire_label.setObjectName("HeaderExpireLabel")
|
|
|
|
|
+ self.register_expire_label(expire_label, prefix="卡密倒计时")
|
|
|
|
|
+ right_layout.addWidget(expire_label, 0, Qt.AlignmentFlag.AlignVCenter)
|
|
|
|
|
+
|
|
|
|
|
+ username = self.user_info.get("username", "未知")
|
|
|
|
|
+ self.header_user_label = QLabel(f"当前用户: {username}", right_container)
|
|
|
|
|
+ self.header_user_label.setStyleSheet("color: #4b5563; font-size: 14px;")
|
|
|
|
|
+ right_layout.addWidget(self.header_user_label, 0, Qt.AlignmentFlag.AlignVCenter)
|
|
|
|
|
+
|
|
|
|
|
+ logout_btn = PrimaryPushButton("退出登录", right_container)
|
|
|
|
|
+ logout_btn.setIcon(qta.icon('fa5s.sign-out-alt', color='white'))
|
|
|
|
|
+ logout_btn.setFixedHeight(36)
|
|
|
|
|
+ logout_btn.clicked.connect(self.handle_logout)
|
|
|
|
|
+ right_layout.addWidget(logout_btn, 0, Qt.AlignmentFlag.AlignVCenter)
|
|
|
|
|
+
|
|
|
|
|
+ header_layout.addWidget(right_container, 0)
|
|
|
|
|
+ header_layout.setStretchFactor(left_container, 1)
|
|
|
|
|
+
|
|
|
|
|
+ header.setStyleSheet("""
|
|
|
|
|
+ QWidget#HeaderBar {
|
|
|
|
|
+ background-color: #f9fafb;
|
|
|
|
|
+ border-bottom: 1px solid #e5e7eb;
|
|
|
|
|
+ }
|
|
|
|
|
+ """)
|
|
|
|
|
+
|
|
|
|
|
+ return header
|
|
|
|
|
+
|
|
|
|
|
+ def register_expire_label(self, label: QLabel, prefix: str = "有效期"):
|
|
|
|
|
+ """注册需要显示有效期倒计时的标签"""
|
|
|
|
|
+ if not hasattr(self, "expire_label_targets"):
|
|
|
|
|
+ self.expire_label_targets = []
|
|
|
|
|
+ self.expire_label_targets.append((label, prefix))
|
|
|
|
|
+ self.update_expire_time_display()
|
|
|
|
|
|
|
|
def toggle_fullscreen(self):
|
|
def toggle_fullscreen(self):
|
|
|
"""切换全屏状态"""
|
|
"""切换全屏状态"""
|
|
@@ -233,10 +336,10 @@ class MainWindow(FluentWindow):
|
|
|
card_layout.addWidget(user_label)
|
|
card_layout.addWidget(user_label)
|
|
|
|
|
|
|
|
# 有效期显示
|
|
# 有效期显示
|
|
|
- self.expire_label = QLabel("有效期: 加载中...")
|
|
|
|
|
- self.update_expire_time_display()
|
|
|
|
|
- self.expire_label.setStyleSheet("font-size: 14px; color: #666;")
|
|
|
|
|
- card_layout.addWidget(self.expire_label)
|
|
|
|
|
|
|
+ settings_expire_label = QLabel("有效期: 加载中...")
|
|
|
|
|
+ self.register_expire_label(settings_expire_label, prefix="有效期")
|
|
|
|
|
+ settings_expire_label.setStyleSheet("font-size: 14px; color: #666;")
|
|
|
|
|
+ card_layout.addWidget(settings_expire_label)
|
|
|
|
|
|
|
|
# 窗口控制按钮
|
|
# 窗口控制按钮
|
|
|
controls_layout = QHBoxLayout()
|
|
controls_layout = QHBoxLayout()
|
|
@@ -321,7 +424,7 @@ class MainWindow(FluentWindow):
|
|
|
|
|
|
|
|
def update_expire_time_display(self):
|
|
def update_expire_time_display(self):
|
|
|
"""更新有效期显示(实时倒计时)"""
|
|
"""更新有效期显示(实时倒计时)"""
|
|
|
- if not self.expire_label:
|
|
|
|
|
|
|
+ if not self.expire_label_targets:
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
expire_time = self.user_info.get("expire_time")
|
|
expire_time = self.user_info.get("expire_time")
|
|
@@ -342,32 +445,50 @@ class MainWindow(FluentWindow):
|
|
|
days = delta.days
|
|
days = delta.days
|
|
|
hours = delta.seconds // 3600
|
|
hours = delta.seconds // 3600
|
|
|
minutes = (delta.seconds % 3600) // 60
|
|
minutes = (delta.seconds % 3600) // 60
|
|
|
- self.expire_label.setText(
|
|
|
|
|
- f"有效期: {expire_dt.strftime('%Y-%m-%d %H:%M')} "
|
|
|
|
|
|
|
+ remaining_text = (
|
|
|
|
|
+ f"{expire_dt.strftime('%Y-%m-%d %H:%M')} "
|
|
|
f"(剩余 {days}天 {hours}小时 {minutes}分钟)"
|
|
f"(剩余 {days}天 {hours}小时 {minutes}分钟)"
|
|
|
)
|
|
)
|
|
|
- self.expire_label.setStyleSheet("""
|
|
|
|
|
|
|
+ style = """
|
|
|
QLabel {
|
|
QLabel {
|
|
|
- color: #666;
|
|
|
|
|
|
|
+ color: #4b5563;
|
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
|
- padding: 0 16px;
|
|
|
|
|
|
|
+ padding: 0 8px;
|
|
|
}
|
|
}
|
|
|
- """)
|
|
|
|
|
|
|
+ """
|
|
|
else:
|
|
else:
|
|
|
- self.expire_label.setText("有效期: 已过期")
|
|
|
|
|
- self.expire_label.setStyleSheet("""
|
|
|
|
|
|
|
+ remaining_text = "已过期"
|
|
|
|
|
+ style = """
|
|
|
QLabel {
|
|
QLabel {
|
|
|
color: #ff4d4f;
|
|
color: #ff4d4f;
|
|
|
font-weight: bold;
|
|
font-weight: bold;
|
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
|
- padding: 0 16px;
|
|
|
|
|
|
|
+ padding: 0 8px;
|
|
|
}
|
|
}
|
|
|
- """)
|
|
|
|
|
|
|
+ """
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
logger.error(f"更新有效期显示失败: {e}")
|
|
logger.error(f"更新有效期显示失败: {e}")
|
|
|
- self.expire_label.setText(f"有效期: {expire_time}")
|
|
|
|
|
|
|
+ remaining_text = str(expire_time)
|
|
|
|
|
+ style = """
|
|
|
|
|
+ QLabel {
|
|
|
|
|
+ color: #4b5563;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ padding: 0 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+ """
|
|
|
else:
|
|
else:
|
|
|
- self.expire_label.setText("有效期: 未知")
|
|
|
|
|
|
|
+ remaining_text = "未知"
|
|
|
|
|
+ style = """
|
|
|
|
|
+ QLabel {
|
|
|
|
|
+ color: #9ca3af;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ padding: 0 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ for label, prefix in self.expire_label_targets:
|
|
|
|
|
+ label.setText(f"{prefix}: {remaining_text}")
|
|
|
|
|
+ label.setStyleSheet(style)
|
|
|
|
|
|
|
|
def on_expired(self):
|
|
def on_expired(self):
|
|
|
"""处理过期"""
|
|
"""处理过期"""
|