OAFRONT6 完成合同台账表单分组
这个提交包含在:
@@ -403,181 +403,211 @@ export function ContractsRegistryPage() {
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>合同编号</Label>
|
||||
<Input
|
||||
value={editor.contractNo}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, contractNo: event.target.value }))}
|
||||
placeholder="例如 HT-2026-001"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-6">
|
||||
<section className="space-y-4">
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold text-zinc-900">基础信息</h4>
|
||||
<p className="text-xs text-zinc-500">填写编号、类型、相对方、摘要和金额。</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label>合同类型</Label>
|
||||
<select
|
||||
className="select-std"
|
||||
value={editor.contractType}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, contractType: event.target.value as ContractRegistryType }))}
|
||||
>
|
||||
{CONTRACT_TYPE_OPTIONS.map((item) => (
|
||||
<option key={item} value={item}>
|
||||
{contractTypeLabel(item)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>状态</Label>
|
||||
<select
|
||||
className="select-std"
|
||||
value={editor.status}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, status: event.target.value as ContractRegistryPersistedStatus }))}
|
||||
>
|
||||
{EDITOR_STATUS_OPTIONS.map((item) => (
|
||||
<option key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>合同编号</Label>
|
||||
<Input
|
||||
value={editor.contractNo}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, contractNo: event.target.value }))}
|
||||
placeholder="例如 HT-2026-001"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>关联项目</Label>
|
||||
<select
|
||||
className="select-std"
|
||||
value={editor.projectId}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, projectId: event.target.value }))}
|
||||
>
|
||||
<option value="">未关联</option>
|
||||
{projectOptions.map((item) => (
|
||||
<option key={item.id} value={item.id}>
|
||||
{item.projectNo} · {item.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>相对方</Label>
|
||||
<Input
|
||||
value={editor.counterpartyName}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, counterpartyName: event.target.value }))}
|
||||
placeholder="合同对方名称"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>摘要</Label>
|
||||
<Textarea
|
||||
value={editor.summary}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, summary: event.target.value }))}
|
||||
placeholder="只写最小必要摘要,不录入涉密正文"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label>合同金额</Label>
|
||||
<Input
|
||||
value={editor.amount}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, amount: event.target.value }))}
|
||||
placeholder="例如 100000.00"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>币种</Label>
|
||||
<Input
|
||||
value={editor.currency}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, currency: event.target.value.toUpperCase() }))}
|
||||
placeholder="CNY"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{amountInvalid ? <div className="text-xs text-red-600">合同金额需为最多 12 位整数、最多 2 位小数。</div> : null}
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-3">
|
||||
<div className="space-y-2">
|
||||
<Label>签署日期</Label>
|
||||
<DateTextInput
|
||||
value={editor.signedDate}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, signedDate: event.target.value }))}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>生效日期</Label>
|
||||
<DateTextInput
|
||||
value={editor.effectiveDate}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, effectiveDate: event.target.value }))}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>到期日期</Label>
|
||||
<DateTextInput
|
||||
value={editor.expiryDate}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, expiryDate: event.target.value }))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>归档位置</Label>
|
||||
<Input
|
||||
value={editor.archiveLocation}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, archiveLocation: event.target.value }))}
|
||||
placeholder="NAS/文件柜/共享盘路径"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label>离线档案号</Label>
|
||||
<Input
|
||||
value={editor.offlineArchiveNo}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, offlineArchiveNo: event.target.value }))}
|
||||
placeholder="可选"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>密级</Label>
|
||||
<select
|
||||
className="select-std"
|
||||
value={editor.confidentialityLevel}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, confidentialityLevel: event.target.value as ConfidentialityLevel }))}
|
||||
>
|
||||
{CONFIDENTIALITY_OPTIONS.map((item) => (
|
||||
<option key={item} value={item}>
|
||||
{confidentialityLabel(item)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>责任人</Label>
|
||||
{canBrowseEmployees ? (
|
||||
<select
|
||||
className="select-std"
|
||||
value={editor.ownerEmployeeId}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, ownerEmployeeId: event.target.value }))}
|
||||
>
|
||||
<option value="">默认当前用户</option>
|
||||
{employeeOptions.map((item) => (
|
||||
<option key={item.id} value={item.id}>
|
||||
{item.name} · {item.employeeNo}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : (
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2 text-sm">
|
||||
<div className="font-medium text-zinc-900">{readonlyOwnerLabel}</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">
|
||||
当前账号无人员目录权限,新建时使用当前用户,编辑时保留原责任人。
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label>合同类型</Label>
|
||||
<select
|
||||
className="select-std"
|
||||
value={editor.contractType}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, contractType: event.target.value as ContractRegistryType }))}
|
||||
>
|
||||
{CONTRACT_TYPE_OPTIONS.map((item) => (
|
||||
<option key={item} value={item}>
|
||||
{contractTypeLabel(item)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>状态</Label>
|
||||
<select
|
||||
className="select-std"
|
||||
value={editor.status}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, status: event.target.value as ContractRegistryPersistedStatus }))}
|
||||
>
|
||||
{EDITOR_STATUS_OPTIONS.map((item) => (
|
||||
<option key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>相对方</Label>
|
||||
<Input
|
||||
value={editor.counterpartyName}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, counterpartyName: event.target.value }))}
|
||||
placeholder="合同对方名称"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>摘要</Label>
|
||||
<Textarea
|
||||
value={editor.summary}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, summary: event.target.value }))}
|
||||
placeholder="只写最小必要摘要,不录入涉密正文"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label>合同金额</Label>
|
||||
<Input
|
||||
value={editor.amount}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, amount: event.target.value }))}
|
||||
placeholder="例如 100000.00"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>币种</Label>
|
||||
<Input
|
||||
value={editor.currency}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, currency: event.target.value.toUpperCase() }))}
|
||||
placeholder="CNY"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{amountInvalid ? <div className="text-xs text-red-600">合同金额需为最多 12 位整数、最多 2 位小数。</div> : null}
|
||||
</section>
|
||||
|
||||
<section className="space-y-4 border-t border-zinc-200 pt-5">
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold text-zinc-900">项目关联</h4>
|
||||
<p className="text-xs text-zinc-500">将合同归入项目,便于项目页汇总合同、费用和交付风险。</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>关联项目</Label>
|
||||
<select
|
||||
className="select-std"
|
||||
value={editor.projectId}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, projectId: event.target.value }))}
|
||||
>
|
||||
<option value="">未关联</option>
|
||||
{projectOptions.map((item) => (
|
||||
<option key={item.id} value={item.id}>
|
||||
{item.projectNo} · {item.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4 border-t border-zinc-200 pt-5">
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold text-zinc-900">日期与续约</h4>
|
||||
<p className="text-xs text-zinc-500">用于到期预警、续约草稿和归档判断。</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-3">
|
||||
<div className="space-y-2">
|
||||
<Label>签署日期</Label>
|
||||
<DateTextInput
|
||||
value={editor.signedDate}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, signedDate: event.target.value }))}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>生效日期</Label>
|
||||
<DateTextInput
|
||||
value={editor.effectiveDate}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, effectiveDate: event.target.value }))}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>到期日期</Label>
|
||||
<DateTextInput
|
||||
value={editor.expiryDate}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, expiryDate: event.target.value }))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4 border-t border-zinc-200 pt-5">
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold text-zinc-900">归档与责任</h4>
|
||||
<p className="text-xs text-zinc-500">登记线下归档位置、密级和责任人,不上传涉密正文。</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>归档位置</Label>
|
||||
<Input
|
||||
value={editor.archiveLocation}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, archiveLocation: event.target.value }))}
|
||||
placeholder="文件柜、档案盒或归档目录摘要"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label>离线档案号</Label>
|
||||
<Input
|
||||
value={editor.offlineArchiveNo}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, offlineArchiveNo: event.target.value }))}
|
||||
placeholder="可选"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>密级</Label>
|
||||
<select
|
||||
className="select-std"
|
||||
value={editor.confidentialityLevel}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, confidentialityLevel: event.target.value as ConfidentialityLevel }))}
|
||||
>
|
||||
{CONFIDENTIALITY_OPTIONS.map((item) => (
|
||||
<option key={item} value={item}>
|
||||
{confidentialityLabel(item)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>责任人</Label>
|
||||
{canBrowseEmployees ? (
|
||||
<select
|
||||
className="select-std"
|
||||
value={editor.ownerEmployeeId}
|
||||
onChange={(event) => setEditor((current) => ({ ...current, ownerEmployeeId: event.target.value }))}
|
||||
>
|
||||
<option value="">默认当前用户</option>
|
||||
{employeeOptions.map((item) => (
|
||||
<option key={item.id} value={item.id}>
|
||||
{item.name} · {item.employeeNo}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : (
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2 text-sm">
|
||||
<div className="font-medium text-zinc-900">{readonlyOwnerLabel}</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">
|
||||
当前账号无人员目录权限,新建时使用当前用户,编辑时保留原责任人。
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
|
||||
在新工单中引用
屏蔽一个用户