Skip to content

Commit 1e0d74e

Browse files
feat: 优化任务查看页面功能
- 添加 URL 参数支持,刷新页面时保持选中任务状态 - 实现侧边栏宽窄模式切换(320px/160px) - 优化任务列表样式:去边框、改图标、调整间隔 - 窄模式只显示任务名称,宽模式显示状态、名称、时间 - pending 和 processing 状态统一使用加载图标 - 添加定时刷新避免频繁重新渲染,解决卡顿问题 - 项目启动 AI 后跳转到新的任务查看页面 - 窄模式按钮文字简化为'新任务' Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
1 parent 1f64999 commit 1e0d74e

File tree

2 files changed

+62
-27
lines changed

2 files changed

+62
-27
lines changed

frontend/src/components/console/project/start-develop-task-dialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default function StartDevelopTaskDialog({
4646
if (resp.code === 0) {
4747
toast.success('对话任务已启动')
4848
onOpenChange(false)
49-
window.open(`/console/task/develop/${resp.data?.id}`, "_blank")
49+
window.open(`/console/task/view?taskId=${resp.data?.id}`, "_blank")
5050
} else {
5151
toast.error(resp.message || '任务启动失败')
5252
}

frontend/src/pages/console/user/task/task-view.tsx

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
import { type DomainProjectTask } from "@/api/Api"
2-
import { Badge } from "@/components/ui/badge"
32
import { Button } from "@/components/ui/button"
43
import { ScrollArea } from "@/components/ui/scroll-area"
54
import { Spinner } from "@/components/ui/spinner"
65
import { cn } from "@/lib/utils"
76
import { stripMarkdown } from "@/utils/common"
87
import { apiRequest } from "@/utils/requestUtils"
9-
import { IconPlus } from "@tabler/icons-react"
8+
import { IconPlus, IconCircleCheck, IconAlertTriangle, IconLoader, IconLayoutSidebar } from "@tabler/icons-react"
109
import dayjs from "dayjs"
10+
import relativeTime from "dayjs/plugin/relativeTime"
1111
import { useCallback, useEffect, useState } from "react"
12+
import { useSearchParams } from "react-router-dom"
1213
import { toast } from "sonner"
1314

15+
dayjs.extend(relativeTime)
16+
1417
const PAGE_SIZE = 50
1518

1619
export default function TaskViewPage() {
20+
const [searchParams, setSearchParams] = useSearchParams()
1721
const [tasks, setTasks] = useState<DomainProjectTask[]>([])
1822
const [loading, setLoading] = useState(false)
19-
const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null)
23+
const [selectedTaskId, setSelectedTaskId] = useState<string | null>(searchParams.get('taskId') || null)
2024
const [selectedTask, setSelectedTask] = useState<DomainProjectTask | null>(null)
25+
const [sidebarWidth, setSidebarWidth] = useState<'wide' | 'narrow'>('wide')
26+
const [, update] = useState(0)
27+
28+
useEffect(() => {
29+
const interval = setInterval(() => {
30+
update(v => v + 1)
31+
}, 30000)
32+
return () => clearInterval(interval)
33+
}, [])
2134

2235
const fetchTasks = useCallback(async () => {
2336
setLoading(true)
@@ -29,13 +42,19 @@ export default function TaskViewPage() {
2942
const firstTask = fetchedTasks[0]
3043
setSelectedTaskId(firstTask.id)
3144
setSelectedTask(firstTask)
45+
setSearchParams({ taskId: firstTask.id }, { replace: true })
46+
} else if (selectedTaskId) {
47+
const foundTask = fetchedTasks.find(t => t.id === selectedTaskId)
48+
if (foundTask) {
49+
setSelectedTask(foundTask)
50+
}
3251
}
3352
} else {
3453
toast.error("获取任务列表失败: " + resp.message)
3554
}
3655
})
3756
setLoading(false)
38-
}, [selectedTaskId])
57+
}, [selectedTaskId, setSearchParams])
3958

4059
useEffect(() => {
4160
fetchTasks()
@@ -45,20 +64,28 @@ export default function TaskViewPage() {
4564

4665
return (
4766
<div className="flex h-screen overflow-hidden">
48-
<div className="w-80 border-r flex flex-col h-full bg-background overflow-hidden">
49-
<div className="p-2">
67+
<div className={cn("border-r flex flex-col h-full bg-muted/30 overflow-hidden transition-all duration-300", sidebarWidth === 'wide' ? 'w-80' : 'w-40')}>
68+
<div className="flex p-2 gap-2">
5069
<Button
5170
variant="outline"
5271
size="sm"
53-
className="w-full"
72+
className="flex-1"
5473
onClick={() => window.location.href = '/console/tasks'}
5574
>
56-
<IconPlus className="w-4 h-4 mr-2" />
57-
启动新任务
75+
<IconPlus className="w-4 h-4" />
76+
{sidebarWidth === 'wide' ? '启动新任务' : '新任务'}
77+
</Button>
78+
<Button
79+
variant="outline"
80+
size="sm"
81+
className="flex-shrink-0"
82+
onClick={() => setSidebarWidth(sidebarWidth === 'wide' ? 'narrow' : 'wide')}
83+
>
84+
<IconLayoutSidebar className="w-4 h-4" />
5885
</Button>
5986
</div>
6087
<ScrollArea className="flex-1 h-0">
61-
<div className="p-2 space-y-2">
88+
<div className="p-2 space-y-1">
6289
{loading && tasks.length === 0 ? (
6390
<div className="flex justify-center py-8">
6491
<Spinner className="size-6" />
@@ -67,32 +94,40 @@ export default function TaskViewPage() {
6794
<div className="text-center py-8 text-muted-foreground text-sm">
6895
暂无任务
6996
</div>
70-
) : (
97+
) : (
7198
tasks.map((task) => (
7299
<div
73100
key={task.id}
74101
className={cn(
75-
"p-3 rounded-lg border cursor-pointer transition-all hover:bg-accent",
76-
selectedTaskId === task.id && "bg-accent border-primary"
102+
"cursor-pointer transition-all hover:bg-accent rounded",
103+
selectedTaskId === task.id && "bg-accent"
77104
)}
78105
onClick={() => {
79106
setSelectedTaskId(task.id)
80107
setSelectedTask(task)
108+
setSearchParams({ taskId: task.id })
81109
}}
82110
>
83-
<div className="font-medium text-sm line-clamp-1 break-all mb-2">
84-
{task.summary || stripMarkdown(task.content)}
85-
</div>
86-
<div className="flex items-center justify-between">
87-
<Badge variant="outline" className="text-xs">
88-
{task.status === "finished" && "已完成"}
89-
{task.status === "error" && "失败"}
90-
{task.status === "pending" && "等待中"}
91-
{task.status === "processing" && "执行中"}
92-
</Badge>
93-
<div className="text-xs text-muted-foreground">
94-
{dayjs.unix(task.created_at as number).fromNow()}
95-
</div>
111+
<div className={cn(sidebarWidth === 'wide' ? "px-3 py-2" : "px-2 py-2")}>
112+
{sidebarWidth === 'wide' ? (
113+
<div className="flex items-center gap-2 text-sm">
114+
<div className="flex-shrink-0">
115+
{task.status === "finished" && <IconCircleCheck className="w-4 h-4 text-muted-foreground/50" />}
116+
{task.status === "error" && <IconAlertTriangle className="w-4 h-4 text-muted-foreground/50" />}
117+
{(task.status === "pending" || task.status === "processing") && <IconLoader className="w-4 h-4 text-muted-foreground animate-spin" style={{ animationDuration: '3s' }} />}
118+
</div>
119+
<div className="flex-1 min-w-0 line-clamp-1">
120+
{task.summary || stripMarkdown(task.content)}
121+
</div>
122+
<div className="flex-shrink-0 text-xs text-muted-foreground">
123+
{dayjs.unix(task.created_at as number).fromNow()}
124+
</div>
125+
</div>
126+
) : (
127+
<div className="text-sm line-clamp-1">
128+
{task.summary || stripMarkdown(task.content)}
129+
</div>
130+
)}
96131
</div>
97132
</div>
98133
))

0 commit comments

Comments
 (0)