收敛 PC 工作台视觉 05-27-pc-frontend-visual-consistency-wave1

这个提交包含在:
2026-05-27 14:10:51 +08:00
父节点 63e214ca77
当前提交 466075c4a9
共修改 16 个文件,包含 551 行新增和 148 行删除
+1 -1
查看文件
@@ -526,7 +526,7 @@ test("Portal 员工视角优先显示今日动作顺序", async ({ page }) => {
"portal-card-my_projects",
]);
await expect(page.locator("summary").filter({ hasText: "我的申请与资产 / 继续工作" })).toBeVisible();
await expect(page.locator("summary").filter({ hasText: "更多入口" })).toBeVisible();
await expect(page.getByRole("tab", { name: "公告" })).toBeVisible();
await expect(page.getByRole("heading", { name: "常用" })).toBeVisible();
await expect(page.getByRole("heading", { name: "团队状态" })).toBeVisible();
+129 -14
查看文件
@@ -1,5 +1,9 @@
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { expect, test, type Page, type Route, type TestInfo } from "@playwright/test";
const SCREENSHOT_DIR = path.resolve(process.cwd(), "../output/playwright/pc-frontend-visual-consistency-wave1");
function envelope(data: unknown) {
return { requestId: "mock-r96-ui", data };
}
@@ -48,6 +52,22 @@ async function mockVisualApis(page: Page) {
return;
}
if (path === "/api/auth/capabilities") {
await fulfillJson(route, {
emailDelivery: {
ready: true,
statusReasons: [],
},
emailLoginCode: {
enabled: true,
},
passwordReset: {
enabled: true,
},
});
return;
}
if (path === "/api/auth/wecom/config") {
await fulfillJson(route, {
enabled: true,
@@ -166,11 +186,32 @@ async function mockVisualApis(page: Page) {
return;
}
if (path === "/api/workflow/tasks/summary") {
await fulfillJson(route, {
scope: url.searchParams.get("scope") === "system" ? "system" : "mine",
pendingTotal: 1,
overdueTotal: 0,
earliestPendingAt: "2026-05-06T09:00:00.000Z",
averagePendingAgeHours: 2,
byEntityType: [
{
key: "reimbursement",
pendingTotal: 1,
overdueTotal: 0,
},
],
byStep: [],
byAssigneeRole: [],
});
return;
}
if (path === "/api/notifications") {
await fulfillJson(route, {
items: [
{
id: "notice-r96",
type: "workflow.task_assigned",
title: "审批提醒",
body: "有 1 条待办。",
targetType: "workflow_task",
@@ -182,6 +223,59 @@ async function mockVisualApis(page: Page) {
total: 1,
page: 1,
pageSize: 3,
unreadOnly: false,
unreadCount: 1,
});
return;
}
if (path === "/api/reimbursement-templates") {
await fulfillJson(route, { enabled: true, items: [] });
return;
}
if (path === "/api/reimbursements/linkable-travel-requests") {
await fulfillJson(route, { items: [] });
return;
}
if (path === "/api/reimbursements") {
await fulfillJson(route, {
page: 1,
pageSize: 10,
total: 1,
scope: "own",
items: [
{
id: "rb-r96",
formNumber: "BX20260506001",
status: "draft",
reimbursementDate: "2026-05-06",
totalAmount: "168.30",
description: "视觉基线报销",
payment: {
paymentVoucherNo: null,
paymentDate: null,
paymentChannel: null,
paidAt: null,
paidBy: null,
},
owner: {
id: "employee-r96",
employeeNo: "R96001",
name: "Employee",
},
submitter: {
id: "employee-r96",
employeeNo: "R96001",
name: "Employee",
},
project: null,
pendingWorkflow: null,
createdAt: "2026-05-06T09:00:00.000Z",
updatedAt: "2026-05-06T09:00:00.000Z",
},
],
});
return;
}
@@ -191,10 +285,22 @@ async function mockVisualApis(page: Page) {
}
async function attachScreenshot(page: Page, testInfo: TestInfo, name: string) {
const body = await page.screenshot({ fullPage: true });
await testInfo.attach(name, {
body: await page.screenshot({ fullPage: true }),
body,
contentType: "image/png",
});
await mkdir(SCREENSHOT_DIR, { recursive: true });
await writeFile(path.join(SCREENSHOT_DIR, name), body);
}
async function loginVisualUser(page: Page) {
await page.goto("/login");
await expect(page.getByTestId("login-password-primary-panel")).toBeVisible();
await page.getByTestId("login-identifier").fill("[email protected]");
await page.getByTestId("login-password").fill("EmployeePassword1234");
await page.getByTestId("login-submit").click();
await expect(page).toHaveURL(/\/portal/);
}
test.describe("visual shell", () => {
@@ -213,11 +319,7 @@ test.describe("visual shell", () => {
test("登录后壳层布局基线", async ({ page }, testInfo) => {
await mockVisualApis(page);
await page.setViewportSize({ width: 1440, height: 1024 });
await page.goto("/login");
await page.getByTestId("login-identifier").fill("[email protected]");
await page.getByTestId("login-password").fill("EmployeePassword1234");
await page.getByTestId("login-submit").click();
await expect(page).toHaveURL(/\/portal/);
await loginVisualUser(page);
await expect(page.locator("aside")).toBeVisible();
await expect(page.getByTestId("topbar-profile-trigger")).toBeVisible();
@@ -227,13 +329,29 @@ test.describe("visual shell", () => {
await attachScreenshot(page, testInfo, "app-shell.png");
});
test("PC 首页与报销列表收敛布局基线", async ({ page }, testInfo) => {
await mockVisualApis(page);
await page.setViewportSize({ width: 1440, height: 1024 });
await loginVisualUser(page);
await expect(page.getByRole("heading", { name: "待处理" })).toBeVisible();
await expect(page.getByRole("heading", { name: "常用" })).toBeVisible();
await expect(page.locator("summary").filter({ hasText: /角色重点|更多入口/ })).toBeVisible();
await attachScreenshot(page, testInfo, "pc-portal-wave1.png");
await page.goto("/reimbursements");
await expect(page.getByRole("heading", { name: "我的报销" })).toBeVisible();
await expect(page.getByTestId("reimbursements-create")).toBeVisible();
await expect(page.getByTestId("reimbursements-export")).toBeVisible();
await expect(page.getByTestId("reimbursements-status-tabs")).toBeVisible();
await expect(page.getByTestId("reimbursements-table")).toBeVisible();
await attachScreenshot(page, testInfo, "pc-reimbursements-wave1.png");
});
test("移动首页任务化布局基线", async ({ page }, testInfo) => {
await mockVisualApis(page);
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/login");
await page.getByTestId("login-identifier").fill("[email protected]");
await page.getByTestId("login-password").fill("EmployeePassword1234");
await page.getByTestId("login-submit").click();
await loginVisualUser(page);
await page.goto("/mobile");
await expect(page.getByRole("heading", { name: "今天" })).toBeVisible();
@@ -247,10 +365,7 @@ test.describe("visual shell", () => {
test("审批待办手机卡片布局基线", async ({ page }, testInfo) => {
await mockVisualApis(page);
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/login");
await page.getByTestId("login-identifier").fill("[email protected]");
await page.getByTestId("login-password").fill("EmployeePassword1234");
await page.getByTestId("login-submit").click();
await loginVisualUser(page);
await page.goto("/workflow/tasks");
await expect(page.getByTestId("workflow-tasks-mobile-list")).toBeVisible();