feat: simplify report instructions and restore certificate expiry date
This commit is contained in:
parent
41ecfb6d36
commit
cb510075fc
@ -169,7 +169,7 @@ export async function GET(
|
||||
reportKind: true,
|
||||
certificateTestedOn: true,
|
||||
certificateNumber: true,
|
||||
certificateUsageInstructions: true,
|
||||
certificateExpiresOn: true,
|
||||
reportUsageRules: true,
|
||||
liveDate: true,
|
||||
liveHost: true,
|
||||
@ -251,7 +251,7 @@ export async function GET(
|
||||
reportKind: true,
|
||||
certificateTestedOn: true,
|
||||
certificateNumber: true,
|
||||
certificateUsageInstructions: true,
|
||||
certificateExpiresOn: true,
|
||||
reportUsageRules: true,
|
||||
liveDate: true,
|
||||
liveHost: true,
|
||||
@ -387,7 +387,7 @@ export async function GET(
|
||||
reportKind: materialWithUser.reportKind || null,
|
||||
certificateTestedOn: materialWithUser.certificateTestedOn || null,
|
||||
certificateNumber: materialWithUser.certificateNumber || null,
|
||||
certificateUsageInstructions: materialWithUser.certificateUsageInstructions || null,
|
||||
certificateExpiresOn: materialWithUser.certificateExpiresOn || null,
|
||||
reportUsageRules: materialWithUser.reportUsageRules || null,
|
||||
categoryName: materialWithUser.categoryName,
|
||||
sceneName: materialWithUser.sceneName,
|
||||
@ -481,7 +481,7 @@ export async function GET(
|
||||
reportKind: materialWithUser.reportKind || null,
|
||||
certificateTestedOn: materialWithUser.certificateTestedOn || null,
|
||||
certificateNumber: materialWithUser.certificateNumber || null,
|
||||
certificateUsageInstructions: materialWithUser.certificateUsageInstructions || null,
|
||||
certificateExpiresOn: materialWithUser.certificateExpiresOn || null,
|
||||
reportUsageRules: materialWithUser.reportUsageRules || null,
|
||||
categoryName: materialWithUser.categoryName,
|
||||
sceneName: materialWithUser.sceneName,
|
||||
@ -617,7 +617,7 @@ export async function PUT(
|
||||
...(formData.has('reportKind') ? { reportKind: formData.get('reportKind') as string | null } : {}),
|
||||
...(formData.has('certificateTestedOn') ? { certificateTestedOn: formData.get('certificateTestedOn') as string | null } : {}),
|
||||
...(formData.has('certificateNumber') ? { certificateNumber: formData.get('certificateNumber') as string | null } : {}),
|
||||
...(formData.has('certificateUsageInstructions') ? { certificateUsageInstructions: formData.get('certificateUsageInstructions') as string | null } : {}),
|
||||
...(formData.has('certificateExpiresOn') ? { certificateExpiresOn: formData.get('certificateExpiresOn') as string | null } : {}),
|
||||
...(formData.has('reportUsageRules') ? { reportUsageRules: formData.get('reportUsageRules') as string | null } : {}),
|
||||
categoryName: formData.get('categoryName') as string | null,
|
||||
sceneName: formData.get('sceneName') as string | null,
|
||||
|
||||
@ -128,7 +128,7 @@ export async function GET(request: NextRequest) {
|
||||
reportKind: true,
|
||||
certificateTestedOn: true,
|
||||
certificateNumber: true,
|
||||
certificateUsageInstructions: true,
|
||||
certificateExpiresOn: true,
|
||||
reportUsageRules: true,
|
||||
liveDate: true,
|
||||
liveHost: true,
|
||||
@ -364,7 +364,7 @@ export async function GET(request: NextRequest) {
|
||||
reportKind: (material as any).reportKind || null,
|
||||
certificateTestedOn: (material as any).certificateTestedOn || null,
|
||||
certificateNumber: (material as any).certificateNumber || null,
|
||||
certificateUsageInstructions: (material as any).certificateUsageInstructions || null,
|
||||
certificateExpiresOn: (material as any).certificateExpiresOn || null,
|
||||
reportUsageRules: (material as any).reportUsageRules || null,
|
||||
isAiGenerated: Boolean((material as any).isAiGenerated),
|
||||
taskDetailSourceType: (material as any).taskDetailSourceType || null,
|
||||
|
||||
@ -48,7 +48,7 @@ export async function PUT(request: NextRequest) {
|
||||
...(formData.has('reportKind') ? { reportKind: formData.get('reportKind') as string | null } : {}),
|
||||
...(formData.has('certificateTestedOn') ? { certificateTestedOn: formData.get('certificateTestedOn') as string | null } : {}),
|
||||
...(formData.has('certificateNumber') ? { certificateNumber: formData.get('certificateNumber') as string | null } : {}),
|
||||
...(formData.has('certificateUsageInstructions') ? { certificateUsageInstructions: formData.get('certificateUsageInstructions') as string | null } : {}),
|
||||
...(formData.has('certificateExpiresOn') ? { certificateExpiresOn: formData.get('certificateExpiresOn') as string | null } : {}),
|
||||
...(formData.has('reportUsageRules') ? { reportUsageRules: formData.get('reportUsageRules') as string | null } : {}),
|
||||
categoryName: formData.get('categoryName') as string | null,
|
||||
sceneName: formData.get('sceneName') as string | null,
|
||||
|
||||
@ -22,7 +22,7 @@ const input = {
|
||||
platformName: '品牌',
|
||||
certificateTestedOn: '2027-09-23',
|
||||
certificateNumber: 'CMA-2026-001',
|
||||
certificateUsageInstructions: '使用前核对送检主体',
|
||||
certificateExpiresOn: '2028-09-23',
|
||||
reportUsageRules: '完整说明\n第二行',
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ describe('检测报告初始化', () => {
|
||||
reportKind: '原材料',
|
||||
certificateTestedOn: new Date('2027-09-23T00:00:00.000Z'),
|
||||
certificateNumber: 'CMA-2026-001',
|
||||
certificateUsageInstructions: '使用前核对送检主体',
|
||||
certificateExpiresOn: new Date('2028-09-23T00:00:00.000Z'),
|
||||
reportUsageRules: '完整说明\n第二行',
|
||||
platformName: '品牌',
|
||||
}),
|
||||
@ -62,4 +62,10 @@ describe('检测报告初始化', () => {
|
||||
expect(response.status).toBe(400)
|
||||
expect(prisma.material.create).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('到期时间早于检测日期时不写入素材', async () => {
|
||||
const response = await POST(request({ ...input, certificateExpiresOn: '2027-09-22' }))
|
||||
expect(response.status).toBe(400)
|
||||
expect(prisma.material.create).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@ -23,7 +23,7 @@ export async function POST(request: NextRequest) {
|
||||
reportTitle,
|
||||
certificateTestedOn: body.certificateTestedOn,
|
||||
certificateNumber: body.certificateNumber,
|
||||
certificateUsageInstructions: body.certificateUsageInstructions,
|
||||
certificateExpiresOn: body.certificateExpiresOn,
|
||||
reportUsageRules: body.reportUsageRules,
|
||||
platformName,
|
||||
})
|
||||
|
||||
@ -7,7 +7,7 @@ import Header from '@/components/Header'
|
||||
import SearchableUserSelect from '@/components/SearchableUserSelect'
|
||||
import { useConfirmActionDialog } from '@/components/ConfirmActionDialog'
|
||||
import TaskReferenceFields from '@/components/upload/TaskReferenceFields'
|
||||
import CertificateTestDatePicker from '@/components/upload/CertificateTestDatePicker'
|
||||
import CertificateDatePicker from '@/components/upload/CertificateDatePicker'
|
||||
import {
|
||||
canManuallyReplacePreview,
|
||||
getSelectableMaterialTypeOptions,
|
||||
@ -38,7 +38,7 @@ interface Material {
|
||||
reportKind?: string | null
|
||||
certificateTestedOn?: string | Date | null
|
||||
certificateNumber?: string | null
|
||||
certificateUsageInstructions?: string | null
|
||||
certificateExpiresOn?: string | null
|
||||
reportUsageRules?: string | null
|
||||
liveDate?: Date | string | null
|
||||
liveHost?: string | null
|
||||
@ -111,7 +111,7 @@ export default function EditMaterialPage({ params }: { params: { id: string } })
|
||||
const [reportKind, setReportKind] = useState('')
|
||||
const [certificateTestedOn, setCertificateTestedOn] = useState('')
|
||||
const [certificateNumber, setCertificateNumber] = useState('')
|
||||
const [certificateUsageInstructions, setCertificateUsageInstructions] = useState('')
|
||||
const [certificateExpiresOn, setCertificateExpiresOn] = useState('')
|
||||
const [reportUsageRules, setReportUsageRules] = useState('')
|
||||
const [liveDate, setLiveDate] = useState('')
|
||||
const [liveHost, setLiveHost] = useState('')
|
||||
@ -163,7 +163,7 @@ export default function EditMaterialPage({ params }: { params: { id: string } })
|
||||
reportKind,
|
||||
certificateTestedOn,
|
||||
certificateNumber,
|
||||
certificateUsageInstructions,
|
||||
certificateExpiresOn,
|
||||
reportUsageRules,
|
||||
categoryName,
|
||||
sceneName,
|
||||
@ -191,7 +191,7 @@ export default function EditMaterialPage({ params }: { params: { id: string } })
|
||||
reportKind,
|
||||
certificateTestedOn,
|
||||
certificateNumber,
|
||||
certificateUsageInstructions,
|
||||
certificateExpiresOn,
|
||||
reportUsageRules,
|
||||
sceneName,
|
||||
taskDetailRequesterUserId,
|
||||
@ -306,7 +306,7 @@ export default function EditMaterialPage({ params }: { params: { id: string } })
|
||||
setReportKind(data.reportKind || '')
|
||||
setCertificateTestedOn(data.certificateTestedOn ? String(data.certificateTestedOn).slice(0, 10) : '')
|
||||
setCertificateNumber(data.certificateNumber || '')
|
||||
setCertificateUsageInstructions(data.certificateUsageInstructions || '')
|
||||
setCertificateExpiresOn(data.certificateExpiresOn ? String(data.certificateExpiresOn).slice(0, 10) : '')
|
||||
setReportUsageRules(data.reportUsageRules || (isCertificateReportMaterialType(data.materialType) ? DEFAULT_CERTIFICATE_REPORT_USAGE_RULES : ''))
|
||||
setLiveDate(data.liveDate ? String(data.liveDate).slice(0, 10) : '')
|
||||
setLiveHost(data.liveHost || '')
|
||||
@ -349,7 +349,7 @@ export default function EditMaterialPage({ params }: { params: { id: string } })
|
||||
reportKind: data.reportKind || '',
|
||||
certificateTestedOn: data.certificateTestedOn ? String(data.certificateTestedOn).slice(0, 10) : '',
|
||||
certificateNumber: data.certificateNumber || '',
|
||||
certificateUsageInstructions: data.certificateUsageInstructions || '',
|
||||
certificateExpiresOn: data.certificateExpiresOn ? String(data.certificateExpiresOn).slice(0, 10) : '',
|
||||
reportUsageRules: data.reportUsageRules || (isCertificateReportMaterialType(data.materialType) ? DEFAULT_CERTIFICATE_REPORT_USAGE_RULES : ''),
|
||||
categoryName: data.categoryName || '',
|
||||
sceneName: data.sceneName || '',
|
||||
@ -526,7 +526,7 @@ export default function EditMaterialPage({ params }: { params: { id: string } })
|
||||
reportTitle,
|
||||
certificateTestedOn,
|
||||
certificateNumber,
|
||||
certificateUsageInstructions,
|
||||
certificateExpiresOn,
|
||||
reportUsageRules,
|
||||
platformName,
|
||||
}, material?.materialType === materialType ? material?.platformName || '' : '')
|
||||
@ -592,7 +592,7 @@ export default function EditMaterialPage({ params }: { params: { id: string } })
|
||||
formData.append('reportKind', reportKind)
|
||||
formData.append('certificateTestedOn', certificateTestedOn)
|
||||
formData.append('certificateNumber', certificateNumber)
|
||||
formData.append('certificateUsageInstructions', certificateUsageInstructions)
|
||||
formData.append('certificateExpiresOn', certificateExpiresOn)
|
||||
formData.append('reportUsageRules', reportUsageRules)
|
||||
}
|
||||
if (materialType === '直播切片') {
|
||||
@ -1065,17 +1065,17 @@ export default function EditMaterialPage({ params }: { params: { id: string } })
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div>
|
||||
<label htmlFor={`edit-material-${params.id}-certificate-tested-on`} className="mb-2 block text-sm font-medium text-gray-700">证书检测日期 *</label>
|
||||
<CertificateTestDatePicker id={`edit-material-${params.id}-certificate-tested-on`} value={certificateTestedOn} onChange={setCertificateTestedOn} disabled={submitting} />
|
||||
<CertificateDatePicker id={`edit-material-${params.id}-certificate-tested-on`} label="证书检测日期" value={certificateTestedOn} onChange={setCertificateTestedOn} disabled={submitting} />
|
||||
<p className="mt-1 text-xs text-gray-500">请按报告标注的检测日期选择</p>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor={`edit-material-${params.id}-certificate-number`} className="mb-2 block text-sm font-medium text-gray-700">证书编号 *</label>
|
||||
<input id={`edit-material-${params.id}-certificate-number`} type="text" value={certificateNumber} onChange={(event) => setCertificateNumber(event.target.value)} maxLength={120} className="input-apple" required />
|
||||
<label htmlFor={`edit-material-${params.id}-certificate-expires-on`} className="mb-2 block text-sm font-medium text-gray-700">证书到期时间 *</label>
|
||||
<CertificateDatePicker id={`edit-material-${params.id}-certificate-expires-on`} label="证书到期时间" value={certificateExpiresOn} onChange={setCertificateExpiresOn} disabled={submitting} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor={`edit-material-${params.id}-certificate-instructions`} className="mb-2 block text-sm font-medium text-gray-700">证书使用说明 *</label>
|
||||
<textarea id={`edit-material-${params.id}-certificate-instructions`} value={certificateUsageInstructions} onChange={(event) => setCertificateUsageInstructions(event.target.value)} rows={3} maxLength={5000} className="input-apple resize-y" required />
|
||||
<label htmlFor={`edit-material-${params.id}-certificate-number`} className="mb-2 block text-sm font-medium text-gray-700">证书编号 *</label>
|
||||
<input id={`edit-material-${params.id}-certificate-number`} type="text" value={certificateNumber} onChange={(event) => setCertificateNumber(event.target.value)} maxLength={120} className="input-apple" required />
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor={`edit-material-${params.id}-report-rules`} className="mb-2 block text-sm font-medium text-gray-700">检测报告使用规则说明 *</label>
|
||||
|
||||
@ -270,8 +270,9 @@ describe('certificate report share page', () => {
|
||||
render(<SharePage />)
|
||||
expect(await screen.findByText('CMA-2026-001')).toBeInTheDocument()
|
||||
expect(screen.getByText('2027-09-23')).toBeInTheDocument()
|
||||
expect(screen.queryByText('2028-09-23')).not.toBeInTheDocument()
|
||||
expect(screen.getByText('使用前核对主体')).toBeInTheDocument()
|
||||
expect(screen.getByText('2028-09-23')).toBeInTheDocument()
|
||||
expect(screen.queryByRole('heading', { name: '证书使用说明' })).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('使用前核对主体')).not.toBeInTheDocument()
|
||||
expect(screen.getByRole('heading', { name: '检测报告使用规则说明' }).nextElementSibling?.textContent)
|
||||
.toBe('第一段\n第二段')
|
||||
})
|
||||
|
||||
@ -41,7 +41,7 @@ interface Material {
|
||||
reportKind?: string | null
|
||||
certificateTestedOn?: string | null
|
||||
certificateNumber?: string | null
|
||||
certificateUsageInstructions?: string | null
|
||||
certificateExpiresOn?: string | null
|
||||
reportUsageRules?: string | null
|
||||
liveDate?: string | Date | null
|
||||
liveHost?: string | null
|
||||
@ -2085,6 +2085,10 @@ export default function SharePage() {
|
||||
<p className="mb-1 text-[12px] text-[#86898e]">证书检测日期</p>
|
||||
<p className="font-en text-[15px] font-medium tabular-nums text-[#1a1c1d]">{material.certificateTestedOn ? String(material.certificateTestedOn).slice(0, 10) : '待补充'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="mb-1 text-[12px] text-[#86898e]">证书到期时间</p>
|
||||
<p className="font-en text-[15px] font-medium tabular-nums text-[#1a1c1d]">{material.certificateExpiresOn ? String(material.certificateExpiresOn).slice(0, 10) : '待补充'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="mb-1 text-[12px] text-[#86898e]">证书编号</p>
|
||||
<p className="break-all text-[15px] font-medium text-[#1a1c1d]">{material.certificateNumber || '待补充'}</p>
|
||||
@ -2244,15 +2248,9 @@ export default function SharePage() {
|
||||
</div>
|
||||
{isCertificateReportMaterialType(material.materialType) && (
|
||||
<section className="mt-6 rounded-[12px] border border-[#e7e8ea] bg-white px-5 py-6 sm:px-7" aria-label="检测报告使用说明">
|
||||
<div className="max-w-[75ch] space-y-6">
|
||||
<div>
|
||||
<h2 className="text-[17px] font-semibold text-[#1a1c1d]">证书使用说明</h2>
|
||||
<p className="mt-3 whitespace-pre-wrap break-words text-[14px] leading-7 text-[#3d4246]">{material.certificateUsageInstructions || '待补充'}</p>
|
||||
</div>
|
||||
<div className="border-t border-[#e7e8ea] pt-6">
|
||||
<h2 className="text-[17px] font-semibold text-[#1a1c1d]">检测报告使用规则说明</h2>
|
||||
<p className="mt-3 whitespace-pre-wrap break-words text-[14px] leading-7 text-[#3d4246]">{material.reportUsageRules || '待补充'}</p>
|
||||
</div>
|
||||
<div className="max-w-[75ch]">
|
||||
<h2 className="text-[17px] font-semibold text-[#1a1c1d]">检测报告使用规则说明</h2>
|
||||
<p className="mt-3 whitespace-pre-wrap break-words text-[14px] leading-7 text-[#3d4246]">{material.reportUsageRules || '待补充'}</p>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
@ -1,9 +1,27 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import CertificateTestDatePicker from '@/components/upload/CertificateTestDatePicker'
|
||||
import CertificateDatePicker from '@/components/upload/CertificateDatePicker'
|
||||
|
||||
describe('CertificateDatePicker', () => {
|
||||
test('检测日期和到期时间分别选择,日历使用独立的名称和状态', () => {
|
||||
const onTested = jest.fn()
|
||||
const onExpires = jest.fn()
|
||||
render(<>
|
||||
<CertificateDatePicker id="tested-on" label="证书检测日期" value="2026-09-23" onChange={onTested} />
|
||||
<CertificateDatePicker id="expires-on" label="证书到期时间" value="2027-09-23" onChange={onExpires} />
|
||||
</>)
|
||||
const expires = screen.getByRole('button', { name: /证书到期时间/ })
|
||||
fireEvent.click(expires)
|
||||
expect(screen.getByRole('dialog', { name: '选择证书到期时间' })).toBeInTheDocument()
|
||||
fireEvent.click(expires)
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
||||
fireEvent.click(expires)
|
||||
fireEvent.click(screen.getByRole('button', { name: '2027年9月24日' }))
|
||||
expect(onExpires).toHaveBeenCalledWith('2027-09-24')
|
||||
expect(onTested).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
describe('CertificateTestDatePicker', () => {
|
||||
test('日历图标可反复展开和收起,Escape 也会关闭', () => {
|
||||
render(<CertificateTestDatePicker id="tested-on" value="2026-09-23" onChange={jest.fn()} />)
|
||||
render(<CertificateDatePicker id="tested-on" label="证书检测日期" value="2026-09-23" onChange={jest.fn()} />)
|
||||
|
||||
const trigger = screen.getByRole('button', { name: /证书检测日期/ })
|
||||
const icon = trigger.querySelector('svg')
|
||||
@ -25,7 +43,7 @@ describe('CertificateTestDatePicker', () => {
|
||||
|
||||
test('按日历选择日期时返回统一格式,并关闭日历', () => {
|
||||
const onChange = jest.fn()
|
||||
render(<CertificateTestDatePicker id="tested-on" value="2026-09-23" onChange={onChange} />)
|
||||
render(<CertificateDatePicker id="tested-on" label="证书检测日期" value="2026-09-23" onChange={onChange} />)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /证书检测日期/ }))
|
||||
expect(screen.getByText('2026年9月')).toBeInTheDocument()
|
||||
@ -39,7 +57,7 @@ describe('CertificateTestDatePicker', () => {
|
||||
|
||||
test('点击日历外部会关闭,不改动已选择的日期', () => {
|
||||
const onChange = jest.fn()
|
||||
render(<CertificateTestDatePicker id="tested-on" value="2026-09-23" onChange={onChange} />)
|
||||
render(<CertificateDatePicker id="tested-on" label="证书检测日期" value="2026-09-23" onChange={onChange} />)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /证书检测日期/ }))
|
||||
fireEvent.pointerDown(document.body)
|
||||
@ -50,7 +68,7 @@ describe('CertificateTestDatePicker', () => {
|
||||
|
||||
test('可以直接切换年份和月份选择较远的检测日期', () => {
|
||||
const onChange = jest.fn()
|
||||
render(<CertificateTestDatePicker id="tested-on" value="2026-09-23" onChange={onChange} />)
|
||||
render(<CertificateDatePicker id="tested-on" label="证书检测日期" value="2026-09-23" onChange={onChange} />)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /证书检测日期/ }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '选择年份和月份' }))
|
||||
@ -12,14 +12,15 @@ function parseDate(value: string): Date | null {
|
||||
return isValid(date) && format(date, 'yyyy-MM-dd') === value ? date : null
|
||||
}
|
||||
|
||||
interface CertificateTestDatePickerProps {
|
||||
interface CertificateDatePickerProps {
|
||||
id: string
|
||||
label: string
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export default function CertificateTestDatePicker({ id, value, onChange, disabled = false }: CertificateTestDatePickerProps) {
|
||||
export default function CertificateDatePicker({ id, label, value, onChange, disabled = false }: CertificateDatePickerProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const triggerRef = useRef<HTMLButtonElement>(null)
|
||||
const calendarRef = useRef<HTMLDivElement>(null)
|
||||
@ -91,7 +92,7 @@ export default function CertificateTestDatePicker({ id, value, onChange, disable
|
||||
id={id}
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
aria-label={`证书检测日期,${selectedDate ? format(selectedDate, 'yyyy年M月d日') : '未选择'}`}
|
||||
aria-label={`${label},${selectedDate ? format(selectedDate, 'yyyy年M月d日') : '未选择'}`}
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded={isOpen}
|
||||
aria-controls={`${id}-calendar`}
|
||||
@ -102,7 +103,7 @@ export default function CertificateTestDatePicker({ id, value, onChange, disable
|
||||
className="input-apple flex min-h-[50px] items-center justify-between gap-3 text-left disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
<span className={selectedDate ? 'text-[#1a1c1d]' : 'text-gray-500'}>
|
||||
{selectedDate ? format(selectedDate, 'yyyy年M月d日') : '选择检测日期'}
|
||||
{selectedDate ? format(selectedDate, 'yyyy年M月d日') : '选择日期'}
|
||||
</span>
|
||||
<CalendarDays className="h-[18px] w-[18px] shrink-0 text-[#5c6470]" aria-hidden="true" />
|
||||
</button>
|
||||
@ -112,7 +113,7 @@ export default function CertificateTestDatePicker({ id, value, onChange, disable
|
||||
ref={calendarRef}
|
||||
id={`${id}-calendar`}
|
||||
role="dialog"
|
||||
aria-label="选择证书检测日期"
|
||||
aria-label={`选择${label}`}
|
||||
className="absolute left-1/2 top-full z-40 mt-2 w-[320px] max-w-[calc(100vw-3rem)] -translate-x-1/2 rounded-[12px] border border-[#e7e8ea] bg-white p-3 text-[#1a1c1d] shadow-[0_12px_28px_rgba(26,28,29,0.10)]"
|
||||
>
|
||||
<div className="mb-3 flex items-center justify-between gap-2">
|
||||
44
components/upload/CertificateReportUploader.test.tsx
Normal file
44
components/upload/CertificateReportUploader.test.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import CertificateReportUploader from './CertificateReportUploader'
|
||||
import { chunkUpload } from '@/lib/chunk-upload'
|
||||
import { DEFAULT_CERTIFICATE_REPORT_USAGE_RULES } from '@/lib/certificate-report'
|
||||
|
||||
jest.mock('@/lib/chunk-upload', () => ({ chunkUpload: jest.fn() }))
|
||||
|
||||
test.each(['成品', '原材料'])('%s上传只提交一段说明,并分别提交检测日期和到期时间', async (kind) => {
|
||||
const originalFetch = global.fetch
|
||||
jest.useFakeTimers().setSystemTime(new Date('2026-09-27T12:00:00'))
|
||||
const { container } = render(<CertificateReportUploader onSuccess={jest.fn()} onCancel={jest.fn()} productCategories={[{ id: '1', name: '板材' }]} />)
|
||||
try {
|
||||
expect(screen.getByLabelText('成品')).toBeChecked()
|
||||
fireEvent.click(screen.getByLabelText(kind))
|
||||
fireEvent.change(screen.getByLabelText(/报告名称 \/ 检测对象/), { target: { value: '板材检测报告' } })
|
||||
fireEvent.change(screen.getByLabelText('品类 *'), { target: { value: '板材' } })
|
||||
fireEvent.change(screen.getByLabelText('证书编号 *'), { target: { value: 'CMA-001' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: /证书检测日期/ }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '2026年9月23日' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: /证书到期时间/ }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '选择年份和月份' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '下一年' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '9月' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '2027年9月23日' }))
|
||||
expect(screen.queryByLabelText('证书使用说明 *')).not.toBeInTheDocument()
|
||||
expect(screen.getAllByRole('textbox').filter((input) => input.tagName === 'TEXTAREA')).toHaveLength(1)
|
||||
expect(screen.getByLabelText('检测报告使用规则说明 *')).toHaveValue(DEFAULT_CERTIFICATE_REPORT_USAGE_RULES)
|
||||
const pdf = new File(['%PDF-1.4'], '报告.pdf', { type: 'application/pdf' })
|
||||
fireEvent.change(container.querySelector('input[type="file"]')!, { target: { files: [pdf] } })
|
||||
jest.useRealTimers()
|
||||
global.fetch = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ materialId: 'report-1' }) })
|
||||
;(chunkUpload as jest.Mock).mockResolvedValue({ uploadId: 'file-1' })
|
||||
fireEvent.click(screen.getByRole('button', { name: '创建分享素材' }))
|
||||
await waitFor(() => expect(global.fetch).toHaveBeenCalledTimes(2))
|
||||
const [url, init] = (global.fetch as jest.Mock).mock.calls[0]
|
||||
expect(url).toBe('/api/materials/upload/certificate-report/init')
|
||||
const payload = JSON.parse(init.body)
|
||||
expect(payload).toMatchObject({ reportKind: kind, certificateTestedOn: '2026-09-23', certificateExpiresOn: '2027-09-23', reportUsageRules: DEFAULT_CERTIFICATE_REPORT_USAGE_RULES })
|
||||
expect(payload).not.toHaveProperty('certificateUsageInstructions')
|
||||
} finally {
|
||||
jest.useRealTimers()
|
||||
global.fetch = originalFetch
|
||||
}
|
||||
})
|
||||
@ -9,7 +9,7 @@ import {
|
||||
detectFileKindFromName,
|
||||
validateCertificateReportMetadata,
|
||||
} from '@/lib/certificate-report'
|
||||
import CertificateTestDatePicker from '@/components/upload/CertificateTestDatePicker'
|
||||
import CertificateDatePicker from '@/components/upload/CertificateDatePicker'
|
||||
import { FileUp } from 'lucide-react'
|
||||
import { dropZoneIdleClass, fieldControlClass, fieldHintClass, fieldLabelClass, inkButtonClass, outlineButtonClass, uploadAreaButtonClass } from './upload-field-styles'
|
||||
|
||||
@ -41,7 +41,7 @@ export default function CertificateReportUploader({
|
||||
const [reportKind, setReportKind] = useState<string>(CERTIFICATE_REPORT_KIND.FINISHED)
|
||||
const [certificateTestedOn, setCertificateTestedOn] = useState('')
|
||||
const [certificateNumber, setCertificateNumber] = useState('')
|
||||
const [certificateUsageInstructions, setCertificateUsageInstructions] = useState('')
|
||||
const [certificateExpiresOn, setCertificateExpiresOn] = useState('')
|
||||
const [reportUsageRules, setReportUsageRules] = useState(DEFAULT_CERTIFICATE_REPORT_USAGE_RULES)
|
||||
const [productCategoryName, setProductCategoryName] = useState('')
|
||||
const [platformName, setPlatformName] = useState('')
|
||||
@ -126,7 +126,7 @@ export default function CertificateReportUploader({
|
||||
reportTitle,
|
||||
certificateTestedOn,
|
||||
certificateNumber,
|
||||
certificateUsageInstructions,
|
||||
certificateExpiresOn,
|
||||
reportUsageRules,
|
||||
platformName,
|
||||
})
|
||||
@ -160,7 +160,7 @@ export default function CertificateReportUploader({
|
||||
reportKind,
|
||||
certificateTestedOn,
|
||||
certificateNumber: certificateNumber.trim(),
|
||||
certificateUsageInstructions: certificateUsageInstructions.trim(),
|
||||
certificateExpiresOn,
|
||||
reportUsageRules: reportUsageRules.trim(),
|
||||
productCategoryName: productCategoryName.trim(),
|
||||
platformName: platformName.trim(),
|
||||
@ -313,8 +313,9 @@ export default function CertificateReportUploader({
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<label className={fieldLabelClass}>品类 *</label>
|
||||
<label htmlFor="certificate-report-category" className={fieldLabelClass}>品类 *</label>
|
||||
<select
|
||||
id="certificate-report-category"
|
||||
value={productCategoryName}
|
||||
onChange={(event) => setProductCategoryName(event.target.value)}
|
||||
className={fieldControlClass}
|
||||
@ -346,18 +347,18 @@ export default function CertificateReportUploader({
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<label htmlFor="certificate-report-tested-on" className={fieldLabelClass}>证书检测日期 *</label>
|
||||
<CertificateTestDatePicker id="certificate-report-tested-on" value={certificateTestedOn} onChange={setCertificateTestedOn} disabled={uploading} />
|
||||
<CertificateDatePicker id="certificate-report-tested-on" label="证书检测日期" value={certificateTestedOn} onChange={setCertificateTestedOn} disabled={uploading} />
|
||||
<p className="mt-1 text-xs text-gray-500">请按报告标注的检测日期选择</p>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="certificate-report-number" className={fieldLabelClass}>证书编号 *</label>
|
||||
<input id="certificate-report-number" type="text" value={certificateNumber} onChange={(event) => setCertificateNumber(event.target.value)} maxLength={120} className={fieldControlClass} required />
|
||||
<label htmlFor="certificate-report-expires-on" className={fieldLabelClass}>证书到期时间 *</label>
|
||||
<CertificateDatePicker id="certificate-report-expires-on" label="证书到期时间" value={certificateExpiresOn} onChange={setCertificateExpiresOn} disabled={uploading} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="certificate-report-instructions" className={fieldLabelClass}>证书使用说明 *</label>
|
||||
<textarea id="certificate-report-instructions" value={certificateUsageInstructions} onChange={(event) => setCertificateUsageInstructions(event.target.value)} rows={3} maxLength={5000} className={`${fieldControlClass} resize-y`} required />
|
||||
<label htmlFor="certificate-report-number" className={fieldLabelClass}>证书编号 *</label>
|
||||
<input id="certificate-report-number" type="text" value={certificateNumber} onChange={(event) => setCertificateNumber(event.target.value)} maxLength={120} className={fieldControlClass} required />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@ -2,7 +2,7 @@ import {
|
||||
DEFAULT_CERTIFICATE_REPORT_USAGE_RULES,
|
||||
getCertificateReportDisplayTitle,
|
||||
isPublicCertificateReportPdf,
|
||||
parseCertificateTestDate,
|
||||
parseCertificateDate,
|
||||
validateCertificateReportMetadata,
|
||||
} from '@/lib/certificate-report'
|
||||
|
||||
@ -12,7 +12,7 @@ const metadata = {
|
||||
reportTitle: '',
|
||||
certificateTestedOn: '2027-09-23',
|
||||
certificateNumber: 'CMA-2026-001',
|
||||
certificateUsageInstructions: '使用前核对送检主体',
|
||||
certificateExpiresOn: '2028-09-23',
|
||||
reportUsageRules: DEFAULT_CERTIFICATE_REPORT_USAGE_RULES,
|
||||
platformName: '大货',
|
||||
}
|
||||
@ -47,14 +47,25 @@ describe('检测报告字段规则', () => {
|
||||
})
|
||||
|
||||
test('检测日期必须是真实的 YYYY-MM-DD 日期', () => {
|
||||
expect(parseCertificateTestDate('2027-02-28')?.toISOString().slice(0, 10)).toBe('2027-02-28')
|
||||
expect(parseCertificateTestDate('2027-02-30')).toBeNull()
|
||||
expect(parseCertificateTestDate('2027/02/28')).toBeNull()
|
||||
expect(parseCertificateDate('2027-02-28')?.toISOString().slice(0, 10)).toBe('2027-02-28')
|
||||
expect(parseCertificateDate('2027-02-30')).toBeNull()
|
||||
expect(parseCertificateDate('2027/02/28')).toBeNull()
|
||||
expect(validateCertificateReportMetadata({ ...metadata, certificateTestedOn: '2027-02-30' })).toEqual({
|
||||
error: '请选择有效的证书检测日期',
|
||||
})
|
||||
})
|
||||
|
||||
test('到期时间独立必填,不自动计算,并拒绝早于检测日期的日期', () => {
|
||||
for (const certificateExpiresOn of ['', undefined, '2028-02-30', '2028/09/23']) {
|
||||
expect(validateCertificateReportMetadata({ ...metadata, certificateExpiresOn })).toEqual({ error: '请选择有效的证书到期时间' })
|
||||
}
|
||||
expect(validateCertificateReportMetadata({ ...metadata, certificateExpiresOn: '2027-09-22' }))
|
||||
.toEqual({ error: '证书到期时间不能早于检测日期' })
|
||||
expect(validateCertificateReportMetadata({ ...metadata, certificateExpiresOn: '2027-09-23' }))
|
||||
.toHaveProperty('value.certificateExpiresOn', new Date('2027-09-23T00:00:00.000Z'))
|
||||
expect(validateCertificateReportMetadata(metadata)).not.toHaveProperty('value.certificateUsageInstructions')
|
||||
})
|
||||
|
||||
test('平台分类只接受报告专用选项,整段预置说明保持为一个文本值', () => {
|
||||
expect(validateCertificateReportMetadata({ ...metadata, platformName: '品牌' })).toHaveProperty('value')
|
||||
expect(validateCertificateReportMetadata({ ...metadata, platformName: '其它' })).toEqual({
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/** @jest-environment jsdom */
|
||||
|
||||
import { act, render, screen, waitFor } from '@testing-library/react'
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { useSession } from 'next-auth/react'
|
||||
import EditMaterialPage from '@/app/edit/[id]/page'
|
||||
@ -203,12 +203,22 @@ describe('EditMaterialPage', () => {
|
||||
const kindSelect = await screen.findByLabelText('报告类别 *')
|
||||
expect(screen.getByLabelText('SPU')).toHaveValue('SPU-001')
|
||||
expect(screen.getByLabelText('证书检测日期 *')).toHaveTextContent('2027年9月23日')
|
||||
expect(screen.queryByText('2028年9月23日')).not.toBeInTheDocument()
|
||||
expect(screen.getByLabelText('证书到期时间 *')).toHaveTextContent('2028年9月23日')
|
||||
expect(screen.queryByLabelText('证书使用说明 *')).not.toBeInTheDocument()
|
||||
expect(screen.getByLabelText('检测报告使用规则说明 *')).toHaveValue('第一段\n第二段')
|
||||
expect(screen.getByLabelText('平台分类')).toHaveValue('品牌')
|
||||
|
||||
await user.selectOptions(kindSelect, '原材料')
|
||||
expect(screen.queryByLabelText('SPU')).not.toBeInTheDocument()
|
||||
expect(screen.getByLabelText('报告名称 / 检测对象 *')).toHaveValue('板材检测报告')
|
||||
await user.click(screen.getByLabelText('证书到期时间 *'))
|
||||
await user.click(screen.getByRole('button', { name: '2028年9月24日' }))
|
||||
fireEvent.submit(screen.getByLabelText('报告名称 / 检测对象 *').closest('form')!)
|
||||
await waitFor(() => expect((global.fetch as jest.Mock).mock.calls.some(([, init]) => init?.method === 'PUT')).toBe(true))
|
||||
const [, init] = (global.fetch as jest.Mock).mock.calls.find(([, init]) => init?.method === 'PUT')!
|
||||
expect(init.body.get('certificateTestedOn')).toBe('2027-09-23')
|
||||
expect(init.body.get('certificateExpiresOn')).toBe('2028-09-24')
|
||||
expect(init.body.get('reportUsageRules')).toBe('第一段\n第二段')
|
||||
expect(init.body.has('certificateUsageInstructions')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@ -63,7 +63,7 @@ describe('material-update shared rules', () => {
|
||||
reportKind: '成品',
|
||||
certificateTestedOn: '2027-09-23',
|
||||
certificateNumber: 'CMA-2026-001',
|
||||
certificateUsageInstructions: '使用前核对送检主体',
|
||||
certificateExpiresOn: '2028-09-23',
|
||||
reportUsageRules: '完整的报告使用规则说明',
|
||||
}
|
||||
const baseMaterial = {
|
||||
@ -250,7 +250,7 @@ describe('material-update shared rules', () => {
|
||||
reportKind: '成品',
|
||||
certificateTestedOn: new Date('2027-09-23T00:00:00.000Z'),
|
||||
certificateNumber: 'CMA-2026-001',
|
||||
certificateUsageInstructions: '使用前核对送检主体',
|
||||
certificateExpiresOn: new Date('2028-09-23T00:00:00.000Z'),
|
||||
reportUsageRules: '完整的报告使用规则说明',
|
||||
categoryName: '通用',
|
||||
sceneName: '通用',
|
||||
@ -271,7 +271,7 @@ describe('material-update shared rules', () => {
|
||||
reportKind: '成品',
|
||||
certificateTestedOn: new Date('2027-09-23T00:00:00.000Z'),
|
||||
certificateNumber: 'CMA-2026-001',
|
||||
certificateUsageInstructions: '使用前核对送检主体',
|
||||
certificateExpiresOn: new Date('2028-09-23T00:00:00.000Z'),
|
||||
reportUsageRules: '完整的报告使用规则说明',
|
||||
renderer: '',
|
||||
}
|
||||
|
||||
@ -128,7 +128,12 @@ describe('materials share security', () => {
|
||||
test.each([null, { user: { id: 'admin-1' } }])('公开检测报告返回 PDF 和图片,保留源文件隔离 (%j)', async (session) => {
|
||||
;(getServerSession as jest.Mock).mockResolvedValue(session)
|
||||
;(prisma.user.findUnique as jest.Mock).mockResolvedValue({ role: 'SYSTEM_ADMIN' })
|
||||
;(prisma.material.findFirst as jest.Mock).mockResolvedValue(buildMaterial({ materialType: '证书-检测报告' }))
|
||||
;(prisma.material.findFirst as jest.Mock).mockResolvedValue(buildMaterial({
|
||||
materialType: '证书-检测报告',
|
||||
certificateTestedOn: new Date('2026-09-23'),
|
||||
certificateExpiresOn: new Date('2027-09-23'),
|
||||
reportUsageRules: '唯一的完整规则说明',
|
||||
}))
|
||||
const pdfUrl = 'https://s3.jiansun.vip/gongxiangpan/public-display/certificate-report/material-1/pdf/0-report.pdf'
|
||||
const imageUrl = 'https://s3.jiansun.vip/gongxiangpan/public-display/certificate-report/material-1/image/1-report.jpg'
|
||||
;(prisma.materialAsset.findMany as jest.Mock).mockResolvedValue([
|
||||
@ -144,6 +149,11 @@ describe('materials share security', () => {
|
||||
expect(body.assets[1]).toMatchObject({ fileUrl: imageUrl, fileKind: 'image' })
|
||||
expect(body.assets[2].fileUrl).toBe('')
|
||||
expect(body.assets[3].fileUrl).toBe('')
|
||||
expect(body.certificateTestedOn).toBe('2026-09-23T00:00:00.000Z')
|
||||
expect(body.certificateExpiresOn).toBe('2027-09-23T00:00:00.000Z')
|
||||
expect(body.reportUsageRules).toBe('唯一的完整规则说明')
|
||||
expect(body).not.toHaveProperty('certificateUsageInstructions')
|
||||
expect((prisma.material.findFirst as jest.Mock).mock.calls[0][0].select).not.toHaveProperty('certificateUsageInstructions')
|
||||
})
|
||||
|
||||
test('未满15天的视频分享链接保留有效,但不返回任何视频内容', async () => {
|
||||
|
||||
@ -65,7 +65,7 @@ export function normalizeReportTitle(value: string | null | undefined): string |
|
||||
return normalized || null
|
||||
}
|
||||
|
||||
export function parseCertificateTestDate(value: unknown): Date | null {
|
||||
export function parseCertificateDate(value: unknown): Date | null {
|
||||
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return null
|
||||
const parsed = new Date(`${value}T00:00:00.000Z`)
|
||||
return Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== value
|
||||
@ -78,8 +78,8 @@ export function validateCertificateReportMetadata(input: {
|
||||
productModel: unknown
|
||||
reportTitle: unknown
|
||||
certificateTestedOn: unknown
|
||||
certificateExpiresOn: unknown
|
||||
certificateNumber: unknown
|
||||
certificateUsageInstructions: unknown
|
||||
reportUsageRules: unknown
|
||||
platformName: unknown
|
||||
}, legacyPlatformName = ''):
|
||||
@ -87,8 +87,8 @@ export function validateCertificateReportMetadata(input: {
|
||||
| { value: {
|
||||
reportKind: CertificateReportKind
|
||||
certificateTestedOn: Date
|
||||
certificateExpiresOn: Date
|
||||
certificateNumber: string
|
||||
certificateUsageInstructions: string
|
||||
reportUsageRules: string
|
||||
} } {
|
||||
const reportKind = input.reportKind
|
||||
@ -103,18 +103,15 @@ export function validateCertificateReportMetadata(input: {
|
||||
if (reportKind === CERTIFICATE_REPORT_KIND.FINISHED && !productModel && !reportTitle) {
|
||||
return { error: 'SPU 和 报告名称/检测对象 至少填写一个' }
|
||||
}
|
||||
const certificateTestedOn = parseCertificateTestDate(input.certificateTestedOn)
|
||||
const certificateTestedOn = parseCertificateDate(input.certificateTestedOn)
|
||||
if (!certificateTestedOn) return { error: '请选择有效的证书检测日期' }
|
||||
const certificateExpiresOn = parseCertificateDate(input.certificateExpiresOn)
|
||||
if (!certificateExpiresOn) return { error: '请选择有效的证书到期时间' }
|
||||
if (certificateExpiresOn < certificateTestedOn) return { error: '证书到期时间不能早于检测日期' }
|
||||
const certificateNumber = typeof input.certificateNumber === 'string' ? input.certificateNumber.trim() : ''
|
||||
if (!certificateNumber || certificateNumber.length > 120) {
|
||||
return { error: '请填写证书编号(不超过120字)' }
|
||||
}
|
||||
const certificateUsageInstructions = typeof input.certificateUsageInstructions === 'string'
|
||||
? input.certificateUsageInstructions.trim()
|
||||
: ''
|
||||
if (!certificateUsageInstructions || certificateUsageInstructions.length > 5000) {
|
||||
return { error: '请填写证书使用说明(不超过5000字)' }
|
||||
}
|
||||
const reportUsageRules = typeof input.reportUsageRules === 'string' ? input.reportUsageRules.trim() : ''
|
||||
if (!reportUsageRules || reportUsageRules.length > 10000) {
|
||||
return { error: '请填写检测报告使用规则说明(不超过10000字)' }
|
||||
@ -129,8 +126,8 @@ export function validateCertificateReportMetadata(input: {
|
||||
value: {
|
||||
reportKind,
|
||||
certificateTestedOn,
|
||||
certificateExpiresOn,
|
||||
certificateNumber,
|
||||
certificateUsageInstructions,
|
||||
reportUsageRules,
|
||||
},
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ export interface MaterialUpdateInput {
|
||||
reportKind?: string | null
|
||||
certificateTestedOn?: string | null
|
||||
certificateNumber?: string | null
|
||||
certificateUsageInstructions?: string | null
|
||||
certificateExpiresOn?: string | null
|
||||
reportUsageRules?: string | null
|
||||
categoryName?: string | null
|
||||
sceneName?: string | null
|
||||
@ -196,7 +196,7 @@ async function updateMaterialWithSharedRulesLocked(options: UpdateMaterialOption
|
||||
const hasReportKindField = Object.prototype.hasOwnProperty.call(input, 'reportKind')
|
||||
const hasCertificateTestedOnField = Object.prototype.hasOwnProperty.call(input, 'certificateTestedOn')
|
||||
const hasCertificateNumberField = Object.prototype.hasOwnProperty.call(input, 'certificateNumber')
|
||||
const hasCertificateUsageInstructionsField = Object.prototype.hasOwnProperty.call(input, 'certificateUsageInstructions')
|
||||
const hasCertificateExpiresOnField = Object.prototype.hasOwnProperty.call(input, 'certificateExpiresOn')
|
||||
const hasReportUsageRulesField = Object.prototype.hasOwnProperty.call(input, 'reportUsageRules')
|
||||
const hasTaskNoField = Object.prototype.hasOwnProperty.call(input, 'taskNo')
|
||||
const hasDingtalkApprovalNoField = Object.prototype.hasOwnProperty.call(input, 'dingtalkApprovalNo')
|
||||
@ -237,9 +237,9 @@ async function updateMaterialWithSharedRulesLocked(options: UpdateMaterialOption
|
||||
? input.certificateTestedOn
|
||||
: material.certificateTestedOn?.toISOString().slice(0, 10),
|
||||
certificateNumber: hasCertificateNumberField ? input.certificateNumber : material.certificateNumber,
|
||||
certificateUsageInstructions: hasCertificateUsageInstructionsField
|
||||
? input.certificateUsageInstructions
|
||||
: material.certificateUsageInstructions,
|
||||
certificateExpiresOn: hasCertificateExpiresOnField
|
||||
? input.certificateExpiresOn
|
||||
: material.certificateExpiresOn?.toISOString().slice(0, 10),
|
||||
reportUsageRules: hasReportUsageRulesField ? input.reportUsageRules : material.reportUsageRules,
|
||||
platformName,
|
||||
}, isCertificateReportMaterialType(material.materialType) ? material.platformName : '')
|
||||
@ -389,7 +389,7 @@ async function updateMaterialWithSharedRulesLocked(options: UpdateMaterialOption
|
||||
reportKind: certificateReportMetadata && 'value' in certificateReportMetadata ? certificateReportMetadata.value.reportKind : null,
|
||||
certificateTestedOn: certificateReportMetadata && 'value' in certificateReportMetadata ? certificateReportMetadata.value.certificateTestedOn : null,
|
||||
certificateNumber: certificateReportMetadata && 'value' in certificateReportMetadata ? certificateReportMetadata.value.certificateNumber : null,
|
||||
certificateUsageInstructions: certificateReportMetadata && 'value' in certificateReportMetadata ? certificateReportMetadata.value.certificateUsageInstructions : null,
|
||||
certificateExpiresOn: certificateReportMetadata && 'value' in certificateReportMetadata ? certificateReportMetadata.value.certificateExpiresOn : null,
|
||||
reportUsageRules: certificateReportMetadata && 'value' in certificateReportMetadata ? certificateReportMetadata.value.reportUsageRules : null,
|
||||
categoryName: shouldUseGeneralCategoryScene ? '通用' : (categoryName ?? material.categoryName),
|
||||
sceneName: shouldUseGeneralCategoryScene ? '通用' : (sceneName ?? material.sceneName),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user