实现报销材料合并预览与差旅概要(07-27-yang-jun-reimbursement-readiness)
这个提交包含在:
@@ -180,6 +180,13 @@ function buildReimbursementDetail(state: ReturnType<typeof buildReimbursementSta
|
||||
reimbursementDate: "2026-05-08",
|
||||
totalAmount: "1683.00",
|
||||
description: "客户招待报销",
|
||||
travelSummary: {
|
||||
isTravel: true,
|
||||
purpose: "合成差旅概要",
|
||||
location: "测试地点",
|
||||
startDate: "2026-05-07",
|
||||
endDate: "2026-05-08",
|
||||
},
|
||||
projectId: null,
|
||||
linkedTravelRequestId: null,
|
||||
linkedTravelRequest: null,
|
||||
@@ -358,26 +365,19 @@ async function mockApis(page: Page, state: ReturnType<typeof buildReimbursementS
|
||||
}
|
||||
|
||||
if (pathName === `/api/reimbursements/${reimbursementId}/materials/preview-bundle` && request.method() === "POST") {
|
||||
const pages = [
|
||||
...state.invoices.map((_, index) => ({
|
||||
url: `https://oa.hyinda.com/api/reimbursements/${reimbursementId}/materials/preview/77777777-7777-4777-8777-777777777777/${index}?previewToken=invoice-${index}`,
|
||||
materialType: "invoice",
|
||||
materialIndex: index + 1,
|
||||
pageNumber: 1,
|
||||
})),
|
||||
...state.attachments.map((_, index) => ({
|
||||
url: `https://oa.hyinda.com/api/reimbursements/${reimbursementId}/materials/preview/77777777-7777-4777-8777-777777777777/${state.invoices.length + index}?previewToken=attachment-${index}`,
|
||||
materialType: "attachment",
|
||||
materialIndex: index + 1,
|
||||
pageNumber: 1,
|
||||
})),
|
||||
];
|
||||
await fulfillJson(route, { expiresAt: "2026-07-31T10:02:00.000Z", pages });
|
||||
await fulfillJson(route, {
|
||||
expiresAt: "2026-07-31T10:02:00.000Z",
|
||||
signedUrl: `https://oa.hyinda.com/api/reimbursements/${reimbursementId}/materials/preview/77777777-7777-4777-8777-777777777777?previewToken=merged-pdf`,
|
||||
originalName: "reimbursement-materials-preview.pdf",
|
||||
mimeType: "application/pdf",
|
||||
fileSize: 8192,
|
||||
pageCount: state.invoices.length + state.attachments.length,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathName.includes(`/api/reimbursements/${reimbursementId}/materials/preview/`) && request.method() === "GET") {
|
||||
await route.fulfill({ status: 200, contentType: "image/png", body: Buffer.from("synthetic-preview-image") });
|
||||
await route.fulfill({ status: 200, contentType: "application/pdf", body: Buffer.from("%PDF synthetic-preview") });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -658,14 +658,12 @@ test.describe("R111 国内报销发票与附件双端体验", () => {
|
||||
});
|
||||
await page.getByRole("button", { name: "上传附件" }).click();
|
||||
await expect(page.getByText("付款截图.png")).toBeVisible();
|
||||
await expect(page.getByTestId("reimbursement-travel-summary")).toContainText("合成差旅概要");
|
||||
await expect(page.getByTestId("reimbursement-travel-summary")).toContainText("测试地点");
|
||||
await expect(page.getByTestId("reimbursement-materials")).toContainText("发票 2 · 补充附件 1");
|
||||
await page.getByTestId("reimbursement-materials-open-all").click();
|
||||
await page.waitForURL(new RegExp(`/reimbursements/${reimbursementId}/materials$`));
|
||||
await expect(page.getByTestId("reimbursement-material-viewer")).toContainText("共 3 页");
|
||||
await expect(page.getByRole("img", { name: /预览/ })).toHaveCount(3);
|
||||
await page.getByRole("button", { name: "返回报销单" }).click();
|
||||
await page.waitForURL(new RegExp(`/reimbursements/${reimbursementId}$`));
|
||||
expect(await page.evaluate(() => (window as typeof window & { __r111OpenCalls: unknown[][] }).__r111OpenCalls.length)).toBe(0);
|
||||
await expect.poll(() => page.evaluate(() => (window as typeof window & { __r111OpenCalls: unknown[][] }).__r111OpenCalls.length)).toBe(1);
|
||||
expect(await page.evaluate(() => (window as typeof window & { __r111OpenCalls: unknown[][] }).__r111OpenCalls[0]?.[0])).toContain("previewToken=merged-pdf");
|
||||
|
||||
const bodyText = await page.locator("body").innerText();
|
||||
expect(bodyText).not.toContain("绑定整单");
|
||||
@@ -783,6 +781,8 @@ test.describe("R111 国内报销发票与附件双端体验", () => {
|
||||
});
|
||||
await mobile.page.getByTestId("mobile-reimbursement-materials").getByRole("button", { name: "上传附件" }).click();
|
||||
await expect(mobile.page.getByText("付款截图.png")).toBeVisible();
|
||||
await expect(mobile.page.getByTestId("mobile-reimbursement-travel-summary")).toContainText("合成差旅概要");
|
||||
await expect(mobile.page.getByTestId("mobile-reimbursement-travel-summary")).toContainText("测试地点");
|
||||
await expect(mobile.page.getByTestId("mobile-reimbursement-materials")).toContainText("发票 2 · 补充附件 1");
|
||||
const openAllButton = mobile.page.getByTestId("mobile-reimbursement-materials-open-all");
|
||||
const openAllTarget = await openAllButton.boundingBox();
|
||||
@@ -790,19 +790,16 @@ test.describe("R111 国内报销发票与附件双端体验", () => {
|
||||
await openAllButton.click();
|
||||
await expect.poll(() => mobile.page.evaluate(() => (
|
||||
window as typeof window & { __r111BridgeCalls: Array<{ name: string }> }
|
||||
).__r111BridgeCalls.filter((call) => call.name === "imagePreview").length)).toBe(1);
|
||||
).__r111BridgeCalls.filter((call) => call.name === "previewFile").length)).toBe(1);
|
||||
const nativePreviewCalls = await mobile.page.evaluate(() => (
|
||||
window as typeof window & { __r111BridgeCalls: Array<{ name: string; params: Record<string, unknown> }> }
|
||||
).__r111BridgeCalls.filter((call) => call.name === "imagePreview"));
|
||||
).__r111BridgeCalls.filter((call) => call.name === "previewFile"));
|
||||
expect(nativePreviewCalls).toEqual([
|
||||
expect.objectContaining({
|
||||
params: expect.objectContaining({
|
||||
current: expect.stringContaining(`/materials/preview/`),
|
||||
urls: expect.arrayContaining([
|
||||
expect.stringContaining("previewToken=invoice-0"),
|
||||
expect.stringContaining("previewToken=invoice-1"),
|
||||
expect.stringContaining("previewToken=attachment-0"),
|
||||
]),
|
||||
url: expect.stringContaining("previewToken=merged-pdf"),
|
||||
name: "reimbursement-materials-preview.pdf",
|
||||
size: 8192,
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
|
||||
在新工单中引用
屏蔽一个用户