From 846b8c601bcbb0623ce378a8a18992632f9fda01 Mon Sep 17 00:00:00 2001 From: WuYongJie <549727955@qq.com> Date: Mon, 28 Sep 2026 20:28:48 +0800 Subject: [PATCH] fix: use report title as certificate material name --- .../certificate-report/init/route.test.ts | 7 ++++ .../upload/certificate-report/init/route.ts | 5 ++- app/share/[id]/page.tsx | 1 - components/home/MaterialCard.test.tsx | 8 ++++ components/home/MaterialCard.tsx | 1 - .../upload/CertificateReportUploader.test.tsx | 20 ++++++++++ .../upload/CertificateReportUploader.tsx | 39 ++++++++++--------- lib/__tests__/certificate-report.test.ts | 6 ++- lib/certificate-report.ts | 8 +--- 9 files changed, 66 insertions(+), 29 deletions(-) diff --git a/app/api/materials/upload/certificate-report/init/route.test.ts b/app/api/materials/upload/certificate-report/init/route.test.ts index b71015e..a6556cc 100644 --- a/app/api/materials/upload/certificate-report/init/route.test.ts +++ b/app/api/materials/upload/certificate-report/init/route.test.ts @@ -63,6 +63,13 @@ describe('检测报告初始化', () => { expect(prisma.material.create).not.toHaveBeenCalled() }) + test('成品即使有 SPU,上传时也必须填写报告名称', async () => { + const response = await POST(request({ ...input, reportKind: '成品', productModel: 'SPU-001', reportTitle: ' ' })) + expect(response.status).toBe(400) + await expect(response.json()).resolves.toEqual({ error: '请填写报告名称 / 检测对象' }) + expect(prisma.material.create).not.toHaveBeenCalled() + }) + test('到期时间早于检测日期时不写入素材', async () => { const response = await POST(request({ ...input, certificateExpiresOn: '2027-09-22' })) expect(response.status).toBe(400) diff --git a/app/api/materials/upload/certificate-report/init/route.ts b/app/api/materials/upload/certificate-report/init/route.ts index c5baa8a..00b6210 100644 --- a/app/api/materials/upload/certificate-report/init/route.ts +++ b/app/api/materials/upload/certificate-report/init/route.ts @@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server' import { getServerSession } from 'next-auth' import { authOptions } from '@/lib/auth' import { prisma } from '@/lib/prisma' -import { CERTIFICATE_REPORT_MATERIAL_TYPE, normalizeReportTitle, validateCertificateReportMetadata } from '@/lib/certificate-report' +import { CERTIFICATE_REPORT_KIND, CERTIFICATE_REPORT_MATERIAL_TYPE, normalizeReportTitle, validateCertificateReportMetadata } from '@/lib/certificate-report' import { INCOMPLETE_SHELL_CREATE_DATA } from '@/lib/material-query' export async function POST(request: NextRequest) { @@ -17,6 +17,9 @@ export async function POST(request: NextRequest) { const reportTitle = normalizeReportTitle(body.reportTitle) const productCategoryName = String(body.productCategoryName || '').trim() const platformName = String(body.platformName || '').trim() + if (body.reportKind === CERTIFICATE_REPORT_KIND.FINISHED && !reportTitle) { + return NextResponse.json({ error: '请填写报告名称 / 检测对象' }, { status: 400 }) + } const metadata = validateCertificateReportMetadata({ reportKind: body.reportKind, productModel, diff --git a/app/share/[id]/page.tsx b/app/share/[id]/page.tsx index 90139d1..1bd39e5 100644 --- a/app/share/[id]/page.tsx +++ b/app/share/[id]/page.tsx @@ -594,7 +594,6 @@ export default function SharePage() { ? getCertificateReportDisplayTitle({ productModel: material.productModel, reportTitle: material.reportTitle, - reportKind: material.reportKind, }) : '' diff --git a/components/home/MaterialCard.test.tsx b/components/home/MaterialCard.test.tsx index 271544b..5108d6c 100644 --- a/components/home/MaterialCard.test.tsx +++ b/components/home/MaterialCard.test.tsx @@ -156,6 +156,14 @@ describe('MaterialCard', () => { expect(notice.parentElement).not.toHaveClass('bg-[#fafafa]/55') }) + test.each([ + ['成品检测报告', '成品检测报告'], + [null, 'SPU-001'], + ])('检测报告卡片优先使用报告名称,缺失时保留旧 SPU (%s)', (reportTitle, expectedTitle) => { + render() + expect(screen.getByRole('heading', { level: 3 })).toHaveTextContent(expectedTitle) + }) + test('视频卡片初始只加载封面,悬停时才挂载视频预览', () => { const { container } = render( ({ chunkUpload: jest.fn() })) +test('成品只填写 SPU 时不能上传,须先填写报告名称', () => { + const originalFetch = global.fetch + global.fetch = jest.fn() + try { + render() + fireEvent.change(screen.getByLabelText('SPU'), { target: { value: 'SPU-001' } }) + fireEvent.click(screen.getByRole('button', { name: '创建分享素材' })) + expect(screen.getByText('请填写报告名称 / 检测对象')).toBeInTheDocument() + expect(global.fetch).not.toHaveBeenCalled() + } finally { + global.fetch = originalFetch + } +}) + test.each([['成品', true], ['成品', false], ['原材料', true], ['原材料', false]] as const)('%s上传说明与日期:手动到期时间 %s', async (kind, manualExpiry) => { const originalFetch = global.fetch jest.useFakeTimers().setSystemTime(new Date('2026-09-27T12:00:00')) @@ -12,6 +26,12 @@ test.each([['成品', true], ['成品', false], ['原材料', true], ['原材料 try { expect(screen.getByLabelText('成品')).toBeChecked() fireEvent.click(screen.getByLabelText(kind)) + const reportTitleInput = screen.getByLabelText('报告名称 / 检测对象 *') + expect(reportTitleInput).toBeRequired() + if (kind === '成品') { + expect(reportTitleInput.compareDocumentPosition(screen.getByLabelText('SPU')) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy() + } + expect(screen.getByText('上传后将作为素材名称显示')).toBeInTheDocument() fireEvent.change(screen.getByLabelText(/报告名称 \/ 检测对象/), { target: { value: '板材检测报告' } }) fireEvent.change(screen.getByLabelText('品类 *'), { target: { value: '板材' } }) fireEvent.change(screen.getByLabelText('证书编号 *'), { target: { value: 'CMA-001' } }) diff --git a/components/upload/CertificateReportUploader.tsx b/components/upload/CertificateReportUploader.tsx index c1ec989..bfb4d60 100644 --- a/components/upload/CertificateReportUploader.tsx +++ b/components/upload/CertificateReportUploader.tsx @@ -122,6 +122,10 @@ export default function CertificateReportUploader({ const handleSubmit = async () => { const submittedProductModel = reportKind === CERTIFICATE_REPORT_KIND.RAW ? '' : productModel.trim() + if (reportKind === CERTIFICATE_REPORT_KIND.FINISHED && !reportTitle.trim()) { + setError('请填写报告名称 / 检测对象') + return + } const metadata = validateCertificateReportMetadata({ reportKind, productModel: submittedProductModel, @@ -280,6 +284,23 @@ export default function CertificateReportUploader({
+
+ + setReportTitle(event.target.value)} + placeholder="例如:E0级颗粒板检测报告" + className={fieldControlClass} + required + /> +

+ 上传后将作为素材名称显示 +

+
{reportKind === CERTIFICATE_REPORT_KIND.FINISHED && (
@@ -293,24 +314,6 @@ export default function CertificateReportUploader({ />
)} - -
- - setReportTitle(event.target.value)} - placeholder="例如:E0级颗粒板检测报告" - className={fieldControlClass} - required={reportKind === CERTIFICATE_REPORT_KIND.RAW} - /> -

- {reportKind === CERTIFICATE_REPORT_KIND.RAW ? '原材料检测报告必须填写报告名称' : 'SPU 和这个字段至少填写一个'} -

-
diff --git a/lib/__tests__/certificate-report.test.ts b/lib/__tests__/certificate-report.test.ts index 377a6fd..19e91c0 100644 --- a/lib/__tests__/certificate-report.test.ts +++ b/lib/__tests__/certificate-report.test.ts @@ -41,12 +41,16 @@ describe('检测报告字段规则', () => { reportTitle: '板材检测报告', })).toHaveProperty('value.reportKind', '原材料') expect(getCertificateReportDisplayTitle({ - reportKind: '原材料', productModel: '旧 SPU', reportTitle: '板材检测报告', })).toBe('板材检测报告') }) + test('成品有报告名称时优先显示,旧记录缺少名称时显示 SPU', () => { + expect(getCertificateReportDisplayTitle({ productModel: 'SPU-001', reportTitle: '成品检测报告' })).toBe('成品检测报告') + expect(getCertificateReportDisplayTitle({ productModel: 'SPU-001', reportTitle: null })).toBe('SPU-001') + }) + test('检测日期必须是真实的 YYYY-MM-DD 日期', () => { expect(parseCertificateDate('2027-02-28')?.toISOString().slice(0, 10)).toBe('2027-02-28') expect(parseCertificateDate('2027-02-30')).toBeNull() diff --git a/lib/certificate-report.ts b/lib/certificate-report.ts index db9945f..c45f9eb 100644 --- a/lib/certificate-report.ts +++ b/lib/certificate-report.ts @@ -153,21 +153,15 @@ export function validateCertificateReportMetadata(input: { export function getCertificateReportDisplayTitle(input: { productModel?: string | null reportTitle?: string | null - reportKind?: string | null }): string { const productModel = String(input.productModel || '').trim() const reportTitle = String(input.reportTitle || '').trim() - if (input.reportKind === CERTIFICATE_REPORT_KIND.RAW && reportTitle) { + if (reportTitle) { return reportTitle } if (productModel) { return productModel } - - if (reportTitle) { - return reportTitle - } - return '未命名报告' }