refactor: 拆分页面 UI 模块 (#3)

This commit is contained in:
chengma
2026-08-06 12:06:16 +08:00
parent 1f95de373a
commit 5916de4b92
3 changed files with 243 additions and 0 deletions
+81
View File
@@ -24,3 +24,84 @@
注意:本页唯一会产生外部后果(真的去操作手机、可能下单)的命令是
“开始自动获取”。其余操作全部只读本地数据库。
"""
from PyQt5.QtWidgets import (
QGridLayout,
QHBoxLayout,
QSizePolicy,
QVBoxLayout,
QWidget,
)
from qfluentwidgets import (
CaptionLabel,
CardWidget,
FluentIcon as FIF,
LineEdit,
PrimaryPushButton,
SubtitleLabel,
TitleLabel,
)
class PDDTaskPage(QWidget):
"""商品与规格配置页。"""
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("pddTaskPage")
self.goodsInput = LineEdit(self)
self.goodsInput.setPlaceholderText("输入拼多多商品链接或商品 ID")
self.goodsInput.setClearButtonEnabled(True)
self.goodsInput.setAccessibleName("商品链接或商品 ID")
self.colorInput = LineEdit(self)
self.colorInput.setPlaceholderText("例如:黑色")
self.colorInput.setClearButtonEnabled(True)
self.colorInput.setAccessibleName("目标颜色")
self.sizeInput = LineEdit(self)
self.sizeInput.setPlaceholderText("例如:L")
self.sizeInput.setClearButtonEnabled(True)
self.sizeInput.setAccessibleName("目标尺码")
self.startButton = PrimaryPushButton(FIF.PLAY, "开始任务", self)
self.startButton.setAccessibleName("开始自动购买任务")
self.statusLabel = CaptionLabel("等待启动", self)
card = CardWidget(self)
card.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
cardLayout = QVBoxLayout(card)
cardLayout.setContentsMargins(24, 20, 24, 22)
cardLayout.setSpacing(12)
cardLayout.addWidget(SubtitleLabel("创建任务", card))
cardLayout.addWidget(CaptionLabel("商品链接 / ID", card))
cardLayout.addWidget(self.goodsInput)
specLayout = QGridLayout()
specLayout.setHorizontalSpacing(16)
specLayout.setVerticalSpacing(8)
specLayout.addWidget(CaptionLabel("颜色", card), 0, 0)
specLayout.addWidget(CaptionLabel("尺码", card), 0, 1)
specLayout.addWidget(self.colorInput, 1, 0)
specLayout.addWidget(self.sizeInput, 1, 1)
specLayout.setColumnStretch(0, 1)
specLayout.setColumnStretch(1, 1)
cardLayout.addLayout(specLayout)
actionLayout = QHBoxLayout()
actionLayout.setContentsMargins(0, 6, 0, 0)
actionLayout.addWidget(self.statusLabel)
actionLayout.addStretch(1)
actionLayout.addWidget(self.startButton)
cardLayout.addLayout(actionLayout)
layout = QVBoxLayout(self)
layout.setContentsMargins(36, 30, 36, 36)
layout.setSpacing(16)
layout.addWidget(TitleLabel("pdd任务", self))
# description = BodyLabel("配置商品与目标规格,然后启动自动化任务。", self)
# description.setWordWrap(True)
# layout.addWidget(description)
layout.addWidget(card)
layout.addStretch(1)
+79
View File
@@ -20,3 +20,82 @@
注意:设备连接**不是**独立的顶级页面,统一放在本页,
见 `docs/client/01-requirements.md` §3。
"""
from PyQt5.QtWidgets import QHBoxLayout, QSizePolicy, QVBoxLayout, QWidget
from qfluentwidgets import (
CaptionLabel,
CardWidget,
FluentIcon as FIF,
LineEdit,
PushButton,
SubtitleLabel,
TitleLabel,
)
class DevicePage(QWidget):
"""Android 设备连接页。"""
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("devicePage")
self.addressInput = LineEdit(self)
self.addressInput.setText("192.168.0.173:5555")
self.addressInput.setPlaceholderText("IP:端口")
self.addressInput.setClearButtonEnabled(True)
self.addressInput.setAccessibleName("Android 设备地址")
self.connectButton = PushButton(FIF.LINK, "连接设备", self)
self.connectButton.setAccessibleName("连接 Android 设备")
self.deviceStatusLabel = CaptionLabel("当前状态:未连接", self)
card = CardWidget(self)
card.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
cardLayout = QVBoxLayout(card)
cardLayout.setContentsMargins(24, 20, 24, 22)
cardLayout.setSpacing(12)
cardLayout.addWidget(SubtitleLabel("无线调试设备", card))
cardLayout.addWidget(CaptionLabel("设备地址", card))
connectionLayout = QHBoxLayout()
connectionLayout.setSpacing(12)
connectionLayout.addWidget(self.addressInput, 1)
connectionLayout.addWidget(self.connectButton)
cardLayout.addLayout(connectionLayout)
cardLayout.addWidget(self.deviceStatusLabel)
layout = QVBoxLayout(self)
layout.setContentsMargins(36, 30, 36, 36)
layout.setSpacing(16)
layout.addWidget(TitleLabel("设备管理", self))
# description = BodyLabel("管理 uiautomator2 使用的 Android 设备连接。", self)
# description.setWordWrap(True)
# layout.addWidget(description)
layout.addWidget(card)
layout.addStretch(1)
class SettingsPage(QWidget):
"""后续设置项的扩展入口。"""
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("settingsPage")
card = CardWidget(self)
card.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
cardLayout = QVBoxLayout(card)
cardLayout.setContentsMargins(24, 20, 24, 22)
cardLayout.setSpacing(8)
cardLayout.addWidget(SubtitleLabel("应用设置", card))
# hint = BodyLabel("主题、日志和任务策略可以在这里继续扩展。", card)
# hint.setWordWrap(True)
# cardLayout.addWidget(hint)
layout = QVBoxLayout(self)
layout.setContentsMargins(36, 30, 36, 36)
layout.setSpacing(16)
layout.addWidget(TitleLabel("设置", self))
layout.addWidget(card)
layout.addStretch(1)
+83
View File
@@ -0,0 +1,83 @@
"""应用与主窗口装配。
本文件只负责把窗口、导航和页面拼起来,不写业务逻辑。
改动本文件前必读 `client/AGENTS.md`。以下几条最容易踩:
- 图标一律用 `FluentIcon`,**不得使用表情符号充当图标**。
- 不得在这里发起 Admin 请求、SQLite 长查询或 uiautomator2 调用,
这些会卡死 Qt 主线程。业务事件放到各页面的 `*_ui_event.py`。
- 页面的 `objectName` 必须唯一且稳定,改名会破坏导航。
- 用布局和伸缩项排版,**不要用固定坐标**。
- 不要用全局 QSS 覆盖 Fluent 控件;局部样式必须保留悬停、按下、
焦点和禁用四种状态。
- 需要文件路径时走 `data_dir()` / `app_dir()`,不要硬编码,
见 `docs/client/03-data-model.md` §2.2。
"""
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
from qfluentwidgets import (
FluentIcon as FIF,
FluentWindow,
NavigationItemPosition,
Theme,
setTheme,
)
from .pdd_ui import PDDTaskPage
from .settings_ui import DevicePage, SettingsPage
class MainWindow(FluentWindow):
"""应用主窗口。"""
def __init__(self):
super().__init__()
self.pddTaskPage = PDDTaskPage(self)
self.devicePage = DevicePage(self)
self.settingsPage = SettingsPage(self)
self.addSubInterface(self.pddTaskPage, FIF.HOME, "pdd")
self.addSubInterface(self.devicePage, FIF.IOT, "设备")
self.addSubInterface(
self.settingsPage,
FIF.SETTING,
"设置",
NavigationItemPosition.BOTTOM,
)
self.setWindowTitle("采集采购工具")
self.setMinimumSize(820, 560)
self.resize(1040, 680)
self._moveToScreenCenter()
def _moveToScreenCenter(self):
screen = QApplication.primaryScreen()
if screen is None:
return
frame = self.frameGeometry()
frame.moveCenter(screen.availableGeometry().center())
self.move(frame.topLeft())
def ui_main():
"""启动桌面应用。"""
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
app = QApplication(sys.argv)
setTheme(Theme.AUTO)
window = MainWindow()
window.show()
return app.exec_()
if __name__ == "__main__":
sys.exit(ui_main())