2026-08-06 12:01:38 +08:00
|
|
|
|
"""PDD 任务页的界面结构。
|
|
|
|
|
|
|
|
|
|
|
|
这一页显示**本机已领取的全部任务**(正在做的和早已做完的),
|
|
|
|
|
|
提供搜索、筛选、详情,以及启动/停止自动获取。
|
|
|
|
|
|
完整规范见 `docs/client/05-ui-specification.md`。
|
|
|
|
|
|
|
|
|
|
|
|
改动本文件前必读 `client/AGENTS.md`。以下几条最容易踩:
|
|
|
|
|
|
|
|
|
|
|
|
- 本文件只负责显示和把用户操作转发出去,**不写业务逻辑**,
|
|
|
|
|
|
不发 Admin 请求、不查数据库、不碰手机。这些放 `pdd_ui_event.py`。
|
|
|
|
|
|
- 任务表格必须用**模型/视图**(`QAbstractTableModel` + `TableView`),
|
|
|
|
|
|
并用 `remote_task_id` 作为任务身份。**不要用表格行号**——
|
|
|
|
|
|
行号会随排序和筛选变化,拿它当身份必出错。
|
|
|
|
|
|
- 不要为每个单元格创建常驻 `QWidget`(几万行会把内存吃光)。
|
|
|
|
|
|
“详情”那一列用委托绘制。
|
|
|
|
|
|
- 不要把完整的 `pdd_data` 放进表格模型,那是详情数据,
|
|
|
|
|
|
查看详情时再从数据库读。
|
|
|
|
|
|
- 表格数据只增不减(已完成任务永久保留),所以增量加载是必须的,
|
|
|
|
|
|
照抄 `docs/client/05-ui-specification.md` §5.3 的模板。
|
|
|
|
|
|
- 空状态要分情况:从没领过任务、筛选无结果、加载失败,文案不能一样。
|
|
|
|
|
|
- 图标用 `FluentIcon`,**不得用表情符号**。
|
|
|
|
|
|
- 页面 `objectName` 固定为 `pddTaskPage`,不要改。
|
|
|
|
|
|
|
|
|
|
|
|
注意:本页唯一会产生外部后果(真的去操作手机、可能下单)的命令是
|
|
|
|
|
|
“开始自动获取”。其余操作全部只读本地数据库。
|
|
|
|
|
|
"""
|
2026-08-06 12:06:16 +08:00
|
|
|
|
|
|
|
|
|
|
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)
|