feat: 实现 PDD 任务列表 UI (#4)
This commit is contained in:
+505
-48
@@ -25,83 +25,540 @@
|
||||
“开始自动获取”。其余操作全部只读本地数据库。
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Iterable, Optional
|
||||
|
||||
from PyQt5.QtCore import QAbstractTableModel, QModelIndex, Qt, pyqtSignal
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView,
|
||||
QGridLayout,
|
||||
QHeaderView,
|
||||
QHBoxLayout,
|
||||
QSizePolicy,
|
||||
QShortcut,
|
||||
QStackedWidget,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
from qfluentwidgets import (
|
||||
BodyLabel,
|
||||
CaptionLabel,
|
||||
CardWidget,
|
||||
ComboBox,
|
||||
FluentIcon as FIF,
|
||||
LineEdit,
|
||||
PrimaryPushButton,
|
||||
PushButton,
|
||||
SearchLineEdit,
|
||||
SubtitleLabel,
|
||||
TableView,
|
||||
TitleLabel,
|
||||
)
|
||||
|
||||
|
||||
PAGE_SIZE = 50
|
||||
DETAIL_COLUMN = 8
|
||||
REMOTE_TASK_ID_ROLE = Qt.UserRole + 1
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TaskRow:
|
||||
"""任务表格的一行,只保存列表展示和搜索需要的字段。"""
|
||||
|
||||
remote_task_id: str
|
||||
task_type: str
|
||||
goods_id: str = ""
|
||||
title: str = ""
|
||||
color: str = ""
|
||||
size: str = ""
|
||||
price_cents: Optional[int] = None
|
||||
price_is_starting: bool = False
|
||||
quantity: Optional[int] = None
|
||||
status: str = "待执行"
|
||||
updated_at: str = ""
|
||||
|
||||
|
||||
class TaskTableModel(QAbstractTableModel):
|
||||
"""PDD 任务表格模型,当前使用内存数据模拟 Repository 查询结果。"""
|
||||
|
||||
HEADERS = (
|
||||
"任务类型",
|
||||
"商品标题",
|
||||
"颜色",
|
||||
"尺码",
|
||||
"价格",
|
||||
"数量",
|
||||
"状态",
|
||||
"更新时间",
|
||||
"操作",
|
||||
)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._source_rows: list[TaskRow] = []
|
||||
self._filtered_rows: list[TaskRow] = []
|
||||
self._rows: list[TaskRow] = []
|
||||
self._task_type = "全部"
|
||||
self._status = "全部"
|
||||
self._keyword = ""
|
||||
self._sort_column = 7
|
||||
self._sort_order = Qt.DescendingOrder
|
||||
|
||||
def rowCount(self, parent=QModelIndex()) -> int:
|
||||
return 0 if parent.isValid() else len(self._rows)
|
||||
|
||||
def columnCount(self, parent=QModelIndex()) -> int:
|
||||
return 0 if parent.isValid() else len(self.HEADERS)
|
||||
|
||||
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
||||
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
|
||||
return self.HEADERS[section]
|
||||
return super().headerData(section, orientation, role)
|
||||
|
||||
def data(self, index, role=Qt.DisplayRole):
|
||||
if not index.isValid() or not 0 <= index.row() < len(self._rows):
|
||||
return None
|
||||
|
||||
task = self._rows[index.row()]
|
||||
column = index.column()
|
||||
|
||||
if role == Qt.DisplayRole:
|
||||
return self._display_value(task, column)
|
||||
|
||||
if role == Qt.TextAlignmentRole:
|
||||
if column in (0, 6, 8):
|
||||
return Qt.AlignCenter
|
||||
if column in (4, 5):
|
||||
return Qt.AlignRight | Qt.AlignVCenter
|
||||
return Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
if role == Qt.ToolTipRole:
|
||||
if column == 1:
|
||||
return task.title or "尚未获取标题"
|
||||
if column == DETAIL_COLUMN:
|
||||
return f"查看任务 {task.remote_task_id} 的详情"
|
||||
|
||||
if role == REMOTE_TASK_ID_ROLE:
|
||||
return task.remote_task_id
|
||||
|
||||
return None
|
||||
|
||||
def flags(self, index):
|
||||
if not index.isValid():
|
||||
return Qt.NoItemFlags
|
||||
return Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
|
||||
def canFetchMore(self, parent=QModelIndex()) -> bool:
|
||||
"""告诉 Qt 内存结果中是否还有下一页。"""
|
||||
|
||||
return not parent.isValid() and len(self._rows) < len(self._filtered_rows)
|
||||
|
||||
def fetchMore(self, parent=QModelIndex()) -> None:
|
||||
"""每次再展示最多 50 行,模拟后续 Repository 的分页行为。"""
|
||||
|
||||
if parent.isValid() or not self.canFetchMore(parent):
|
||||
return
|
||||
|
||||
start = len(self._rows)
|
||||
end = min(start + PAGE_SIZE, len(self._filtered_rows))
|
||||
self.beginInsertRows(QModelIndex(), start, end - 1)
|
||||
self._rows.extend(self._filtered_rows[start:end])
|
||||
self.endInsertRows()
|
||||
|
||||
def sort(self, column, order=Qt.AscendingOrder) -> None:
|
||||
if not 0 <= column < len(self.HEADERS):
|
||||
return
|
||||
|
||||
loaded_count = max(PAGE_SIZE, len(self._rows))
|
||||
self.beginResetModel()
|
||||
self._sort_column = column
|
||||
self._sort_order = order
|
||||
self._sort_filtered_rows()
|
||||
self._rows = self._filtered_rows[:loaded_count]
|
||||
self.endResetModel()
|
||||
|
||||
def set_tasks(self, tasks: Iterable[TaskRow]) -> None:
|
||||
"""替换内存任务列表;后续由 Repository 查询结果调用。"""
|
||||
|
||||
self.beginResetModel()
|
||||
self._source_rows = list(tasks)
|
||||
self._rebuild_filtered_rows()
|
||||
self._rows = self._filtered_rows[:PAGE_SIZE]
|
||||
self.endResetModel()
|
||||
|
||||
def apply_filters(self, task_type: str, status: str, keyword: str) -> None:
|
||||
"""按类型、状态和关键词同时筛选当前内存任务。"""
|
||||
|
||||
self.beginResetModel()
|
||||
self._task_type = task_type
|
||||
self._status = status
|
||||
self._keyword = keyword.strip().casefold()
|
||||
self._rebuild_filtered_rows()
|
||||
self._rows = self._filtered_rows[:PAGE_SIZE]
|
||||
self.endResetModel()
|
||||
|
||||
def row_at(self, row: int) -> Optional[TaskRow]:
|
||||
"""根据当前可见行获取任务,越界时返回 None。"""
|
||||
|
||||
if 0 <= row < len(self._rows):
|
||||
return self._rows[row]
|
||||
return None
|
||||
|
||||
def find_row(self, remote_task_id: str) -> int:
|
||||
"""根据稳定任务编号查找当前已加载行。"""
|
||||
|
||||
for row, task in enumerate(self._rows):
|
||||
if task.remote_task_id == remote_task_id:
|
||||
return row
|
||||
return -1
|
||||
|
||||
def filtered_count(self) -> int:
|
||||
return len(self._filtered_rows)
|
||||
|
||||
def _rebuild_filtered_rows(self) -> None:
|
||||
self._filtered_rows = [
|
||||
task for task in self._source_rows if self._matches_filters(task)
|
||||
]
|
||||
self._sort_filtered_rows()
|
||||
|
||||
def _matches_filters(self, task: TaskRow) -> bool:
|
||||
if self._task_type != "全部" and task.task_type != self._task_type:
|
||||
return False
|
||||
if self._status != "全部" and task.status != self._status:
|
||||
return False
|
||||
if not self._keyword:
|
||||
return True
|
||||
|
||||
searchable_text = " ".join(
|
||||
(task.remote_task_id, task.goods_id, task.title)
|
||||
).casefold()
|
||||
return self._keyword in searchable_text
|
||||
|
||||
def _sort_filtered_rows(self) -> None:
|
||||
reverse = self._sort_order == Qt.DescendingOrder
|
||||
self._filtered_rows.sort(
|
||||
key=lambda task: self._sort_value(task, self._sort_column),
|
||||
reverse=reverse,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _sort_value(task: TaskRow, column: int):
|
||||
values = (
|
||||
task.task_type,
|
||||
task.title,
|
||||
task.color,
|
||||
task.size,
|
||||
task.price_cents if task.price_cents is not None else -1,
|
||||
task.quantity if task.quantity is not None else -1,
|
||||
task.status,
|
||||
task.updated_at,
|
||||
task.remote_task_id,
|
||||
)
|
||||
value = values[column]
|
||||
return value.casefold() if isinstance(value, str) else value
|
||||
|
||||
@staticmethod
|
||||
def _display_value(task: TaskRow, column: int) -> str:
|
||||
if column == 0:
|
||||
return task.task_type
|
||||
if column == 1:
|
||||
return task.title or "尚未获取标题"
|
||||
if column == 2:
|
||||
return task.color or "—"
|
||||
if column == 3:
|
||||
return task.size or "—"
|
||||
if column == 4:
|
||||
if task.price_cents is None:
|
||||
return "—"
|
||||
suffix = " 起" if task.price_is_starting else ""
|
||||
return f"¥{task.price_cents / 100:.2f}{suffix}"
|
||||
if column == 5:
|
||||
return "—" if task.quantity is None else str(task.quantity)
|
||||
if column == 6:
|
||||
return task.status
|
||||
if column == 7:
|
||||
return task.updated_at or "—"
|
||||
return "详情"
|
||||
|
||||
|
||||
class PDDTaskPage(QWidget):
|
||||
"""商品与规格配置页。"""
|
||||
"""展示、筛选本机任务,并把用户操作转发给事件层。"""
|
||||
|
||||
autoFetchRequested = pyqtSignal()
|
||||
searchRequested = pyqtSignal(dict)
|
||||
refreshRequested = pyqtSignal()
|
||||
detailRequested = pyqtSignal(str)
|
||||
|
||||
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.taskModel = TaskTableModel(self)
|
||||
self._build_command_area()
|
||||
self._build_content_area()
|
||||
self._build_status_area()
|
||||
self._build_page_layout()
|
||||
self._connect_ui_events()
|
||||
self._update_empty_state()
|
||||
|
||||
self.colorInput = LineEdit(self)
|
||||
self.colorInput.setPlaceholderText("例如:黑色")
|
||||
self.colorInput.setClearButtonEnabled(True)
|
||||
self.colorInput.setAccessibleName("目标颜色")
|
||||
def _build_command_area(self) -> None:
|
||||
self.autoFetchButton = PrimaryPushButton(
|
||||
FIF.PLAY, "开始自动获取", self
|
||||
)
|
||||
self.autoFetchButton.setMinimumWidth(132)
|
||||
self.autoFetchButton.setAccessibleName("开始自动获取 PDD 任务")
|
||||
|
||||
self.sizeInput = LineEdit(self)
|
||||
self.sizeInput.setPlaceholderText("例如:L")
|
||||
self.sizeInput.setClearButtonEnabled(True)
|
||||
self.sizeInput.setAccessibleName("目标尺码")
|
||||
self.taskTypeCombo = ComboBox(self)
|
||||
self.taskTypeCombo.addItems(("全部", "采集", "采购"))
|
||||
self.taskTypeCombo.setMinimumWidth(100)
|
||||
self.taskTypeCombo.setAccessibleName("任务类型")
|
||||
|
||||
self.startButton = PrimaryPushButton(FIF.PLAY, "开始任务", self)
|
||||
self.startButton.setAccessibleName("开始自动购买任务")
|
||||
self.statusLabel = CaptionLabel("等待启动", self)
|
||||
self.statusCombo = ComboBox(self)
|
||||
self.statusCombo.addItems(
|
||||
("全部", "待执行", "执行中", "已完成", "失败", "需要人工处理")
|
||||
)
|
||||
self.statusCombo.setMinimumWidth(130)
|
||||
self.statusCombo.setAccessibleName("任务状态")
|
||||
|
||||
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)
|
||||
self.keywordInput = SearchLineEdit(self)
|
||||
self.keywordInput.setPlaceholderText("任务编号、商品编号或商品标题")
|
||||
self.keywordInput.setClearButtonEnabled(True)
|
||||
self.keywordInput.setMinimumWidth(240)
|
||||
self.keywordInput.setAccessibleName("任务关键词")
|
||||
|
||||
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)
|
||||
self.searchButton = PushButton(FIF.SEARCH, "搜索", self)
|
||||
self.searchButton.setAccessibleName("搜索本地任务")
|
||||
|
||||
actionLayout = QHBoxLayout()
|
||||
actionLayout.setContentsMargins(0, 6, 0, 0)
|
||||
actionLayout.addWidget(self.statusLabel)
|
||||
actionLayout.addStretch(1)
|
||||
actionLayout.addWidget(self.startButton)
|
||||
cardLayout.addLayout(actionLayout)
|
||||
self.refreshButton = PushButton(FIF.SYNC, "刷新", self)
|
||||
self.refreshButton.setAccessibleName("刷新本地任务表格")
|
||||
|
||||
self.commandCard = CardWidget(self)
|
||||
commandLayout = QGridLayout(self.commandCard)
|
||||
commandLayout.setContentsMargins(20, 16, 20, 18)
|
||||
commandLayout.setHorizontalSpacing(12)
|
||||
commandLayout.setVerticalSpacing(6)
|
||||
commandLayout.addWidget(CaptionLabel("任务类型", self.commandCard), 0, 1)
|
||||
commandLayout.addWidget(CaptionLabel("任务状态", self.commandCard), 0, 2)
|
||||
commandLayout.addWidget(CaptionLabel("关键词", self.commandCard), 0, 3)
|
||||
commandLayout.addWidget(self.autoFetchButton, 1, 0)
|
||||
commandLayout.addWidget(self.taskTypeCombo, 1, 1)
|
||||
commandLayout.addWidget(self.statusCombo, 1, 2)
|
||||
commandLayout.addWidget(self.keywordInput, 1, 3)
|
||||
commandLayout.addWidget(self.searchButton, 1, 4)
|
||||
commandLayout.addWidget(self.refreshButton, 1, 5)
|
||||
commandLayout.setColumnStretch(3, 1)
|
||||
|
||||
def _build_content_area(self) -> None:
|
||||
self.taskTable = TableView(self)
|
||||
self.taskTable.setModel(self.taskModel)
|
||||
self.taskTable.setAccessibleName("本机 PDD 任务列表")
|
||||
self.taskTable.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.taskTable.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.taskTable.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
||||
self.taskTable.setAlternatingRowColors(True)
|
||||
self.taskTable.setWordWrap(False)
|
||||
self.taskTable.setSortingEnabled(True)
|
||||
self.taskTable.verticalHeader().setVisible(False)
|
||||
|
||||
header = self.taskTable.horizontalHeader()
|
||||
header.setSectionResizeMode(QHeaderView.Interactive)
|
||||
header.setSectionResizeMode(1, QHeaderView.Stretch)
|
||||
header.setMinimumSectionSize(64)
|
||||
self.taskTable.setColumnWidth(0, 82)
|
||||
self.taskTable.setColumnWidth(2, 108)
|
||||
self.taskTable.setColumnWidth(3, 82)
|
||||
self.taskTable.setColumnWidth(4, 96)
|
||||
self.taskTable.setColumnWidth(5, 70)
|
||||
self.taskTable.setColumnWidth(6, 116)
|
||||
self.taskTable.setColumnWidth(7, 156)
|
||||
self.taskTable.setColumnWidth(8, 72)
|
||||
self.taskTable.sortByColumn(7, Qt.DescendingOrder)
|
||||
|
||||
self.emptyPage = QWidget(self)
|
||||
emptyLayout = QVBoxLayout(self.emptyPage)
|
||||
emptyLayout.setContentsMargins(32, 32, 32, 32)
|
||||
emptyLayout.addStretch(1)
|
||||
self.emptyTitleLabel = SubtitleLabel("还没有领取过任务", self.emptyPage)
|
||||
self.emptyTitleLabel.setAlignment(Qt.AlignCenter)
|
||||
self.emptyMessageLabel = BodyLabel(
|
||||
"点击“开始自动获取”开始领取任务。", self.emptyPage
|
||||
)
|
||||
self.emptyMessageLabel.setAlignment(Qt.AlignCenter)
|
||||
self.emptyMessageLabel.setWordWrap(True)
|
||||
self.clearFiltersButton = PushButton("清除筛选条件", self.emptyPage)
|
||||
self.clearFiltersButton.setAccessibleName("清除全部任务筛选条件")
|
||||
|
||||
clearButtonLayout = QHBoxLayout()
|
||||
clearButtonLayout.addStretch(1)
|
||||
clearButtonLayout.addWidget(self.clearFiltersButton)
|
||||
clearButtonLayout.addStretch(1)
|
||||
|
||||
emptyLayout.addWidget(self.emptyTitleLabel)
|
||||
emptyLayout.addWidget(self.emptyMessageLabel)
|
||||
emptyLayout.addSpacing(8)
|
||||
emptyLayout.addLayout(clearButtonLayout)
|
||||
emptyLayout.addStretch(1)
|
||||
|
||||
self.contentStack = QStackedWidget(self)
|
||||
self.contentStack.addWidget(self.taskTable)
|
||||
self.contentStack.addWidget(self.emptyPage)
|
||||
|
||||
def _build_status_area(self) -> None:
|
||||
self.statusCard = CardWidget(self)
|
||||
statusLayout = QHBoxLayout(self.statusCard)
|
||||
statusLayout.setContentsMargins(16, 10, 16, 10)
|
||||
self.statusLabel = CaptionLabel(
|
||||
"自动获取:已停止 · 当前没有执行中的任务", self.statusCard
|
||||
)
|
||||
self.statusLabel.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
||||
self.statusLabel.setAccessibleName("PDD 任务运行状态")
|
||||
statusLayout.addWidget(self.statusLabel, 1)
|
||||
|
||||
def _build_page_layout(self) -> None:
|
||||
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)
|
||||
layout.addWidget(TitleLabel("PDD 任务", self))
|
||||
layout.addWidget(self.commandCard)
|
||||
layout.addWidget(self.contentStack, 1)
|
||||
layout.addWidget(self.statusCard)
|
||||
|
||||
def _connect_ui_events(self) -> None:
|
||||
self.autoFetchButton.clicked.connect(self.autoFetchRequested.emit)
|
||||
self.searchButton.clicked.connect(self._apply_filters)
|
||||
self.keywordInput.returnPressed.connect(self._apply_filters)
|
||||
self.refreshButton.clicked.connect(self.refreshRequested.emit)
|
||||
self.clearFiltersButton.clicked.connect(self.clear_filters)
|
||||
self.taskTable.clicked.connect(self._on_table_clicked)
|
||||
self.taskTable.activated.connect(self._on_table_activated)
|
||||
|
||||
self.findShortcut = QShortcut(QKeySequence.Find, self)
|
||||
self.findShortcut.activated.connect(self.keywordInput.setFocus)
|
||||
|
||||
def set_tasks(self, tasks: Iterable[TaskRow]) -> None:
|
||||
"""显示任务数据,并尽量恢复原来选中的稳定任务编号。"""
|
||||
|
||||
current_task_id = self.current_task_id()
|
||||
self.taskModel.set_tasks(tasks)
|
||||
self._update_empty_state()
|
||||
self._restore_current_task(current_task_id)
|
||||
|
||||
def set_loading(self) -> None:
|
||||
"""显示正在读取本地任务的状态。"""
|
||||
|
||||
self._show_empty_state("正在加载任务", "正在读取本地任务,请稍候。", False)
|
||||
|
||||
def set_load_error(self, message: str) -> None:
|
||||
"""显示本地任务加载失败状态,不清除当前筛选条件。"""
|
||||
|
||||
self._show_empty_state(
|
||||
"任务加载失败",
|
||||
message or "无法读取本地任务,请检查日志后重试。",
|
||||
False,
|
||||
)
|
||||
|
||||
def set_auto_fetch_state(self, state: str) -> None:
|
||||
"""更新自动获取按钮;state 为 stopped/starting/running/stopping。"""
|
||||
|
||||
states = {
|
||||
"stopped": ("开始自动获取", FIF.PLAY, True),
|
||||
"starting": ("正在启动", FIF.SYNC, False),
|
||||
"running": ("停止自动获取", FIF.PAUSE, True),
|
||||
"stopping": ("正在停止", FIF.PAUSE, False),
|
||||
}
|
||||
text, icon, enabled = states.get(state, states["stopped"])
|
||||
self.autoFetchButton.setText(text)
|
||||
self.autoFetchButton.setIcon(icon)
|
||||
self.autoFetchButton.setEnabled(enabled)
|
||||
|
||||
def set_engine_status(self, text: str) -> None:
|
||||
"""更新页面底部的任务引擎状态说明。"""
|
||||
|
||||
self.statusLabel.setText(text or "自动获取:已停止 · 当前没有执行中的任务")
|
||||
|
||||
def clear_filters(self) -> None:
|
||||
"""清除全部搜索条件并恢复当前内存任务列表。"""
|
||||
|
||||
self.taskTypeCombo.setCurrentIndex(0)
|
||||
self.statusCombo.setCurrentIndex(0)
|
||||
self.keywordInput.clear()
|
||||
self._apply_filters()
|
||||
|
||||
def current_task_id(self) -> str:
|
||||
"""返回当前行的稳定任务编号,没有当前行时返回空字符串。"""
|
||||
|
||||
index = self.taskTable.currentIndex()
|
||||
task = self.taskModel.row_at(index.row()) if index.isValid() else None
|
||||
return task.remote_task_id if task else ""
|
||||
|
||||
def _apply_filters(self) -> None:
|
||||
current_task_id = self.current_task_id()
|
||||
filters = {
|
||||
"task_type": self.taskTypeCombo.currentText(),
|
||||
"status": self.statusCombo.currentText(),
|
||||
"keyword": self.keywordInput.text().strip(),
|
||||
}
|
||||
self.taskModel.apply_filters(
|
||||
filters["task_type"], filters["status"], filters["keyword"]
|
||||
)
|
||||
self._update_empty_state()
|
||||
self._restore_current_task(current_task_id)
|
||||
self.searchRequested.emit(filters)
|
||||
|
||||
def _on_table_clicked(self, index: QModelIndex) -> None:
|
||||
if index.column() == DETAIL_COLUMN:
|
||||
self._emit_detail(index)
|
||||
|
||||
def _on_table_activated(self, index: QModelIndex) -> None:
|
||||
self._emit_detail(index)
|
||||
|
||||
def _emit_detail(self, index: QModelIndex) -> None:
|
||||
task = self.taskModel.row_at(index.row())
|
||||
if task is not None:
|
||||
self.detailRequested.emit(task.remote_task_id)
|
||||
|
||||
def _restore_current_task(self, remote_task_id: str) -> None:
|
||||
if not remote_task_id:
|
||||
return
|
||||
|
||||
row = self.taskModel.find_row(remote_task_id)
|
||||
if row < 0:
|
||||
return
|
||||
|
||||
index = self.taskModel.index(row, 0)
|
||||
self.taskTable.setCurrentIndex(index)
|
||||
self.taskTable.selectRow(row)
|
||||
self.taskTable.scrollTo(index)
|
||||
|
||||
def _has_active_filters(self) -> bool:
|
||||
return (
|
||||
self.taskTypeCombo.currentIndex() != 0
|
||||
or self.statusCombo.currentIndex() != 0
|
||||
or bool(self.keywordInput.text().strip())
|
||||
)
|
||||
|
||||
def _update_empty_state(self) -> None:
|
||||
if self.taskModel.rowCount() > 0:
|
||||
self.contentStack.setCurrentWidget(self.taskTable)
|
||||
return
|
||||
|
||||
if self._has_active_filters():
|
||||
self._show_empty_state(
|
||||
"没有符合条件的任务",
|
||||
"请调整搜索条件,或者清除筛选后查看全部本地任务。",
|
||||
True,
|
||||
)
|
||||
return
|
||||
|
||||
self._show_empty_state(
|
||||
"还没有领取过任务",
|
||||
"点击“开始自动获取”开始领取任务。",
|
||||
False,
|
||||
)
|
||||
|
||||
def _show_empty_state(
|
||||
self, title: str, message: str, show_clear_button: bool
|
||||
) -> None:
|
||||
self.emptyTitleLabel.setText(title)
|
||||
self.emptyMessageLabel.setText(message)
|
||||
self.clearFiltersButton.setVisible(show_clear_button)
|
||||
self.contentStack.setCurrentWidget(self.emptyPage)
|
||||
|
||||
Reference in New Issue
Block a user