yeg
2026-09-08 5d556fee1d0c287dd7935d06bb48f27206485fdc
完成化成工步端到端验收并保留旧版回归
4个文件已修改
43 ■■■■■ 已修改文件
src/views/FormationReportForm.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tests/e2e/formation-curves-print.spec.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tests/e2e/formation-matrix.spec.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tests/e2e/formation-step-curves.spec.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/FormationReportForm.vue
@@ -119,7 +119,7 @@
  const allowed = new Set(items.value.flatMap(item => fields.value.flatMap(field =>
    (field.scope === 'detail' ? [0] : item.cycleNos).map(cycle => formationValueKey(item, cycle, field)))))
  values.value = Object.fromEntries(Object.entries(values.value).filter(([key]) => allowed.has(key)))
  const sourceKeys = new Set(items.value.flatMap(item => [formationSeriesKey(item), ...Object.entries(item.stepCurves ?? {}).flatMap(([cycle, steps]) => Object.keys(steps).map(step => `${formationSeriesKey(item)}|${cycle}|${step}`))]))
  const sourceKeys = new Set(items.value.flatMap(item => [formationSeriesKey(item), ...Object.entries(item.chargeSteps ?? {}).flatMap(([cycle, steps]) => steps.map(step => `${formationSeriesKey(item)}|${cycle}|${step.stepNo}`))]))
  sourceDetailOverrides.value = Object.fromEntries(Object.entries(sourceDetailOverrides.value).filter(([key]) => sourceKeys.has(key)))
  hiddenSeriesKeys.value = hiddenSeriesKeys.value.filter(key => sourceKeys.has(key))
  const cycleKeys = new Set(items.value.flatMap(item => item.cycleNos.map(cycle => formationCycleKey(item, cycle))))
tests/e2e/formation-curves-print.spec.ts
@@ -101,30 +101,23 @@
  await api(page, `formation-report/${v1.id}`, 'GET', undefined, 403)
})
test('化成页面循环切换、五类四条曲线、版本设置和多页打印', async ({ page }) => {
test('历史整循环化成页面保留五类四条曲线、版本设置和多页打印', async ({ page }) => {
  await login(page)
  const pageErrors: string[] = []
  page.on('pageerror', error => pageErrors.push(error.message))
  await page.goto('/report-formation/new')
  await page.getByRole('button', { name: '打印 / 另存 PDF', exact: true }).click()
  await expect(page.getByText('请先保存报表后再打印', { exact: true })).toBeVisible()
  await page.locator('.source-panel .el-select').click()
  for (const number of [2, 3, 4, 5]) await page.getByRole('option', { name: `化成测试${number} / 曲线验收组 / 同名电池`, exact: true }).click()
  await page.getByRole('combobox', { name: '化成来源', exact: true }).press('Escape')
  const created = (await api(page, 'formation-report', 'POST', { name: '页面化成打印', description: '', selections: selections.slice(1), settings: { ...settings, values: {}, seriesColors: { '8902|8902': '#123456' } } })).data
  // 整循环展示已属于历史契约;隔离库保留旧快照事实,新增工步验收见 formation-step-curves。
  stack.executeIsolatedSql(`UPDATE t_formation_report_version SET snapshot_json=JSON_REMOVE(snapshot_json,'$.curveMode',${[0, 1, 2, 3].map(index => `'$.items[${index}].stepCurves'`).join(',')},'$.settings.manualChargeSteps','$.settings.curveStepSelections','$.settings.hiddenSeriesKeys','$.settings.activeCycle') WHERE report_id=${created.id}`)
  const id = created.id
  await page.goto(`/report-formation/${id}/edit?mode=view`)
  await expect(page.locator('.chart-card')).toHaveCount(5)
  await expect(page.locator('.chart-legend-item')).toHaveCount(4)
  await expect(page.locator('.chart-legend-bar')).toContainText('化成测试5 · 同名电池')
  await page.locator('.curve-cycle-select').click()
  await page.getByRole('option', { name: '循环3', exact: true }).click()
  await page.getByPlaceholder('请输入报表名称').fill('页面化成打印')
  const colorPicker = page.locator('.chart-legend-item').filter({ hasText: '化成测试2' }).locator('.el-color-picker')
  await colorPicker.click()
  const colorPanel = page.locator('.el-color-picker__panel:visible')
  await colorPanel.locator('input').fill('#123456')
  await colorPanel.getByRole('button', { name: /确定|确认|OK/ }).click()
  await page.getByRole('button', { name: '保存报表', exact: true }).click()
  await expect(page).toHaveURL(/mode=view/)
  const id = Number(page.url().match(/formation\/(\d+)/)![1])
  const v1 = (await api(page, `formation-report/${id}`)).data
  expect(v1.settings.seriesColors['8902|8902']).toBe('#123456')
  expect(v1.items).toHaveLength(4)
@@ -204,7 +197,7 @@
  `)
  const created = (await api(page, 'formation-report', 'POST', { name: '旧版化成', description: '', selections: [{ testId: 8990, testBatteryId: 8990 }], settings: { ...settings, values: {}, seriesColors: {} } })).data
  // 仅在隔离库构造早期快照,模拟当时尚未保存曲线与工步选择的报表。
  stack.executeIsolatedSql(`UPDATE t_formation_report_version SET snapshot_json=JSON_REMOVE(snapshot_json,'$.items[0].curves','$.items[0].chargeSteps','$.settings.metrics','$.settings.seriesColors') WHERE report_id=${created.id}`)
  stack.executeIsolatedSql(`UPDATE t_formation_report_version SET snapshot_json=JSON_REMOVE(snapshot_json,'$.curveMode','$.items[0].stepCurves','$.items[0].curves','$.items[0].chargeSteps','$.settings.metrics','$.settings.seriesColors') WHERE report_id=${created.id}`)
  const old = (await api(page, `formation-report/${created.id}`)).data
  await page.context().addInitScript(() => { window.print = () => { document.body.dataset.printRequested = 'true' } })
  await page.goto(`/report-formation/${created.id}/edit?mode=view`)
tests/e2e/formation-matrix.spec.ts
@@ -61,7 +61,8 @@
  await expect(page.getByRole('textbox', { name: '化成测试甲 同名电池 循环1 充电容量', exact: true })).toHaveValue('8512.2000')
  await expect(page.getByRole('textbox', { name: '化成测试甲 同名电池 循环2 充电容量', exact: true })).toHaveAttribute('placeholder', '指标缺失')
  await expect(page.getByRole('textbox', { name: '化成测试甲 同名电池 循环1 放电容量', exact: true })).toHaveValue('0.0000')
  await expect(page.locator('[data-cycle="4"] .missing-cycle')).toHaveCount(17)
  await expect(page.locator('[data-cycle="4"] .missing-cycle').first()).toHaveText('无该循环')
  await expect(page.locator('[data-cycle="4"] .charge-termination-row')).toContainText('无该循环')
  await expect(page.getByRole('textbox', { name: '化成测试甲 同名电池 F/N', exact: true })).toHaveValue('')
  await expect(page.getByRole('textbox', { name: '化成测试甲 同名电池 F', exact: true })).toHaveValue('12.5')
  await expect(page.getByText('自动计算', { exact: true })).toHaveCount(0)
tests/e2e/formation-step-curves.spec.ts
@@ -84,23 +84,24 @@
  await expect(page.locator('.cycle-card[data-cycle="1"]')).toContainText('4.222 V')
  await expect(page.locator('.cycle-card[data-cycle="3"] .charge-termination-row').nth(1)).toContainText('缺失')
  const unknown = page.getByRole('combobox', { name: '未知充电工步 工步测试8605 同名电池 循环1', exact: true })
  await unknown.click()
  await unknown.evaluate(element => element.scrollIntoView({ block: 'center' }))
  await page.locator('.el-select').filter({ has: unknown }).click()
  for (const step of [1, 2]) await page.getByRole('option', { name: `${step} 实际工步${step}`, exact: true }).click()
  await unknown.press('Escape')
  await expect(page.locator('.cycle-card[data-cycle="1"] .charge-termination-row').nth(1).locator('td').last()).toContainText('4.222 V')
  const chargeChoice = page.getByRole('combobox', { name: '充电工步 工步测试8601 同名电池', exact: true })
  await chargeChoice.click()
  await page.getByRole('option', { name: '1 实际工步1', exact: true }).click()
  await page.locator('.el-select').filter({ has: chargeChoice }).click()
  await page.locator(`#${await chargeChoice.getAttribute('aria-controls')}`).getByRole('option', { name: '1 实际工步1', exact: true }).click()
  await chargeChoice.press('Escape')
  await expect(page.locator('.cycle-card[data-cycle="1"]')).toContainText('4.111 V')
  const legend = page.locator('[data-group="charge"] .chart-legend-item').filter({ hasText: '工步测试8601 · 工步甲组 / 同名电池 · 循环1 工步2' })
  await legend.getByRole('button').click()
  await expect(legend.getByRole('button')).toHaveAttribute('aria-pressed', 'false')
  await legend.locator('button[aria-pressed]').click()
  await expect(legend.locator('button[aria-pressed]')).toHaveAttribute('aria-pressed', 'false')
  await legend.locator('.el-color-picker').click()
  const panel = page.locator('.el-color-picker__panel:visible')
  await panel.locator('input').fill('#123456')
  await panel.getByRole('button', { name: /确定|确认|OK/ }).click()
  await expect(legend.getByRole('button')).toHaveAttribute('aria-pressed', 'false')
  await expect(legend.locator('button[aria-pressed]')).toHaveAttribute('aria-pressed', 'false')
  await page.getByPlaceholder('请输入报表名称').fill('化成工步独立验收')
  await page.getByRole('button', { name: '保存报表', exact: true }).click()
  await expect(page).toHaveURL(/mode=view/)
@@ -156,8 +157,8 @@
  await api(page, `formation-report/${id}/versions`, 'POST', { name: '陈旧', description: '', baseVersionNo: 1, changeSummary: '陈旧拒绝', settings: v1.settings }, 500)
  stack.executeIsolatedSql('UPDATE t_test_step_record SET voltage_v=9 WHERE test_id=8601; UPDATE t_test_realtime_record SET current_a=99 WHERE test_id=8601')
  const v3 = await api(page, `formation-report/${id}/refresh-source`, 'POST', { baseVersionNo: 2, changeSummary: '主动重读' })
  expect(v3.items[0].stepCurves['1']['1'].records.every((row: any) => row.current === 99)).toBe(true)
  expect(v3.items[0].chargeSteps['1'][0].endVoltage).toBe(9)
  expect(v3.items.find((item: any) => item.source.selection.testBatteryId === 8601).stepCurves['1']['1'].records.every((row: any) => row.current === 99)).toBe(true)
  expect(v3.items.find((item: any) => item.source.selection.testBatteryId === 8601).chargeSteps['1'][0].endVoltage).toBe(9)
  expect((await api(page, `formation-report/${id}/versions/1`)).items).toEqual(v1.items)
  for (const sourceId of [8601, 8603, 8604, 8605]) await api(page, `test/${sourceId}`, 'DELETE')
  await api(page, `formation-report/${id}/refresh-source`, 'POST', { baseVersionNo: 3, changeSummary: '已删源拒绝' }, 500)