gongxiangp2/components/upload/TaskReferenceFields.tsx

110 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useState } from 'react'
import {
DINGTALK_APPROVAL_NO_MAX_LENGTH,
getDingtalkApprovalNoError,
} from '@/lib/dingtalk-approval-no'
import {
fieldControlClass,
fieldErrorClass,
fieldLabelClass,
} from '@/components/upload/upload-field-styles'
interface TaskReferenceFieldsProps {
idPrefix: string
taskNo: string
onTaskNoChange: (value: string) => void
dingtalkApprovalNo: string
onDingtalkApprovalNoChange: (value: string) => void
taskNoRequired?: boolean
disabled?: boolean
}
function getTaskNoError(value: string): string | null {
const normalized = value.trim().toUpperCase()
if (!normalized) {
return null
}
return /^TASK\d{12,18}(-\d{2})*$/.test(normalized)
? null
: '任务号格式错误'
}
export default function TaskReferenceFields({
idPrefix,
taskNo,
onTaskNoChange,
dingtalkApprovalNo,
onDingtalkApprovalNoChange,
taskNoRequired = false,
disabled = false,
}: TaskReferenceFieldsProps) {
const [taskNoTouched, setTaskNoTouched] = useState(false)
const [approvalNoTouched, setApprovalNoTouched] = useState(false)
const taskNoId = `${idPrefix}-task-no`
const taskNoErrorId = `${taskNoId}-error`
const approvalNoId = `${idPrefix}-dingtalk-approval-no`
const approvalNoErrorId = `${approvalNoId}-error`
const taskNoError = getTaskNoError(taskNo)
const approvalNoError = getDingtalkApprovalNoError(dingtalkApprovalNo)
const visibleTaskNoError = taskNoTouched ? taskNoError : null
const visibleApprovalNoError = approvalNoTouched ? approvalNoError : null
return (
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="min-w-0">
<label htmlFor={taskNoId} className={fieldLabelClass}>
任务号{taskNoRequired ? ' *' : ''}
</label>
<input
id={taskNoId}
type="text"
value={taskNo}
onChange={(event) => onTaskNoChange(event.target.value.toUpperCase())}
onBlur={() => setTaskNoTouched(true)}
placeholder="例如:TASK202601030184"
className={fieldControlClass}
disabled={disabled}
required={taskNoRequired}
aria-invalid={Boolean(visibleTaskNoError)}
aria-describedby={visibleTaskNoError ? taskNoErrorId : undefined}
/>
{visibleTaskNoError && (
<p id={taskNoErrorId} className={fieldErrorClass}>
{visibleTaskNoError}
</p>
)}
</div>
<div className="min-w-0">
<label htmlFor={approvalNoId} className={fieldLabelClass}>
钉钉审批编号
</label>
<input
id={approvalNoId}
type="text"
inputMode="numeric"
autoComplete="off"
maxLength={DINGTALK_APPROVAL_NO_MAX_LENGTH}
value={dingtalkApprovalNo}
onChange={(event) => onDingtalkApprovalNoChange(event.target.value)}
onBlur={() => setApprovalNoTouched(true)}
placeholder="例如:202508211445000157693"
className={fieldControlClass}
disabled={disabled}
aria-invalid={Boolean(visibleApprovalNoError)}
aria-describedby={visibleApprovalNoError ? approvalNoErrorId : undefined}
/>
{visibleApprovalNoError && (
<p id={approvalNoErrorId} className={fieldErrorClass}>
{visibleApprovalNoError}
</p>
)}
</div>
</div>
)
}