gongxiangp2/components/upload/TaskReferenceFields.tsx

113 lines
3.7 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'
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
const inputClassName =
'w-full rounded-apple border border-gray-300 px-4 py-2 outline-none focus:border-gray-900 focus:ring-2 focus:ring-gray-200 disabled:cursor-not-allowed disabled:opacity-60 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:focus:border-gray-400 dark:focus:ring-gray-600'
return (
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="min-w-0">
<label
htmlFor={taskNoId}
className="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>
任务号{taskNoRequired ? ' *' : ''}
</label>
<input
id={taskNoId}
type="text"
value={taskNo}
onChange={(event) => onTaskNoChange(event.target.value.toUpperCase())}
onBlur={() => setTaskNoTouched(true)}
placeholder="例如:TASK202601030184"
className={inputClassName}
disabled={disabled}
required={taskNoRequired}
aria-invalid={Boolean(visibleTaskNoError)}
aria-describedby={visibleTaskNoError ? taskNoErrorId : undefined}
/>
{visibleTaskNoError && (
<p id={taskNoErrorId} className="mt-1 text-sm text-red-600 dark:text-red-400">
{visibleTaskNoError}
</p>
)}
</div>
<div className="min-w-0">
<label
htmlFor={approvalNoId}
className="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>
钉钉审批编号
</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={inputClassName}
disabled={disabled}
aria-invalid={Boolean(visibleApprovalNoError)}
aria-describedby={visibleApprovalNoError ? approvalNoErrorId : undefined}
/>
{visibleApprovalNoError && (
<p id={approvalNoErrorId} className="mt-1 text-sm text-red-600 dark:text-red-400">
{visibleApprovalNoError}
</p>
)}
</div>
</div>
)
}