feat(projects): 工作台小组件设计

This commit is contained in:
2026-05-28 08:20:01 +08:00
parent 3988eaf910
commit 4ed4b537ad
54 changed files with 4726 additions and 2720 deletions

View File

@@ -1,30 +1,21 @@
import dayjs from 'dayjs';
import type {
WorkbenchActivityItemSource,
WorkbenchBannerSummarySource,
WorkbenchFavoriteItemSource,
WorkbenchKpiSource,
WorkbenchMyRequirementGroupSource,
WorkbenchMyTaskItemSource,
WorkbenchMyExecutionItemSource,
WorkbenchMyWeekWorklogSource,
WorkbenchOwnedProjectItemSource,
WorkbenchProgressBarSource,
WorkbenchProjectHealthCardSource,
WorkbenchProjectItemSource,
WorkbenchTeamTodoRowSource,
WorkbenchTeamLoadSource,
WorkbenchTeamWorklogSource,
WorkbenchTodoItemSource
} from './homepage';
const now = dayjs();
const iso = (date: dayjs.Dayjs) => date.format('YYYY-MM-DD HH:mm:ss');
export const workbenchBannerSummaryMock = {
todoCount: 8,
upcomingCount: 2,
weekDone: 12,
weekTotal: 18,
weekInProgress: 5,
weekOverdue: 1
} satisfies WorkbenchBannerSummarySource;
export const workbenchKpiMock = {
todo: { count: 8, diffFromYesterday: 1 },
task: { count: 14, diffFromYesterday: -1 },
@@ -57,13 +48,13 @@ export const workbenchTodoMock = [
},
{
id: 'todo-3',
category: 'review',
title: '订单导出 V2 需求评审',
createdTime: iso(now.subtract(5, 'day').hour(14).minute(40)),
deadline: iso(now.add(3, 'day').hour(18).minute(0)),
source: '需求 · 收银台 V3',
priority: 'high',
routeKey: 'product_list'
category: 'approval',
title: '李四 · 第 21 周周报待审批',
createdTime: iso(now.subtract(2, 'day').hour(18).minute(20)),
deadline: null,
source: '周报 · 产品组',
priority: 'mid',
routeKey: 'workbench'
},
{
id: 'todo-4',
@@ -117,13 +108,13 @@ export const workbenchTodoMock = [
},
{
id: 'todo-9',
category: 'review',
title: 'API 返回结构调整评审',
category: 'approval',
title: '张三 · 5 月加班 12h 申请待审批',
createdTime: iso(now.subtract(1, 'day').hour(17).minute(0)),
deadline: iso(now.add(12, 'day').hour(18).minute(0)),
source: '需求 · 收银台 V3',
deadline: null,
source: '加班申请 · 研发组',
priority: 'mid',
routeKey: 'product_list'
routeKey: 'workbench'
},
{
id: 'todo-10',
@@ -217,6 +208,209 @@ export const workbenchActivityMock = [
}
] satisfies WorkbenchActivityItemSource[];
export const workbenchMyExecutionMock = [
// 商城 V2 升级 · 3 条(分组测试主项目)
{
id: 'exec-1',
executionName: '迭代 24.05 · 后端联调',
projectId: 'prj-mall-v2',
projectName: '商城 V2 升级',
statusCode: 'active',
statusName: '进行中',
priority: '1',
plannedStartDate: iso(now.subtract(10, 'day').startOf('day')),
plannedEndDate: iso(now.add(3, 'day').endOf('day')),
actualStartDate: iso(now.subtract(8, 'day').startOf('day')),
actualEndDate: null,
progressRate: 68,
projectRequirementId: 'req-mall-001',
projectRequirementName: '订单履约后端拆分(一期)'
},
{
id: 'exec-2',
executionName: '会员等级提示文案',
projectId: 'prj-mall-v2',
projectName: '商城 V2 升级',
statusCode: 'active',
statusName: '进行中',
priority: '3',
plannedStartDate: iso(now.subtract(4, 'day').startOf('day')),
plannedEndDate: iso(now.add(6, 'day').endOf('day')),
actualStartDate: iso(now.subtract(3, 'day').startOf('day')),
actualEndDate: null,
progressRate: 25,
projectRequirementId: 'req-mall-002',
projectRequirementName: '会员等级 UI 升级'
},
{
id: 'exec-3',
executionName: '订单退款流程拆分',
projectId: 'prj-mall-v2',
projectName: '商城 V2 升级',
statusCode: 'paused',
statusName: '已暂停',
priority: '2',
plannedStartDate: iso(now.subtract(20, 'day').startOf('day')),
plannedEndDate: iso(now.add(10, 'day').endOf('day')),
actualStartDate: iso(now.subtract(15, 'day').startOf('day')),
actualEndDate: null,
progressRate: 50
},
// 风控引擎 · 2 条(含一条计划已过期)
{
id: 'exec-4',
executionName: '关键路径优化',
projectId: 'prj-risk',
projectName: '风控引擎',
statusCode: 'active',
statusName: '进行中',
priority: '1',
plannedStartDate: iso(now.subtract(20, 'day').startOf('day')),
plannedEndDate: iso(now.subtract(2, 'day').endOf('day')),
actualStartDate: iso(now.subtract(18, 'day').startOf('day')),
actualEndDate: null,
progressRate: 42,
projectRequirementId: 'req-risk-001',
projectRequirementName: '风控决策链路压缩'
},
{
id: 'exec-5',
executionName: '黑名单规则改造',
projectId: 'prj-risk',
projectName: '风控引擎',
statusCode: 'pending',
statusName: '待开始',
priority: '3',
plannedStartDate: iso(now.add(5, 'day').startOf('day')),
plannedEndDate: iso(now.add(20, 'day').endOf('day')),
actualStartDate: null,
actualEndDate: null,
progressRate: 0
},
// 收银台 V3 · 1 条
{
id: 'exec-6',
executionName: '多币种支持 · 计算引擎',
projectId: 'prj-cashier',
projectName: '收银台 V3',
statusCode: 'pending',
statusName: '待开始',
priority: '2',
plannedStartDate: iso(now.add(2, 'day').startOf('day')),
plannedEndDate: iso(now.add(15, 'day').endOf('day')),
actualStartDate: null,
actualEndDate: null,
progressRate: 0,
projectRequirementId: 'req-cashier-001',
projectRequirementName: '多币种结算(含汇率快照)'
},
// 订单中心 · 1 条
{
id: 'exec-7',
executionName: '订单导出 V2',
projectId: 'prj-order',
projectName: '订单中心',
statusCode: 'active',
statusName: '进行中',
priority: '4',
plannedStartDate: iso(now.subtract(15, 'day').startOf('day')),
plannedEndDate: iso(now.add(7, 'day').endOf('day')),
actualStartDate: iso(now.subtract(12, 'day').startOf('day')),
actualEndDate: null,
progressRate: 35
},
// 已完成 —— builder 应过滤掉
{
id: 'exec-8',
executionName: '上一迭代 · 前端联调',
projectId: 'prj-mall-v2',
projectName: '商城 V2 升级',
statusCode: 'completed',
statusName: '已完成',
priority: '3',
plannedStartDate: iso(now.subtract(40, 'day').startOf('day')),
plannedEndDate: iso(now.subtract(15, 'day').endOf('day')),
actualStartDate: iso(now.subtract(38, 'day').startOf('day')),
actualEndDate: iso(now.subtract(14, 'day').endOf('day')),
progressRate: 100
},
// 已取消 —— builder 应过滤掉
{
id: 'exec-9',
executionName: '促销活动 · 春节专题',
projectId: 'prj-marketing',
projectName: '营销中台',
statusCode: 'cancelled',
statusName: '已取消',
priority: '3',
plannedStartDate: iso(now.subtract(30, 'day').startOf('day')),
plannedEndDate: iso(now.subtract(10, 'day').endOf('day')),
actualStartDate: null,
actualEndDate: null,
progressRate: 15
},
// 进度 100 但状态未扭转 —— builder 应过滤掉
{
id: 'exec-10',
executionName: '风控规则升级(待扭转)',
projectId: 'prj-risk',
projectName: '风控引擎',
statusCode: 'active',
statusName: '进行中',
priority: '2',
plannedStartDate: iso(now.subtract(8, 'day').startOf('day')),
plannedEndDate: iso(now.add(1, 'day').endOf('day')),
actualStartDate: iso(now.subtract(6, 'day').startOf('day')),
actualEndDate: null,
progressRate: 100
}
] satisfies WorkbenchMyExecutionItemSource[];
export const workbenchOwnedProjectMock = [
{
id: 'p1',
name: '商城 V2 升级',
code: 'MALL-V2',
progress: 70,
executionCount: 5,
taskCount: 32,
memberCount: 6,
overdueCount: 1,
remainingDays: 12,
myRole: '项目负责人',
milestones: [
{ id: 'm1', title: 'SSO 改造提测', timeLabel: '今日 18:00', tone: 'amber' },
{ id: 'm2', title: '迭代 24.05 关闭', timeLabel: '今日', tone: 'amber' },
{ id: 'm3', title: '多币种支持评审', timeLabel: '05-26', tone: 'slate' }
],
members: [
{ name: '张三', load: 50, level: 'ok' },
{ name: '李四', load: 30, level: 'ok' },
{ name: '王五', load: 90, level: 'over' }
]
},
{
id: 'p2',
name: '风控引擎接入',
code: 'RISK-ENGINE',
progress: 45,
executionCount: 3,
taskCount: 18,
memberCount: 4,
overdueCount: 2,
remainingDays: 30,
myRole: '项目负责人',
milestones: [
{ id: 'm4', title: '分片设计评审', timeLabel: '明日', tone: 'amber' },
{ id: 'm5', title: '缓存穿透优化交付', timeLabel: '05-28', tone: 'slate' }
],
members: [
{ name: '李四', load: 30, level: 'ok' },
{ name: '钱七', load: 65, level: 'warn' }
]
}
] satisfies WorkbenchOwnedProjectItemSource[];
export const workbenchProjectMock = [
{
id: 'prj-1',
@@ -253,104 +447,218 @@ export const workbenchProjectMock = [
}
] satisfies WorkbenchProjectItemSource[];
export const workbenchMyTaskMock = [
{
id: 't-1',
title: '支付回调接口联调',
statusCode: 'inProgress',
statusLabel: '进行中',
executionName: '收银台 V3 · 后端联调',
projectName: '收银台 V3',
priority: 'high',
deadline: iso(now.add(1, 'day').hour(17))
},
{
id: 't-2',
title: '订单导出 V2 文档编写',
statusCode: 'inProgress',
statusLabel: '进行中',
executionName: '订单中心 · 文档',
projectName: '订单中心',
priority: 'mid',
deadline: iso(now.add(3, 'day').hour(12))
},
{
id: 't-3',
title: 'API 返回结构调整',
statusCode: 'pending',
statusLabel: '待开始',
executionName: '收银台 V3 · 后端联调',
projectName: '收银台 V3',
priority: 'mid',
deadline: iso(now.subtract(1, 'day').hour(18))
},
{
id: 't-4',
title: '会员等级文案校对',
statusCode: 'inProgress',
statusLabel: '进行中',
executionName: '会员中心 · 文案',
projectName: '会员中心',
priority: 'low',
deadline: iso(now.add(2, 'day').hour(15))
},
{
id: 't-5',
title: '收银台 H5 适配',
statusCode: 'inProgress',
statusLabel: '进行中',
executionName: '收银台 V3 · 前端',
projectName: '收银台 V3',
priority: 'high',
deadline: iso(now.hour(20))
}
] satisfies WorkbenchMyTaskItemSource[];
const currentWeekStart = now.startOf('isoWeek').format('YYYY-MM-DD');
const previousWeekStart = now.subtract(1, 'week').startOf('isoWeek').format('YYYY-MM-DD');
export const workbenchMyRequirementMock = [
{ statusCode: 'pendingReview', statusLabel: '待评审', count: 3, tone: 'amber' },
{ statusCode: 'reviewing', statusLabel: '评审中', count: 2, tone: 'sky' },
{ statusCode: 'developing', statusLabel: '开发中', count: 5, tone: 'emerald' },
{ statusCode: 'paused', statusLabel: '已暂停', count: 1, tone: 'rose' }
] satisfies WorkbenchMyRequirementGroupSource[];
export const workbenchTeamTodoMock = [
{
projectId: 'prj-1',
projectName: '收银台 V3',
memberId: 'm-1',
memberName: '张三',
inProgress: 5,
overdue: 2,
weekDone: 3
export const workbenchMyWeekWorklogMock = {
current: {
weekStart: currentWeekStart,
weeklyFilledHours: 5,
dailyHours: [4, 7, 6, 8, 7.5],
target: 40,
distribution: [
{ key: 'prj-mall-v2', label: '商城 V2 升级', hours: 12.5, kind: 'project', projectId: 'prj-mall-v2' },
{ key: 'prj-risk', label: '风控引擎接入', hours: 8, kind: 'project', projectId: 'prj-risk' },
{ key: 'prj-cashier', label: '收银台 V3', hours: 6, kind: 'project', projectId: 'prj-cashier' },
{ key: 'prj-order', label: '订单中心', hours: 5, kind: 'project', projectId: 'prj-order' },
{ key: 'prj-member', label: '会员中心', hours: 3, kind: 'project', projectId: 'prj-member' },
{ key: 'prj-marketing', label: '营销中台', hours: 2, kind: 'project', projectId: 'prj-marketing' },
{ key: 'personal', label: '个人事项', hours: 4, kind: 'personal' },
{ key: 'other', label: '其他', hours: 2, kind: 'other' }
]
},
{
projectId: 'prj-1',
projectName: '收银台 V3',
memberId: 'm-2',
memberName: '李四',
inProgress: 3,
overdue: 0,
weekDone: 4
},
{
projectId: 'prj-2',
projectName: '会员中心',
memberId: 'm-3',
memberName: '王五',
inProgress: 2,
overdue: 1,
weekDone: 2
},
{
projectId: 'prj-3',
projectName: '订单中心',
memberId: 'm-4',
memberName: '赵六',
inProgress: 4,
overdue: 0,
weekDone: 5
previous: {
weekStart: previousWeekStart,
weeklyFilledHours: 0,
dailyHours: [8, 8, 7, 8, 7],
target: 40,
distribution: [
{ key: 'prj-mall-v2', label: '商城 V2 升级', hours: 15, kind: 'project', projectId: 'prj-mall-v2' },
{ key: 'prj-risk', label: '风控引擎接入', hours: 9, kind: 'project', projectId: 'prj-risk' },
{ key: 'prj-cashier', label: '收银台 V3', hours: 7, kind: 'project', projectId: 'prj-cashier' },
{ key: 'personal', label: '个人事项', hours: 5, kind: 'personal' },
{ key: 'other', label: '其他', hours: 2, kind: 'other' }
]
}
] satisfies WorkbenchTeamTodoRowSource[];
} satisfies WorkbenchMyWeekWorklogSource;
// 团队工时口径约定:「团队 = 当前用户所在团队,含自己」。
// 后端 /workbench/team-worklog 接口返回的 members 必须包含当前用户自己——
// 这样没有下级的人普通员工切到「团队工时」tab 也至少有自己这一条数据,
// 不会出现空白态。约定 members[0] = 当前用户UI 用「(我)」后缀标识。
export const workbenchTeamWorklogMock = {
current: {
weekStart: currentWeekStart,
expectedHoursPerMember: 40,
members: [
{
memberId: 'u-1',
memberName: '张三(我)',
items: [
{ key: 'prj-cashier', label: '收银台 V3', hours: 22, kind: 'project' },
{ key: 'prj-order', label: '订单中心', hours: 10, kind: 'project' },
{ key: 'personal', label: '个人事项', hours: 4, kind: 'personal' },
{ key: 'other', label: '其他', hours: 2, kind: 'other' }
]
},
{
memberId: 'u-2',
memberName: '李四',
items: [
{ key: 'prj-cashier', label: '收银台 V3', hours: 18, kind: 'project' },
{ key: 'prj-member', label: '会员中心', hours: 20, kind: 'project' },
{ key: 'personal', label: '个人事项', hours: 3, kind: 'personal' },
{ key: 'other', label: '其他', hours: 1, kind: 'other' }
]
},
{
memberId: 'u-3',
memberName: '王五',
items: [
{ key: 'prj-member', label: '会员中心', hours: 14, kind: 'project' },
{ key: 'prj-order', label: '订单中心', hours: 12, kind: 'project' },
{ key: 'personal', label: '个人事项', hours: 2, kind: 'personal' },
{ key: 'other', label: '其他', hours: 2, kind: 'other' }
]
},
{
memberId: 'u-4',
memberName: '赵六',
items: [
{ key: 'prj-cashier', label: '收银台 V3', hours: 24, kind: 'project' },
{ key: 'prj-order', label: '订单中心', hours: 18, kind: 'project' },
{ key: 'prj-member', label: '会员中心', hours: 4, kind: 'project' },
{ key: 'personal', label: '个人事项', hours: 2, kind: 'personal' }
]
},
{
memberId: 'u-5',
memberName: '钱七',
items: [
{ key: 'prj-order', label: '订单中心', hours: 14, kind: 'project' },
{ key: 'personal', label: '个人事项', hours: 6, kind: 'personal' },
{ key: 'other', label: '其他', hours: 5, kind: 'other' }
]
}
]
},
previous: {
weekStart: previousWeekStart,
expectedHoursPerMember: 40,
members: [
{
memberId: 'u-1',
memberName: '张三(我)',
items: [
{ key: 'prj-cashier', label: '收银台 V3', hours: 26, kind: 'project' },
{ key: 'prj-order', label: '订单中心', hours: 8, kind: 'project' },
{ key: 'personal', label: '个人事项', hours: 3, kind: 'personal' },
{ key: 'other', label: '其他', hours: 3, kind: 'other' }
]
},
{
memberId: 'u-2',
memberName: '李四',
items: [
{ key: 'prj-cashier', label: '收银台 V3', hours: 15, kind: 'project' },
{ key: 'prj-member', label: '会员中心', hours: 22, kind: 'project' },
{ key: 'personal', label: '个人事项', hours: 4, kind: 'personal' }
]
},
{
memberId: 'u-3',
memberName: '王五',
items: [
{ key: 'prj-member', label: '会员中心', hours: 16, kind: 'project' },
{ key: 'prj-order', label: '订单中心', hours: 10, kind: 'project' },
{ key: 'personal', label: '个人事项', hours: 5, kind: 'personal' },
{ key: 'other', label: '其他', hours: 3, kind: 'other' }
]
},
{
memberId: 'u-4',
memberName: '赵六',
items: [
{ key: 'prj-cashier', label: '收银台 V3', hours: 20, kind: 'project' },
{ key: 'prj-order', label: '订单中心', hours: 16, kind: 'project' },
{ key: 'prj-member', label: '会员中心', hours: 6, kind: 'project' },
{ key: 'personal', label: '个人事项', hours: 3, kind: 'personal' }
]
},
{
memberId: 'u-5',
memberName: '钱七',
items: [
{ key: 'prj-order', label: '订单中心', hours: 16, kind: 'project' },
{ key: 'personal', label: '个人事项', hours: 4, kind: 'personal' },
{ key: 'other', label: '其他', hours: 4, kind: 'other' }
]
}
]
}
} satisfies { current: WorkbenchTeamWorklogSource; previous: WorkbenchTeamWorklogSource };
// 项目 key 与 workbenchTeamWorklogMock 对齐,保证跨 widget 项目色一致
export const workbenchTeamLoadMock = {
weekStart: now.startOf('isoWeek').format('YYYY-MM-DD'),
members: [
// 高负载:进行中 7 或 临期+逾期 ≥ 2
{
memberId: 'u-1',
memberName: '张三',
items: [
{ key: 'prj-cashier', label: '收银台 V3', kind: 'project', count: 4 },
{ key: 'prj-order', label: '订单中心', kind: 'project', count: 2 },
{ key: 'personal', label: '个人事项', kind: 'personal', count: 1 }
],
dueSoon: 2,
overdue: 1
},
{
memberId: 'u-4',
memberName: '赵六',
items: [
{ key: 'prj-cashier', label: '收银台 V3', kind: 'project', count: 2 },
{ key: 'prj-order', label: '订单中心', kind: 'project', count: 2 },
{ key: 'prj-member', label: '会员中心', kind: 'project', count: 1 }
],
dueSoon: 1,
overdue: 2
},
// 中负载:进行中 ≥ 4 或 临期+逾期 ≥ 1
{
memberId: 'u-2',
memberName: '李四',
items: [
{ key: 'prj-cashier', label: '收银台 V3', kind: 'project', count: 2 },
{ key: 'prj-member', label: '会员中心', kind: 'project', count: 2 }
],
dueSoon: 1,
overdue: 0
},
{
memberId: 'u-3',
memberName: '王五',
items: [
{ key: 'prj-member', label: '会员中心', kind: 'project', count: 2 },
{ key: 'prj-order', label: '订单中心', kind: 'project', count: 1 }
],
dueSoon: 1,
overdue: 0
},
// 正常
{
memberId: 'u-5',
memberName: '钱七',
items: [
{ key: 'prj-order', label: '订单中心', kind: 'project', count: 1 },
{ key: 'personal', label: '个人事项', kind: 'personal', count: 1 }
],
dueSoon: 0,
overdue: 0
}
]
} satisfies WorkbenchTeamLoadSource;
export const workbenchProjectHealthMock = [
{
@@ -387,10 +695,3 @@ export const workbenchProgressChartMock = [
{ projectId: 'prj-2', projectName: '会员中心', weekCompletionRate: 62 },
{ projectId: 'prj-3', projectName: '订单中心', weekCompletionRate: 45 }
] satisfies WorkbenchProgressBarSource[];
export const workbenchFavoriteMock = [
{ id: 'fav-1', kind: 'product', title: '收银台 V3', source: '产品库' },
{ id: 'fav-2', kind: 'project', title: '会员中心 · 一期', source: '项目库' },
{ id: 'fav-3', kind: 'requirement', title: '订单导出 V2', source: '收银台 V3' },
{ id: 'fav-4', kind: 'task', title: '支付回调接口联调', source: '收银台 V3 · 后端联调' }
] satisfies WorkbenchFavoriteItemSource[];