wuzilong преди 11 месеца
родител
ревизия
a6b3786314
променени са 2 файла, в които са добавени 144 реда и са изтрити 23 реда
  1. 1 1
      client/config/user_data.enc
  2. 143 22
      client/src/ui/main_window.py

+ 1 - 1
client/config/user_data.enc

@@ -1 +1 @@
-gAAAAABpHuc1tBkWtHr_m7ipIq4CciV6PugNLcNQ-JfVqvE4SgQxL6QxleemeWsKPS-IZwrJ59D2C_BdUMGwhqCGpSF5om1048NZGEI3dP7266jKKhyXqGIwTuHCf9IBYcf2W6PqavyM3cPSNWhvkAWcXVF4TfHyxNYCGYr6ZwwdWooLi2H9l7TEeuIDycx7xg46wyinycD4YgoZOKKvzNA9OFLivkTcmeilPD26BVBiz2JjlbvAGLILtDi9UFQbKmZuT2TJvPMNNaHuNIaKgJbBca7lCfNJlFA_Mpp8R1rF0SxtZ0nNY3Pne0bh1G9uboLlnxWjqEm-WmSbcK3ORkbIi28eAs91PQ4Z1rnGLXb6cRZzI9D0AgHIJ-adgYAFwptG7K0K6OK639saZO9rWjKxl5vMqUILyOyibP1ZVJaOoq6AMMg9irDqzqKmw-yaDPsWXwxw1elqTgBYDcuQUvdk8MzHgM67yzJP-hgdgVoGoK5DfoEw0R5Jpb8oxgXo8oa9fnVMpvfn
+gAAAAABpH8WyW_NQdim3LiJSN48n9B4hjeUwfLGXXujl5EiSqj5017Nppb2l2RG3uWAmVHrT7zXxMDXoWvqB1QS2hFVL1v4NOweeQpcYGQx8DW67X9M8qD_fGEZv8jjMmaLz7iqe8P4nc43vrl9PPce_so8pT-rnnjTUEXEEpdQaNkcRRO1Mh3z1SkA01HTAMpkDHcg1SgDgAofsPTjJcXlyiWxWzDkfFonzR0XnNHz9uH8HoKnnA4bANtui9ShA6_zEpGyajf2gggcpsotM1C1lINl_oho1mDzeh3eUaHYSD2dZxieBUY4FbA2jBfId5C9Y0oIOPNEf5Geh29KbTNmZ_MoOR01KLgwRG0V5YXlFr7jzgQCgC4DmMx3u4aQVGeHysCJjif5TOZrPcOlDpjdRrIsN1owIPTUoG72B9AYAYgyFuv-JOnir3ziLIHkuyFgUJAVARUb0tuiSZtQm7JM8jQ_PBHwIuy1kmYcoLvr-tSIJ60Pk2o1HFEBZvgnnmpOoSGjuZ-AK

+ 143 - 22
client/src/ui/main_window.py

@@ -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):
         """处理过期"""
         """处理过期"""