From 861aad56f401d13aa8400d17f21681f88077a133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Tue, 29 Sep 2026 17:52:55 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96:=20=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E8=BF=9B=E5=BA=A6=E6=94=B9=E4=B8=BA=E7=9C=9F=E5=AE=9E=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=AE=8C=E6=88=90=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 进度条不再用阶段写死百分比,改为已完成任务数/总任务数 - 复用现有任务计数通道,零后端改动;无任务项目显示无任务不伪造数字 - 阶段映射表仅保留颜色文案用途 --- .../dashboard/ActiveProjectsPanel.vue | 15 ++++++++--- src/constants/project.ts | 25 ++++++++++--------- src/i18n/en/dashboard.ts | 1 + src/i18n/zh-CN/dashboard.ts | 1 + src/stores/project.ts | 13 ++++++++-- src/stores/project/state.ts | 2 ++ 6 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/components/dashboard/ActiveProjectsPanel.vue b/src/components/dashboard/ActiveProjectsPanel.vue index e74dbd3..a26889a 100644 --- a/src/components/dashboard/ActiveProjectsPanel.vue +++ b/src/components/dashboard/ActiveProjectsPanel.vue @@ -15,9 +15,10 @@
-
+
- {{ p.progress }}% + {{ p.progress }}% + {{ $t('dashboard.noTasks') }}
@@ -54,10 +55,18 @@ function goDetail(projectId: string) { // P1-b A0:统计走后端 count_tasks 独立通道,不再 filter(store.tasks)。 // store.tasks 是 Tasks 视图当前筛选+分页数据,filter 会让面板数字随 Tasks 翻页/筛选错乱。 // store.projectTaskCounts[projectId] = 后端该项目 in_progress 任务数(loadStats 填充)。 +// store.projectTaskProgress[projectId] = 该项目 { done, total } 任务数(loadStats 填充), +// 进度条按真实完成率渲染;无任务(total=0)时不伪造百分比,显示「无任务」。 function getProjectTaskCount(projectId: string): number { return store.projectTaskCounts[projectId] ?? 0 } +function getProjectProgress(projectId: string): number | null { + const prog = store.projectTaskProgress[projectId] + if (!prog || prog.total === 0) return null + return Math.round((prog.done / prog.total) * 100) +} + // 阶段 label key:projectStageInfo 返回的 stage 值(coding/testing/release/planning/paused/cancelled) // 直接拼接 i18n key dashboard.stage.,与 constants/project.ts PROJECT_STAGE_INFO 单一来源。 // 面板名为"活跃项目",按真实生命周期过滤:排除已完结/已取消(后端状态机无 'active',原 @@ -79,7 +88,7 @@ const displayProjects = computed(() => name: p.name, stage: info.stage, stageLabelKey: info.stage, - progress: info.progress, + progress: getProjectProgress(p.id), activeTasks: getProjectTaskCount(p.id), lastActivity: formatRelative(p.last_active_at ?? p.updated_at), } diff --git a/src/constants/project.ts b/src/constants/project.ts index b3cfbd0..dbc08b5 100644 --- a/src/constants/project.ts +++ b/src/constants/project.ts @@ -1,7 +1,8 @@ //! 项目 & 任务状态映射常量 //! -//! 统一各视图(Projects/ProjectDetail/Tasks/Dashboard)的 label / badge / 阶段进度, +//! 统一各视图(Projects/ProjectDetail/Tasks/Dashboard)的 label / badge / 阶段颜色档, //! 根治此前三套状态映射互相矛盾、任务文案两处不一致的问题。 +//! 项目进度不在此处(按 stage 拍百分比已废弃),由任务完成率计算,见 store.projectTaskProgress。 // ── 项目状态 ── @@ -36,12 +37,10 @@ export const PROJECT_STATUS_BADGE_CLASS: Record = { cancelled: 'stage-dim', } -/** 项目状态 → 阶段/进度信息(详情页 pipeline + Dashboard 进度条共用) */ +/** 项目状态 → 阶段信息(Dashboard 阶段 dot/chip/fill 颜色档与文案共用) */ export interface ProjectStageInfo { /** pipeline 步骤索引 0-4: idea/requirement/coding/testing/release */ stepIndex: number - /** 进度百分比 */ - progress: number /** Dashboard dot/chip 颜色档: coding/testing/release */ stage: string } @@ -49,14 +48,16 @@ export interface ProjectStageInfo { // 对齐 7 态。stage 值决定 Dashboard dot/chip/fill 颜色档(planning/coding/testing/release/paused/cancelled), // 需与 ActiveProjectsPanel.vue 的 dot-*/chip-*/fill-* 类一一对应(paused 为该组件新增 dim 类)。 // 'active' 键已删,老数据兜底走 projectStageInfo 默认 planning。 +// 进度百分比已移出本表:进度改由任务完成率计算(store.projectTaskProgress), +// stage 枚举与任务完成度无必然对应,固定映射会给出与实际不符的数字。 export const PROJECT_STAGE_INFO: Record = { - planning: { stepIndex: 0, progress: 20, stage: 'planning' }, - in_progress: { stepIndex: 2, progress: 55, stage: 'coding' }, - testing: { stepIndex: 3, progress: 80, stage: 'testing' }, - releasing: { stepIndex: 4, progress: 95, stage: 'release' }, - completed: { stepIndex: 4, progress: 100, stage: 'release' }, - paused: { stepIndex: 1, progress: 40, stage: 'paused' }, - cancelled: { stepIndex: 0, progress: 0, stage: 'cancelled' }, + planning: { stepIndex: 0, stage: 'planning' }, + in_progress: { stepIndex: 2, stage: 'coding' }, + testing: { stepIndex: 3, stage: 'testing' }, + releasing: { stepIndex: 4, stage: 'release' }, + completed: { stepIndex: 4, stage: 'release' }, + paused: { stepIndex: 1, stage: 'paused' }, + cancelled: { stepIndex: 0, stage: 'cancelled' }, } export function projectStatusLabel(status: string): string { @@ -69,7 +70,7 @@ export function projectBadgeClass(status: string): string { export function projectStageInfo(status: string): ProjectStageInfo { // 兜底用字面量而非 PROJECT_STAGE_INFO.planning,避免已删键的隐式依赖 - return PROJECT_STAGE_INFO[status] ?? { stepIndex: 0, progress: 0, stage: 'planning' } + return PROJECT_STAGE_INFO[status] ?? { stepIndex: 0, stage: 'planning' } } // ── 任务状态 ── diff --git a/src/i18n/en/dashboard.ts b/src/i18n/en/dashboard.ts index 9c8bce6..178932a 100644 --- a/src/i18n/en/dashboard.ts +++ b/src/i18n/en/dashboard.ts @@ -21,6 +21,7 @@ export default { aiBatchProcess: 'AI Batch Process', tasks: '{count} tasks', taskUnit: '{n} tasks', + noTasks: 'No tasks', trending: 'Trending', approved: 'Approved', pending: 'Pending', diff --git a/src/i18n/zh-CN/dashboard.ts b/src/i18n/zh-CN/dashboard.ts index 680a979..2f8ddd2 100644 --- a/src/i18n/zh-CN/dashboard.ts +++ b/src/i18n/zh-CN/dashboard.ts @@ -21,6 +21,7 @@ export default { aiBatchProcess: 'AI 批量处理', tasks: '{count} 个任务', taskUnit: '{n} 个任务', + noTasks: '无任务', trending: '热门', approved: '已立项', pending: '待定', diff --git a/src/stores/project.ts b/src/stores/project.ts index 8af43e7..64fe9ca 100644 --- a/src/stores/project.ts +++ b/src/stores/project.ts @@ -120,6 +120,8 @@ function createStore() { // P1-b A0:统计拉取走后端 count_tasks(独立通道),与 tasks 数组解耦。 // activeTaskCount = 全局 in_progress 任务数;projectTaskCounts = 各项目 in_progress 任务数。 + // projectTaskProgress = 各项目 { done, total } 任务数,Dashboard 进度条按真实完成率渲染, + // 复用同一 count_tasks 通道(不带 status 计 total,带 status='done' 计完成数)。 // tasks 变更(create/update/delete/advance)后由 df-data-changed listener 调用刷新。 // 注:projectTaskCounts 依赖 state.projects 已加载 —— 调用方需保证先 loadProjects。 // - Dashboard.loadAll:先 Promise.all([loadProjects, loadIdeas]) 完成再 loadStats @@ -128,9 +130,14 @@ function createStore() { try { state.activeTaskCount = await taskApi.count({ status: 'in_progress' } as TaskQuery) const entries = await Promise.all(state.projects.map(p => - taskApi.count({ project_id: p.id, status: 'in_progress' } as TaskQuery).then(n => [p.id, n] as const) + Promise.all([ + taskApi.count({ project_id: p.id, status: 'in_progress' } as TaskQuery), + taskApi.count({ project_id: p.id } as TaskQuery), + taskApi.count({ project_id: p.id, status: 'done' } as TaskQuery), + ]).then(([active, total, done]) => [p.id, active, { done, total }] as const) )) - state.projectTaskCounts = Object.fromEntries(entries) + state.projectTaskCounts = Object.fromEntries(entries.map(([id, active]) => [id, active])) + state.projectTaskProgress = Object.fromEntries(entries.map(([id, , prog]) => [id, prog])) } catch (e) { console.error('[loadStats] 统计拉取失败', e) } @@ -144,6 +151,8 @@ function createStore() { // P1-b A0:统计缓存读口(供 ActiveProjectsPanel 读 projectTaskCounts) get activeTaskCount() { return state.activeTaskCount }, get projectTaskCounts() { return state.projectTaskCounts }, + // 各项目任务完成进度(done/total)读口,Dashboard 项目进度条用 + get projectTaskProgress() { return state.projectTaskProgress }, get ideas() { return state.ideas }, get workflowExecutions() { return state.workflowExecutions }, get liveEvents() { return state.liveEvents }, diff --git a/src/stores/project/state.ts b/src/stores/project/state.ts index b44960f..6b817ae 100644 --- a/src/stores/project/state.ts +++ b/src/stores/project/state.ts @@ -20,6 +20,8 @@ export const state = reactive({ // 统计缓存,与 tasks 数组解耦,供 Dashboard,不被 Tasks 分页/筛选污染(P1-b A0) activeTaskCount: 0 as number, projectTaskCounts: {} as Record, + // 各项目任务完成进度(done/total),Dashboard 进度条完成率的数据源,与 tasks 数组解耦 + projectTaskProgress: {} as Record, ideas: [] as IdeaRecord[], workflowExecutions: [] as WorkflowRecord[], liveEvents: [] as Array,