feat(qml): task #17 任务队列 sidebar 接 taskQueue.model
MainWindow.qml sidebar 改造: - header: 加 "N 进行中" 计数(绑定 taskQueue.runningCount + pendingCount,0 时隐藏) - ListView model: 硬编码 ListModel → taskQueue.model(_TaskListModel QAbstractListModel) - 空态占位: "暂无任务"(visible: count === 0) - delegate 改造,56px 高 ColumnLayout 三行: 1. prompt 摘要(>14 字省略) 2. 状态彩色文字(pending=橙 / running=蓝 / completed=绿 / failed=红) + 耗时 3. 进行中任务的细进度条(高 2px,绑 progress 0-1) - delegate 用 required property 声明 6 个 role:taskId / prompt / status / progress / statusText / elapsed - 右键点击 pending/running 任务 → taskQueue.cancelTask(taskId) 至此 生成 → sidebar → 历史 闭环完整: - 用户在图片生成 tab 点 "生成图片" → ImageGenBridge.submitTask - TaskQueueManager 信号 → TaskQueueBridge._on_task_added → upsert 进 model - sidebar ListView 自动渲染新行,状态文字 + 进度条同步 - 完成后 ImageGenBridge → HistoryBridge.addNew,历史 tab 也会增量 视觉验证:QML_AUTO_LOGIN=1 启动主窗口,空态正确显示"暂无任务",UI 无回归。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Showing
1 changed file
with
97 additions
and
13 deletions
| ... | @@ -146,16 +146,30 @@ Rectangle { | ... | @@ -146,16 +146,30 @@ Rectangle { |
| 146 | implicitHeight: 48 | 146 | implicitHeight: 48 |
| 147 | color: App.Theme.bgSurface | 147 | color: App.Theme.bgSurface |
| 148 | 148 | ||
| 149 | Label { | 149 | RowLayout { |
| 150 | anchors.left: parent.left | 150 | anchors.left: parent.left |
| 151 | anchors.right: parent.right | ||
| 151 | anchors.leftMargin: App.Theme.space4 | 152 | anchors.leftMargin: App.Theme.space4 |
| 153 | anchors.rightMargin: App.Theme.space4 | ||
| 152 | anchors.verticalCenter: parent.verticalCenter | 154 | anchors.verticalCenter: parent.verticalCenter |
| 155 | spacing: App.Theme.space2 | ||
| 156 | |||
| 157 | Label { | ||
| 153 | text: "任务队列" | 158 | text: "任务队列" |
| 154 | font.family: App.Theme.fontFamily | 159 | font.family: App.Theme.fontFamily |
| 155 | font.pointSize: App.Theme.fontBase | 160 | font.pointSize: App.Theme.fontBase |
| 156 | font.weight: Font.DemiBold | 161 | font.weight: Font.DemiBold |
| 157 | color: App.Theme.textPrimary | 162 | color: App.Theme.textPrimary |
| 158 | } | 163 | } |
| 164 | Item { Layout.fillWidth: true } | ||
| 165 | Label { | ||
| 166 | visible: taskQueue.runningCount + taskQueue.pendingCount > 0 | ||
| 167 | text: taskQueue.runningCount + taskQueue.pendingCount + " 进行中" | ||
| 168 | font.family: App.Theme.fontFamily | ||
| 169 | font.pointSize: App.Theme.fontXs | ||
| 170 | color: App.Theme.textSecondary | ||
| 171 | } | ||
| 172 | } | ||
| 159 | 173 | ||
| 160 | // 底部分隔线 | 174 | // 底部分隔线 |
| 161 | Rectangle { | 175 | Rectangle { |
| ... | @@ -169,32 +183,95 @@ Rectangle { | ... | @@ -169,32 +183,95 @@ Rectangle { |
| 169 | 183 | ||
| 170 | // 列表 | 184 | // 列表 |
| 171 | ListView { | 185 | ListView { |
| 186 | id: taskList | ||
| 172 | Layout.fillWidth: true | 187 | Layout.fillWidth: true |
| 173 | Layout.fillHeight: true | 188 | Layout.fillHeight: true |
| 174 | Layout.margins: App.Theme.space2 | 189 | Layout.margins: App.Theme.space2 |
| 175 | spacing: 4 | 190 | spacing: 6 |
| 176 | clip: true | 191 | clip: true |
| 177 | 192 | ||
| 178 | model: ListModel { | 193 | model: taskQueue.model |
| 179 | ListElement { status: "执行中"; color: "#ff9500" } | 194 | |
| 180 | ListElement { status: "等待中"; color: "#0071e3" } | 195 | // 空状态占位 |
| 181 | ListElement { status: "已完成"; color: "#34c759" } | 196 | Label { |
| 197 | anchors.centerIn: parent | ||
| 198 | text: "暂无任务" | ||
| 199 | font.family: App.Theme.fontFamily | ||
| 200 | font.pointSize: App.Theme.fontSm | ||
| 201 | color: App.Theme.textTertiary | ||
| 202 | visible: taskList.count === 0 | ||
| 182 | } | 203 | } |
| 183 | 204 | ||
| 184 | delegate: Rectangle { | 205 | delegate: Rectangle { |
| 206 | required property string taskId | ||
| 207 | required property string prompt | ||
| 208 | required property string status | ||
| 209 | required property real progress | ||
| 210 | required property string statusText | ||
| 211 | required property string elapsed | ||
| 212 | |||
| 185 | width: ListView.view.width | 213 | width: ListView.view.width |
| 186 | height: 36 | 214 | height: 56 |
| 187 | radius: App.Theme.radiusSm | 215 | radius: App.Theme.radiusSm |
| 188 | color: itemMouse.containsMouse ? App.Theme.bgHover : "transparent" | 216 | color: itemMouse.containsMouse ? App.Theme.bgHover : "transparent" |
| 217 | border.width: 1 | ||
| 218 | border.color: App.Theme.divider | ||
| 189 | 219 | ||
| 220 | ColumnLayout { | ||
| 221 | anchors.fill: parent | ||
| 222 | anchors.margins: App.Theme.space2 | ||
| 223 | spacing: 2 | ||
| 224 | |||
| 225 | // 第一行: prompt 摘要(最多约 14 字) | ||
| 190 | Text { | 226 | Text { |
| 191 | anchors.left: parent.left | 227 | text: prompt.length > 14 ? prompt.substring(0, 14) + "…" : prompt |
| 192 | anchors.leftMargin: App.Theme.space3 | 228 | color: App.Theme.textPrimary |
| 193 | anchors.verticalCenter: parent.verticalCenter | ||
| 194 | text: status | ||
| 195 | color: model.color | ||
| 196 | font.family: App.Theme.fontFamily | 229 | font.family: App.Theme.fontFamily |
| 197 | font.pointSize: App.Theme.fontSm | 230 | font.pointSize: App.Theme.fontXs |
| 231 | Layout.fillWidth: true | ||
| 232 | elide: Text.ElideRight | ||
| 233 | } | ||
| 234 | |||
| 235 | // 第二行: 状态彩色 + elapsed | ||
| 236 | RowLayout { | ||
| 237 | Layout.fillWidth: true | ||
| 238 | spacing: 4 | ||
| 239 | |||
| 240 | Text { | ||
| 241 | text: statusText | ||
| 242 | color: status === "completed" ? App.Theme.success | ||
| 243 | : status === "failed" ? App.Theme.danger | ||
| 244 | : status === "running" ? App.Theme.accent | ||
| 245 | : App.Theme.warning // pending | ||
| 246 | font.family: App.Theme.fontFamily | ||
| 247 | font.pointSize: App.Theme.fontXs | ||
| 248 | Layout.fillWidth: true | ||
| 249 | elide: Text.ElideRight | ||
| 250 | } | ||
| 251 | Text { | ||
| 252 | visible: elapsed.length > 0 | ||
| 253 | text: elapsed | ||
| 254 | color: App.Theme.textTertiary | ||
| 255 | font.family: App.Theme.fontFamily | ||
| 256 | font.pointSize: App.Theme.fontXs | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | // 第三行: running 时显示细进度条 | ||
| 261 | Rectangle { | ||
| 262 | visible: status === "running" | ||
| 263 | Layout.fillWidth: true | ||
| 264 | Layout.preferredHeight: 2 | ||
| 265 | color: App.Theme.bgSubtle | ||
| 266 | radius: 1 | ||
| 267 | |||
| 268 | Rectangle { | ||
| 269 | width: parent.width * Math.max(0.05, Math.min(progress, 1.0)) | ||
| 270 | height: parent.height | ||
| 271 | color: App.Theme.accent | ||
| 272 | radius: 1 | ||
| 273 | } | ||
| 274 | } | ||
| 198 | } | 275 | } |
| 199 | 276 | ||
| 200 | MouseArea { | 277 | MouseArea { |
| ... | @@ -202,6 +279,13 @@ Rectangle { | ... | @@ -202,6 +279,13 @@ Rectangle { |
| 202 | anchors.fill: parent | 279 | anchors.fill: parent |
| 203 | hoverEnabled: true | 280 | hoverEnabled: true |
| 204 | cursorShape: Qt.PointingHandCursor | 281 | cursorShape: Qt.PointingHandCursor |
| 282 | acceptedButtons: Qt.LeftButton | Qt.RightButton | ||
| 283 | onClicked: function(mouse) { | ||
| 284 | if (mouse.button === Qt.RightButton | ||
| 285 | && (status === "pending" || status === "running")) { | ||
| 286 | taskQueue.cancelTask(taskId) | ||
| 287 | } | ||
| 288 | } | ||
| 205 | } | 289 | } |
| 206 | } | 290 | } |
| 207 | } | 291 | } | ... | ... |
-
Please register or sign in to post a comment