R60 A1-A6:首页工作台优化

这个提交包含在:
2026-04-12 12:30:14 +08:00
父节点 dbab1d7b6e
当前提交 3f8c5144c2
共修改 7 个文件,包含 1514 行新增和 14 行删除
+38 -1
查看文件
@@ -11,7 +11,6 @@ function envelope(data: unknown) {
};
}
function mockMe(page: import("@playwright/test").Page, roleCodes: string[], name: string) {
return page.route("**/api/me", async (route) => {
await route.fulfill({
@@ -101,6 +100,21 @@ function mockNavigation(page: import("@playwright/test").Page, mode: "employee"
});
}
function mockAttendanceToday(page: import("@playwright/test").Page) {
return page.route("**/api/attendance/me/today", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(envelope({
workDate: "2026-03-22",
needClockIn: true,
exemptionReason: null,
item: null,
})),
});
});
}
function mockPortalHome(page: import("@playwright/test").Page, mode: "employee" | "manager") {
return page.route("**/api/portal/home", async (route) => {
const data = mode === "manager"
@@ -133,6 +147,14 @@ function mockPortalHome(page: import("@playwright/test").Page, mode: "employee"
projectSummary: { myCount: 5, overdueMilestonesCount: 2, highPriorityTaskCount: 2, openRiskCount: 1, followUpDueTodayCount: 2, closurePendingProjectCount: 1, href: "/projects" },
peopleLifecycle: { onboardingCount: 1, offboardingCount: 0, assetReturnPendingCount: 1, href: "/people" },
adoptionSummary: { activeEmployeeCount: 8, login7dCount: 8, weeklySubmittedCount: 6, weeklyMissingCount: 2, presenceUpdatedCount: 7, presenceMissingCount: 1, announcementCount: 1, login7dRate: 100, weeklySubmissionRate: 75, presenceCoverageRate: 87.5, hints: ["本周仍有 2 人未提交周报"] },
recentActivities: [
{ id: "activity-1", type: "reimbursement.submit", actorName: "张三", summary: "张三 提交了报销单", createdAt: "2026-03-22T09:00:00.000Z", href: "/reimbursements/reimbursement-1" },
{ id: "activity-2", type: "attendance.clock_in", actorName: "李四", summary: "李四 完成了上班打卡", createdAt: "2026-03-22T08:50:00.000Z", href: "/attendance" },
],
teamStatusBoard: [
{ employeeId: "employee-1", name: "张三", avatarUrl: null, presenceStatus: "office", attendanceClockIn: "2026-03-22T00:55:00.000Z", attendanceClockOut: null, onLeave: false, leaveType: null },
{ employeeId: "employee-2", name: "李四", avatarUrl: null, presenceStatus: "leave", attendanceClockIn: null, attendanceClockOut: null, onLeave: true, leaveType: "年假" },
],
}
: {
workspace: { id: "workspace-1", persisted: true, profile: "small_team_internal" },
@@ -162,6 +184,13 @@ function mockPortalHome(page: import("@playwright/test").Page, mode: "employee"
projectSummary: { myCount: 2, overdueMilestonesCount: 1, highPriorityTaskCount: 1, openRiskCount: 0, followUpDueTodayCount: 1, closurePendingProjectCount: 0, href: "/projects" },
peopleLifecycle: null,
adoptionSummary: null,
recentActivities: [
{ id: "activity-1", type: "leave.submit", actorName: "Employee", summary: "Employee 提交了请假单", createdAt: "2026-03-22T09:00:00.000Z", href: "/leave-requests" },
],
teamStatusBoard: [
{ employeeId: "employee-1", name: "Employee", avatarUrl: null, presenceStatus: "office", attendanceClockIn: "2026-03-22T00:55:00.000Z", attendanceClockOut: null, onLeave: false, leaveType: null },
{ employeeId: "employee-2", name: "同事", avatarUrl: null, presenceStatus: "remote", attendanceClockIn: null, attendanceClockOut: null, onLeave: false, leaveType: null },
],
};
await route.fulfill({
@@ -175,6 +204,7 @@ function mockPortalHome(page: import("@playwright/test").Page, mode: "employee"
test("Portal 员工视角优先显示今日动作顺序", async ({ page }) => {
await mockNavigation(page, "employee");
await mockPortalHome(page, "employee");
await mockAttendanceToday(page);
await mockMe(page, ["employee"], "Employee");
await login(page, EMPLOYEE);
@@ -195,11 +225,16 @@ test("Portal 员工视角优先显示今日动作顺序", async ({ page }) => {
await expect(page.getByText("我的申请与资产")).toBeVisible();
await expect(page.getByRole("heading", { name: "公告" })).toBeVisible();
await expect(page.getByRole("heading", { name: "快捷操作栏" })).toBeVisible();
await expect(page.getByRole("heading", { name: "团队全员状态" })).toBeVisible();
await expect(page.getByRole("heading", { name: "今日待办聚合" })).toBeVisible();
await expect(page.getByRole("heading", { name: "近期动态流" })).toBeVisible();
});
test("Portal 管理视角重排正确,备用模块默认折叠", async ({ page }) => {
await mockNavigation(page, "manager");
await mockPortalHome(page, "manager");
await mockAttendanceToday(page);
await mockMe(page, ["admin"], "Admin");
await login(page, ADMIN);
@@ -226,6 +261,7 @@ test("Portal 管理视角重排正确,备用模块默认折叠", async ({ page
test("Portal 域管理角色也进入经理视图", async ({ page }) => {
await mockNavigation(page, "manager");
await mockPortalHome(page, "manager");
await mockAttendanceToday(page);
await mockMe(page, ["administration"], "Administration");
await login(page, EMPLOYEE);
@@ -244,6 +280,7 @@ test("企微 WebView 下顶栏显示入口提示", async ({ page }) => {
});
await mockNavigation(page, "employee");
await mockPortalHome(page, "employee");
await mockAttendanceToday(page);
await mockMe(page, ["employee"], "Employee");
await login(page, EMPLOYEE);