diff --git a/apps/desktop/design-qa.md b/apps/desktop/design-qa.md index 73690a2..252568c 100644 --- a/apps/desktop/design-qa.md +++ b/apps/desktop/design-qa.md @@ -7,11 +7,12 @@ - Viewport: 420 × 820 CSS px at device scale factor 1; implementation image is 420 × 820 px. - Source image: 144 × 343 px and only shows the compact icon-navigation language. It does not define the requested all-projects dashboard. - State: signed-in light theme, “工作台 / 全部项目” selected, one pending task and one latest file. The content header is “全部项目” with its creation action aligned on the right. -- Primary interactions tested: default all-projects homepage, task editor modal, explore view, project picker, project-only right navigation, document panel, AI panel, and profile dropdown. Console errors: none. +- Primary interactions tested: default all-projects homepage, task editor modal, dataset-backed explore view, project picker, project-only right navigation, document panel, AI panel, and profile dropdown. Console errors: none. ## Evidence - Full current implementation: `test-results/app-home.png` +- Explore reading flow: `test-results/app-explore.png` - Earlier focused icon-rail comparison: `test-results/design-qa-rail-comparison.png` The source rail is used only for the compact icon treatment. The latest requested behavior intentionally hides that rail in the all-projects dashboard and displays it only after selecting a specific project. @@ -25,10 +26,12 @@ No actionable P0, P1, or P2 findings. - Colors and visual tokens: workbench/explore selection uses the existing blue token, while statistic colors distinguish projects, pending work, files, and plan tasks without overpowering the task list. - Image and icon fidelity: the existing product mark is retained and all controls use the installed Arco icon set; no placeholder or hand-drawn image assets were added. - Copy and content: “工作台”, “探索”, and “全部项目” make scope explicit; its homepage shows only pending tasks and latest files. The project-only menu contains 概况、计划、资料、AI、计划任务 and no longer exposes 收集箱. +- Explore parity: the desktop view now uses the `web_v1` dataset endpoints and mirrors its core source-selection, item-list, and detail-reading workflow in a narrow reading layout. With 全部项目 selected, the “沉淀到项目” action is intentionally disabled until a concrete project is selected. ## Comparison history - Current pass: removed the completed-task section and upload action from the all-projects homepage, then removed the content-level “工作台” label and moved 创建项目 beside 全部项目. Re-captured `test-results/app-home.png` and verified that only pending tasks and latest files remain. +- Explore pass: replaced the project-card placeholder with the dataset source and item reader. Captured `test-results/app-explore.png`; two sources and the selected article detail render without console errors. ## Follow-up polish @@ -41,6 +44,7 @@ No actionable P0, P1, or P2 findings. - [x] Add all-project statistics, pending tasks, project creation, and latest files. - [x] Hide right navigation for all-projects and explore views. - [x] Remove 收集箱 and rename 周期计划 to 计划任务. +- [x] Align top-level 探索 with the `web_v1` dataset reading flow. - [x] Verify key interaction paths and responsive composition. final result: passed diff --git a/apps/desktop/scripts/visual-check.mjs b/apps/desktop/scripts/visual-check.mjs index 7611384..b6f42de 100644 --- a/apps/desktop/scripts/visual-check.mjs +++ b/apps/desktop/scripts/visual-check.mjs @@ -23,6 +23,8 @@ const workspace = { cronPlans: [{ id: 'cron-1', projectId, title: '每周复盘', schedule: '0 17 * * 5', nextRun: null, enabled: true, lastResult: '', owner: '张明' }], } const expert = { id: 'expert-1', slug: 'product-manager', category: 'product', categoryName: '产品', name: '产品经理', description: '负责需求分析', emoji: '📘', color: '#165DFF' } +const datasetSource = { id: 'source-1', name: '行业资讯', kind: 'rss', url: 'https://example.com/feed', iconUrl: '', description: '行业动态', enabled: true, builtIn: false, lastSyncedAt: null, itemCount: 1, createdAt: '2026-07-22T10:00:00Z', updatedAt: '2026-07-22T10:00:00Z' } +const datasetItem = { id: 'item-1', sourceId: 'source-1', title: 'AI 工作台趋势', summary: '新的协作方式正在改变项目管理。', content: '这是来自数据源的探索内容,可在 Web 工作台继续管理数据源和同步任务。', url: '', imageUrl: '', status: 'unread', starred: false, publishedAt: '2026-07-22T10:00:00Z', createdAt: '2026-07-22T10:00:00Z', updatedAt: '2026-07-22T10:00:00Z' } await page.route('http://localhost:9150/api/v1/**', async (route) => { const request = route.request(); const path = new URL(request.url()).pathname @@ -31,6 +33,8 @@ await page.route('http://localhost:9150/api/v1/**', async (route) => { if (path === '/api/v1/projects') return route.fulfill({ json: [workspace.project] }) if (path === `/api/v1/projects/${projectId}/workspace`) return route.fulfill({ json: workspace }) if (path === '/api/v1/ai-experts') return route.fulfill({ json: [expert] }) + if (path === '/api/v1/dataset-sources') return route.fulfill({ json: [datasetSource] }) + if (path === '/api/v1/dataset-items') return route.fulfill({ json: { items: [datasetItem], total: 1, limit: 50, offset: 0 } }) if (path.includes('/tasks/') && request.method() === 'PATCH') return route.fulfill({ json: {} }) return route.fulfill({ status: 404, json: { error: { code: 'not_found', message: '未配置的检查接口' } } }) }) @@ -52,7 +56,10 @@ await page.locator('.task-row').first().click() const taskModal = await page.getByText('编辑任务').count() await page.getByRole('button', { name: '取消' }).click() await page.getByRole('button', { name: '探索' }).click() -const exploreProjects = await page.locator('.project-explore-card').count() +await page.waitForSelector('.explore-detail') +const exploreSources = await page.locator('.explore-source-strip button').count() +const exploreDetail = await page.locator('.explore-detail').count() +await page.screenshot({ path: 'test-results/app-explore.png', fullPage: true }) await page.locator('.project-current').click() await page.getByRole('option', { name: '森林项目' }).click() await page.waitForSelector('.channel-rail') @@ -74,10 +81,10 @@ if (home.navCount !== 0) failures.push(`all-projects view must hide the right ra if (home.statCount !== 4) failures.push(`expected four summary cards, got ${home.statCount}`) if (home.pending !== 1 || home.completed !== 0) failures.push(`all-project task sections incorrect: ${JSON.stringify(home)}`) if (taskModal !== 1) failures.push('task editor did not open') -if (exploreProjects !== 1) failures.push(`expected one project in explore, got ${exploreProjects}`) +if (exploreSources !== 2 || exploreDetail !== 1) failures.push(`explore reader did not render: ${JSON.stringify({ exploreSources, exploreDetail })}`) if (documents !== 1) failures.push(`expected one document, got ${documents}`) if (experts !== 1) failures.push(`expected one AI expert, got ${experts}`) if (profileMenu !== 1) failures.push('profile menu did not open') if (errors.length) failures.push(`console errors: ${errors.join('; ')}`) -console.log(JSON.stringify({ home, documents, experts, taskModal, exploreProjects, profileMenu, errors }, null, 2)) +console.log(JSON.stringify({ home, documents, experts, taskModal, exploreSources, exploreDetail, profileMenu, errors }, null, 2)) if (failures.length) throw new Error(failures.join('\n')) diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index f5f3944..2dd2593 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -1,14 +1,14 @@ import { useEffect, useMemo, useState } from 'react' import { Alert, Avatar, Button, Dropdown, Empty, Form, Input, Menu, Modal, Select, Spin, Switch, Tag, Typography } from '@arco-design/web-react' import { - IconApps, IconCheckCircle, IconCheckCircleFill, IconClockCircle, IconCompass, IconDown, IconFile, - IconFolder, IconLeft, IconList, IconMoon, IconPlus, IconRight, IconRobot, + IconApps, IconBook, IconCheckCircle, IconCheckCircleFill, IconClockCircle, IconCompass, IconDown, IconFile, + IconFolder, IconLeft, IconList, IconMoon, IconPlus, IconRefresh, IconRight, IconRobot, IconSun, IconUser, } from '@arco-design/web-react/icon' import { ApiError, createProject, fetchProjectWorkspace, fetchProjects, getCurrentUser, - listAIExperts, login, setApiBaseUrl, updateCurrentUser, updateTask, - type ApiSession, type CurrentUser, type Expert, type Project, type Workspace, type WorkspaceTask, + listAIExperts, listDatasetItems, listDatasetSources, login, setApiBaseUrl, updateCurrentUser, updateTask, + type ApiSession, type CurrentUser, type DatasetItem, type DatasetSource, type Expert, type Project, type Workspace, type WorkspaceTask, } from './api' const { Text, Title } = Typography @@ -113,7 +113,7 @@ export function App() { {error ? setError('')} /> : null} -
{workspaceMode === 'explore' ? { setProjectId(id); setWorkspaceMode('workbench') }} /> : !projectId ? setProjectModalOpen(true)} /> : visibleWorkspaces.length ? : }
+
{workspaceMode === 'explore' ? : !projectId ? setProjectModalOpen(true)} /> : visibleWorkspaces.length ? : }
{projectId && workspaceMode === 'workbench' ?