import { expect, test } from '@playwright/test' async function setup(page: any, state = 'not_started', failManualOnce = false) { await page.setViewportSize({ width: 1920, height: 1080 }) await page.context().addCookies([{ name: 'Admin-Token', value: 'test-only', domain: 'localhost', path: '/' }]) const requests: { path: string, body: any }[] = [] const items = [ { id: 11, taskType: 'syb_order', executionMode: 'live', status: 'order_created', pddOrderNo: 'EXAMPLE-11', orderSubmittedAt: '2026-09-19T12:00:00Z', pddOrderAmountCent: null as number | null, paymentReviewStatus: 'unpaid', orderWriteback: { status: state, canSubmit: ['not_started', 'failed', 'unknown'].includes(state), reason: state === 'conflict' ? 'SYB 已有不同单号,未覆盖' : '' } }, { id: 12, taskType: 'syb_order', executionMode: 'live', status: 'failed', retryable: true, orderWriteback: { status: 'not_applicable', canSubmit: false, reason: '' } }, { id: 13, taskType: 'stock', executionMode: 'live', status: 'order_created', orderWriteback: { status: 'not_applicable', canSubmit: false, reason: '' } } ] await page.route('**/api/**', async route => { const path = new URL(route.request().url()).pathname if (path.endsWith('/getinfo')) return route.fulfill({ json: { code: 200, data: { roles: ['admin'], name: '测试采购员', avatar: '', introduction: '', permissions: [] } } }) if (path.endsWith('/menurole')) return route.fulfill({ json: { code: 200, data: [{ path: '/collection-purchase', component: 'Layout', menuName: 'GoAutoCollectionPurchase', title: '采集采购', visible: '0', children: [{ path: '/purchase-tasks/index', component: '/goauto/purchase-tasks/index', menuName: 'GoAutoPurchaseTasks', title: '采购管理', visible: '0' }] }] } }) if (route.request().method() === 'POST') { const body = route.request().postDataJSON() requests.push({ path, body }) if (path.endsWith('/resolve-unknown')) { if (failManualOnce) { failManualOnce = false; return route.fulfill({ status: 500, json: { code: 500, message: 'synthetic failure' } }) } items[0].pddOrderAmountCent = body.pddOrderAmountCent return route.fulfill({ json: { code: 200, data: items[0] } }) } if (path.endsWith('/syb-order-writeback')) { items[0].orderWriteback = { status: 'pending', canSubmit: false, reason: '' } return route.fulfill({ json: { code: 200, data: { items: body.purchaseTaskIds.map((taskId: number) => ({ taskId, result: 'pending', reason: '已受理' })) } } }) } if (path.endsWith('/batch-retry')) return route.fulfill({ json: { code: 200, data: { items: [], createdCount: 1, failedCount: 0 } } }) throw new Error('unexpected write: ' + path) } if (path.endsWith('/purchase-tasks/11')) return route.fulfill({ json: { code: 200, data: { task: items[0], attempts: [] } } }) if (path.endsWith('/purchase-tasks')) return route.fulfill({ json: { code: 200, data: { items, total: items.length } } }) return route.fulfill({ json: { code: 200, data: [] } }) }) await page.goto('/#/purchase-tasks/index') await expect(page.getByText('CG-11', { exact: true })).toBeVisible() return requests } test('混选任务分别回填与重试,不把已下单任务交给采购重试', async ({ page }) => { const requests = await setup(page) await page.locator('.el-table__header-wrapper .el-checkbox').click() await expect(page.getByRole('button', { name: '重试(1)', exact: true })).toBeEnabled() await expect(page.getByRole('button', { name: '批量回填 SYB(1)', exact: true })).toBeEnabled() await page.getByRole('button', { name: '重试(1)', exact: true }).click() const retry = page.getByRole('dialog', { name: '确认重试失败任务' }) await expect(retry.getByText('CG-12', { exact: true })).toBeVisible() await expect(retry.getByText('CG-11', { exact: true })).toHaveCount(0) await retry.getByRole('button', { name: '确认重试', exact: true }).click() await expect.poll(() => requests.length).toBe(1) expect(requests[0].body.taskIds).toEqual([12]) await page.getByRole('dialog', { name: '批量重试结果' }).getByRole('button', { name: '留在当前页' }).click() await page.locator('.el-table__header-wrapper .el-checkbox').first().click() await page.getByRole('button', { name: '批量回填 SYB(1)', exact: true }).click() const result = page.getByRole('dialog', { name: 'SYB 单号回填提交结果' }) await expect(result.getByText('待回填', { exact: true })).toBeVisible() expect(requests[1].body.purchaseTaskIds).toEqual([11]) expect(requests[1].body.requestId).toMatch(/^[0-9a-f-]{36}$/) await result.getByRole('button', { name: '关闭', exact: true }).click() await expect(result).toBeHidden() await expect(page.getByRole('button', { name: '批量回填 SYB(0)', exact: true })).toBeDisabled() }) test('未支付订单可从详情补偿回填,旧物流流程保持独立', async ({ page }) => { const requests = await setup(page, 'failed') await page.getByRole('button', { name: '详情', exact: true }).first().click() const drawer = page.getByRole('dialog', { name: '采购任务详情' }) await expect(drawer.getByText('SYB 物流回填', { exact: true })).toBeVisible() await drawer.getByRole('button', { name: '重试回填 SYB 单号', exact: true }).click() await expect(page.getByRole('dialog', { name: 'SYB 单号回填提交结果' })).toBeVisible() expect(requests).toHaveLength(1) expect(requests[0].path).toContain('/syb-order-writeback') expect(requests[0].body.purchaseTaskIds).toEqual([11]) }) test('冲突显示原因且没有覆盖按钮', async ({ page }) => { await setup(page, 'conflict') await page.getByRole('button', { name: '详情', exact: true }).first().click() const drawer = page.getByRole('dialog', { name: '采购任务详情' }) await expect(drawer.getByText('单号冲突', { exact: true })).toBeVisible() await expect(drawer.getByText('SYB 已有不同单号,未覆盖', { exact: true })).toBeVisible() await expect(drawer.getByRole('button', { name: /回填 SYB 单号/ })).toHaveCount(0) }) test('状态下拉框筛选已回填SYB,清空后恢复普通列表', async ({ page }) => { const requests = await setup(page, 'succeeded') const statuses: string[] = [] await page.route('**/api/admin/v1/purchase-tasks?**', async route => { const query = new URL(route.request().url()).searchParams statuses.push(query.get('status') || '') expect(query.get('page')).toBe('1') return route.fulfill({ json: { code: 200, data: { items: [{ id: 11, taskType: 'syb_order', executionMode: 'live', status: 'order_created', orderWriteback: { status: 'succeeded', canSubmit: false } }], total: 1 } } }) }) const field = page.locator('.search-form .el-form-item').filter({ has: page.locator('label').filter({ hasText: /^状态$/ }) }) await field.locator('.el-select').click() await page.getByRole('option', { name: '已回填 SYB', exact: true }).click() await page.getByRole('button', { name: '查询', exact: true }).click() await expect.poll(() => statuses.at(-1)).toBe('syb_writeback_succeeded') await expect(page.getByText('订单已创建', { exact: true }).first()).toBeVisible() await field.locator('.el-select').hover() await field.locator('.el-select__clear').click() await page.getByRole('button', { name: '查询', exact: true }).click() await expect.poll(() => statuses.at(-1)).toBe('') expect(requests).toHaveLength(0) }) for (const amount of ['0', '13.01']) { test(`已创建订单补金额 ${amount},不重新采购或修改支付状态`, async ({ page }) => { const requests = await setup(page) await page.getByRole('button', { name: '详情', exact: true }).first().click() const drawer = page.getByRole('dialog', { name: '采购任务详情' }) await drawer.getByRole('button', { name: '补全订单信息', exact: true }).click() const dialog = page.getByRole('dialog', { name: '补全订单信息', exact: true }) await expect(dialog.getByRole('button', { name: '保存', exact: true })).toBeDisabled() await expect(dialog.getByRole('textbox').first()).toHaveAttribute('readonly', '') await dialog.getByPlaceholder('例如 13.00').fill('-1') await expect(dialog.getByText('请输入不小于0、最多两位小数的金额')).toBeVisible() await expect(dialog.getByRole('button', { name: '保存', exact: true })).toBeDisabled() await dialog.getByPlaceholder('例如 13.00').fill(amount) await dialog.getByRole('button', { name: '保存', exact: true }).click() await expect(dialog).toBeHidden() expect(requests).toHaveLength(1) expect(requests[0].body.pddOrderAmountCent).toBe(Math.round(Number(amount) * 100)) expect(requests[0].body.pddOrderNo).toBe('EXAMPLE-11') expect(requests[0].body.orderSubmittedAt).toBe('2026-09-19T12:00:00Z') expect(requests[0].path).toContain('/resolve-unknown') await expect(drawer.getByText('信息完整', { exact: true })).toBeVisible() await expect(drawer.getByRole('button', { name: '补全订单信息', exact: true })).toHaveCount(0) }) } test('补录失败保留输入,原样重试复用requestId', async ({ page }) => { const requests = await setup(page, 'not_started', true) await page.getByRole('button', { name: '详情', exact: true }).first().click() await page.getByRole('button', { name: '补全订单信息', exact: true }).click() const dialog = page.getByRole('dialog', { name: '补全订单信息', exact: true }) await dialog.getByPlaceholder('例如 13.00').fill('15.50') await dialog.getByRole('button', { name: '保存', exact: true }).click() await expect(dialog.getByText(/保存未确认/)).toBeVisible() await expect(dialog.getByPlaceholder('例如 13.00')).toHaveValue('15.50') await dialog.getByRole('button', { name: '保存', exact: true }).click() await expect(dialog).toBeHidden() expect(requests).toHaveLength(2) expect(requests[1].body).toEqual(requests[0].body) })