feat(web): refine AI sessions and task filtering

This commit is contained in:
2026-07-22 16:26:05 +08:00
parent 5d2f45ad83
commit b28a5d635c
9 changed files with 484 additions and 122 deletions

View File

@@ -37,11 +37,14 @@ const visualCheckWorkspace = {
{ id: 'overview', projectId, type: 'overview', title: '概况', icon: 'home', count: 0, url: '', sortOrder: 0 },
{ id: 'inbox', projectId, type: 'inbox', title: 'Inbox 消息流', icon: 'email', count: 2, url: '', sortOrder: 1 },
{ id: 'tasks', projectId, type: 'tasks', title: '工作计划', icon: 'list', count: 0, url: '', sortOrder: 1 },
{ id: 'ai', projectId, type: 'ai_sessions', title: 'AI 话', icon: 'robot', count: 0, url: '', sortOrder: 2 },
{ id: 'ai', projectId, type: 'ai_sessions', title: 'AI 话', icon: 'robot', count: 0, url: '', sortOrder: 2 },
{ id: 'notes', projectId, type: 'notes_sources', title: '笔记资料', icon: 'file', count: 0, url: '', sortOrder: 3 },
{ id: 'cron', projectId, type: 'cron', title: '计划任务', icon: 'clock', count: 0, url: '', sortOrder: 4 },
],
tags: [{ id: '019b0000-0000-7000-8000-000000000002', name: '产品' }],
tags: [
{ id: '019b0000-0000-7000-8000-000000000002', name: '产品' },
{ id: '019b0000-0000-7000-8000-000000000005', name: '设计' },
],
recentSessions: [],
inbox: [
{
@@ -65,7 +68,50 @@ const visualCheckWorkspace = {
time: '2026-07-22T07:00:00Z',
},
],
tasks: [],
tasks: [
{
id: '019b0000-0000-7000-8000-000000000006',
projectId,
title: '梳理产品需求',
summary: '确认首版工作台的功能边界。',
completed: false,
owner: '森林AI',
createdAt: '2026-07-22',
completedAt: '',
duration: '',
progress: '40%',
tagId: '019b0000-0000-7000-8000-000000000002',
tag: '产品',
},
{
id: '019b0000-0000-7000-8000-000000000007',
projectId,
title: '优化会话界面',
summary: '统一 AI 会话的阅读与输入体验。',
completed: false,
owner: '森林AI',
createdAt: '2026-07-22',
completedAt: '',
duration: '',
progress: '60%',
tagId: '019b0000-0000-7000-8000-000000000005',
tag: '设计',
},
{
id: '019b0000-0000-7000-8000-000000000008',
projectId,
title: '确认项目定位',
summary: '完成项目功能与亮点说明。',
completed: true,
owner: '森林AI',
createdAt: '2026-07-21',
completedAt: '2026-07-22',
duration: '1 天',
progress: '100%',
tagId: '019b0000-0000-7000-8000-000000000002',
tag: '产品',
},
],
aiSessions: [],
notesSources: [],
cronPlans: [],
@@ -90,7 +136,32 @@ await page.route('http://localhost:9150/api/v1/**', async (route) => {
return
}
if (url.pathname === `/api/v1/projects/${projectId}/ai-sessions`) {
await route.fulfill({ json: [] })
if (route.request().method() === 'POST') {
const input = route.request().postDataJSON()
await route.fulfill({
json: {
id: '019b0000-0000-7000-8000-000000000010',
projectId,
title: input.title,
context: input.context,
status: 'ready',
createdAt: '2026-07-22T09:00:00Z',
updatedAt: '2026-07-22T09:00:00Z',
},
})
return
}
await route.fulfill({
json: [{
id: '019b0000-0000-7000-8000-000000000009',
projectId,
title: '报价分析',
context: '分析当前方案的报价结构。',
status: 'ready',
createdAt: '2026-07-22T08:30:00Z',
updatedAt: '2026-07-22T08:30:00Z',
}],
})
return
}
await route.fulfill({ status: 404, json: { error: { code: 'not_found', message: '视觉检查未配置该接口' } } })
@@ -238,16 +309,18 @@ await page.locator('.channel-list').hover()
const channelListHoverMetrics = await collectMetrics()
const channelPageChecks = []
let taskTagFilterCheck = null
let aiSessionCheck = null
for (const channel of [
{ label: '工作计划', pageClass: 'project-tasks-page', expectedHeading: '工作计划' },
{ label: 'AI 话', pageClass: 'project-ai-page', expectedHeading: 'AI 对话' },
{ label: 'AI 话', pageClass: 'project-ai-page', expectedHeading: '' },
{ label: '笔记资料', pageClass: 'project-notes-page', expectedHeading: '笔记资料' },
{ label: '计划任务', pageClass: 'project-cron-page', expectedHeading: '计划任务' },
{ label: '新建频道', pageClass: 'project-new-channel-page', expectedHeading: '新建频道' },
]) {
await page.locator('.channel-button', { hasText: channel.label }).click()
await page.waitForTimeout(250)
channelPageChecks.push(await page.evaluate((expected) => {
const pageCheck = await page.evaluate((expected) => {
const pageNode = document.querySelector(`.${expected.pageClass}`)
const heading = pageNode?.querySelector('h4, h5')?.textContent ?? ''
const activeChannel = document.querySelector('.channel-button.active')?.textContent ?? ''
@@ -258,8 +331,37 @@ for (const channel of [
activeChannel,
inputCount: pageNode?.querySelectorAll('input, textarea').length ?? 0,
hasSaveChannel: [...(pageNode?.querySelectorAll('button') ?? [])].some((button) => button.textContent?.includes('保存频道')),
hasOverviewHead: Boolean(pageNode?.querySelector('.overview-head')),
hasSessionList: Boolean(pageNode?.querySelector('.agent-session-list')),
hasComposer: Boolean(pageNode?.querySelector('.agent-composer')),
}
}, channel))
}, channel)
if (channel.label === '工作计划') {
await page.getByRole('button', { name: '设计', exact: true }).click()
await page.waitForTimeout(150)
taskTagFilterCheck = await page.evaluate(() => ({
activeTag: document.querySelector('.task-tag-filter.active')?.textContent?.trim() ?? '',
pendingTitles: [...document.querySelectorAll('.task-plan-title')].map((node) => node.textContent?.trim()),
completedTitles: [...document.querySelectorAll('.completed-plan-title')].map((node) => node.textContent?.trim()),
pressed: document.querySelector('.task-tag-filter[aria-pressed="true"]')?.textContent?.trim() ?? '',
}))
await page.screenshot({ path: 'test-results/project-task-tag-filter-light.png', fullPage: true })
}
if (channel.label === 'AI 会话') {
await page.locator('.agent-composer textarea').fill('请分析这个项目的下一步计划')
await page.getByRole('button', { name: '发送消息' }).click()
await page.waitForSelector('.agent-user-message')
aiSessionCheck = await page.evaluate(() => ({
userMessage: document.querySelector('.agent-user-message')?.textContent?.trim() ?? '',
activeSession: document.querySelector('.agent-session.active')?.textContent?.trim() ?? '',
composerValue: document.querySelector('.agent-composer textarea')?.value ?? '',
}))
await page.screenshot({ path: 'test-results/project-ai-session-light.png', fullPage: true })
}
channelPageChecks.push(pageCheck)
}
await page.locator('.topbar-actions .arco-btn').first().click()
@@ -280,6 +382,8 @@ console.log(JSON.stringify({
stageHoverMetrics,
channelListHoverMetrics,
channelPageChecks,
taskTagFilterCheck,
aiSessionCheck,
errors,
}, null, 2))
@@ -439,7 +543,7 @@ for (const check of channelPageChecks) {
if (!check.foundPage) {
failures.push(`expected ${check.label} to render .${check.pageClass}`)
}
if (!check.heading.includes(check.expectedHeading)) {
if (check.expectedHeading && !check.heading.includes(check.expectedHeading)) {
failures.push(`expected ${check.label} heading to include ${check.expectedHeading}, got ${JSON.stringify(check.heading)}`)
}
if (!check.activeChannel.includes(check.label)) {
@@ -448,6 +552,27 @@ for (const check of channelPageChecks) {
if (check.label === '新建频道' && (check.inputCount < 4 || !check.hasSaveChannel)) {
failures.push(`expected legacy new channel editor, got inputs=${check.inputCount}, save=${check.hasSaveChannel}`)
}
if (check.label === 'AI 会话' && (check.hasOverviewHead || !check.hasSessionList || !check.hasComposer)) {
failures.push(`expected headerless AI session chat layout, got ${JSON.stringify(check)}`)
}
}
if (
!taskTagFilterCheck ||
taskTagFilterCheck.activeTag !== '设计' ||
taskTagFilterCheck.pressed !== '设计' ||
taskTagFilterCheck.pendingTitles.join('|') !== '优化会话界面' ||
taskTagFilterCheck.completedTitles.length !== 0
) {
failures.push(`expected clickable task tags to filter pending and completed plans, got ${JSON.stringify(taskTagFilterCheck)}`)
}
if (
!aiSessionCheck ||
aiSessionCheck.userMessage !== '请分析这个项目的下一步计划' ||
!aiSessionCheck.activeSession.includes('请分析这个项目的下一步计划') ||
aiSessionCheck.composerValue !== ''
) {
failures.push(`expected AI composer to create and select a session, got ${JSON.stringify(aiSessionCheck)}`)
}
if (failures.length) {