feat: inherit channel report requirements
This commit is contained in:
@@ -509,12 +509,26 @@ export type ChannelGroupItem = DictionaryItem & {
|
||||
|
||||
export type ChannelReportField = DictionaryItem & {
|
||||
channelId: string;
|
||||
drainageFieldId?: string | null;
|
||||
reportType?: 'signature' | 'drainage' | 'both';
|
||||
code: string;
|
||||
name: string;
|
||||
fieldType: string;
|
||||
required: boolean;
|
||||
description?: string | null;
|
||||
sortOrder?: number;
|
||||
drainageField?: DictionaryItem | null;
|
||||
};
|
||||
|
||||
export type ApplicationReportField = {
|
||||
id: string;
|
||||
code: string;
|
||||
name: string;
|
||||
fieldType: string;
|
||||
required: boolean;
|
||||
description?: string | null;
|
||||
reportTypes: string[];
|
||||
channels: Array<{ id: string; code: string; name: string; groupId: string; groupName: string; required: boolean; reportType: 'signature' | 'drainage' | 'both' }>;
|
||||
};
|
||||
|
||||
export type ReportTask = DictionaryItem & {
|
||||
@@ -876,6 +890,8 @@ export const adminApi = {
|
||||
}),
|
||||
listApplicationConnections: (applicationId: string) =>
|
||||
request<ApplicationConnectionsResponse>(`/admin/enterprise-applications/${applicationId}/connections`),
|
||||
listApplicationReportFields: (applicationId: string, reportType?: 'signature' | 'drainage') =>
|
||||
request<ApplicationReportField[]>(withQuery(`/admin/enterprise-applications/${applicationId}/report-fields`, { reportType })),
|
||||
getApplicationCmppParams: (applicationId: string) =>
|
||||
request<ApplicationCmppParams>(`/admin/enterprise-applications/${applicationId}/cmpp-params`),
|
||||
listChannels: () => request<AdminChannel[]>('/admin/channels'),
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { ArrowLeft, Plus, Search } from 'lucide-react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { adminApi, type AdminChannel, type ChannelReportField } from '@/api/adminApi';
|
||||
import { adminApi, type AdminChannel, type ChannelReportField, type DictionaryItem } from '@/api/adminApi';
|
||||
import { Breadcrumb, Button, Input, Modal, Select, Table, Textarea, Tag, type TableColumn } from '@/components/ui';
|
||||
|
||||
export function AdminChannelReportPage() {
|
||||
const navigate = useNavigate();
|
||||
const [channels, setChannels] = useState<AdminChannel[]>([]);
|
||||
const [fields, setFields] = useState<ChannelReportField[]>([]);
|
||||
const [libraryFields, setLibraryFields] = useState<DictionaryItem[]>([]);
|
||||
const [channelId, setChannelId] = useState('');
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [code, setCode] = useState('');
|
||||
const [name, setName] = useState('');
|
||||
const [fieldType, setFieldType] = useState('string');
|
||||
const [drainageFieldId, setDrainageFieldId] = useState('');
|
||||
const [reportType, setReportType] = useState<'signature' | 'drainage' | 'both'>('signature');
|
||||
const [required, setRequired] = useState(false);
|
||||
const [description, setDescription] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
|
||||
function loadData(nextChannelId = channelId) {
|
||||
Promise.all([adminApi.listChannels(), adminApi.listChannelReportFields(nextChannelId || undefined)])
|
||||
.then(([channelItems, fieldItems]) => {
|
||||
Promise.all([adminApi.listChannels(), adminApi.listChannelReportFields(nextChannelId || undefined), adminApi.listDrainageFields()])
|
||||
.then(([channelItems, fieldItems, libraryItems]) => {
|
||||
setChannels(channelItems.filter((item) => item.status !== 'deleted'));
|
||||
setFields(fieldItems);
|
||||
setLibraryFields(libraryItems.filter((item) => item.status === 'active'));
|
||||
setError('');
|
||||
})
|
||||
.catch((failure: Error) => setError(failure.message || '通道报备配置加载失败'));
|
||||
@@ -34,12 +36,14 @@ export function AdminChannelReportPage() {
|
||||
const filteredFields = useMemo(() => fields.filter((field) => !keyword || [field.code, field.name, field.fieldType, field.description].join(' ').includes(keyword)), [fields, keyword]);
|
||||
|
||||
function createField() {
|
||||
adminApi.createChannelReportField({ channelId, code, name, fieldType, description, status: 'active' })
|
||||
const selected = libraryFields.find((item) => item.id === drainageFieldId);
|
||||
if (!selected) return;
|
||||
adminApi.createChannelReportField({ channelId, drainageFieldId, reportType, required, description, status: 'active' })
|
||||
.then(() => {
|
||||
setModalOpen(false);
|
||||
setCode('');
|
||||
setName('');
|
||||
setFieldType('string');
|
||||
setDrainageFieldId('');
|
||||
setReportType('signature');
|
||||
setRequired(false);
|
||||
setDescription('');
|
||||
loadData();
|
||||
})
|
||||
@@ -51,6 +55,7 @@ export function AdminChannelReportPage() {
|
||||
{ key: 'code', title: '字段代码', width: '160px', render: (record) => <strong>{record.code}</strong> },
|
||||
{ key: 'name', title: '字段名称', width: '160px', render: (record) => record.name },
|
||||
{ key: 'type', title: '字段类型', width: '120px', render: (record) => record.fieldType },
|
||||
{ key: 'reportType', title: '报备用途', width: '140px', render: (record) => record.reportType === 'signature' ? '签名报备' : record.reportType === 'drainage' ? '引流信息报备' : '签名+引流' },
|
||||
{ key: 'required', title: '必填', width: '90px', render: (record) => <Tag tone={record.required ? 'warning' : 'info'}>{record.required ? '是' : '否'}</Tag> },
|
||||
{ key: 'description', title: '说明', render: (record) => record.description ?? '-' },
|
||||
];
|
||||
@@ -87,26 +92,20 @@ export function AdminChannelReportPage() {
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
footer={<><Button onClick={() => setModalOpen(false)} variant="ghost">取消</Button><Button disabled={!channelId || !code || !name} onClick={createField}>保存</Button></>}
|
||||
footer={<><Button onClick={() => setModalOpen(false)} variant="ghost">取消</Button><Button disabled={!channelId || !drainageFieldId} onClick={createField}>保存</Button></>}
|
||||
onClose={() => setModalOpen(false)}
|
||||
open={modalOpen}
|
||||
title="新增通道报备字段"
|
||||
>
|
||||
<div className="admin-system-modal-form">
|
||||
<Input label="字段代码" onChange={(event) => setCode(event.target.value)} value={code} />
|
||||
<Input label="字段名称" onChange={(event) => setName(event.target.value)} value={name} />
|
||||
<Select
|
||||
label="字段类型"
|
||||
onChange={(event) => setFieldType(event.target.value)}
|
||||
options={[
|
||||
{ label: '字符串', value: 'string' },
|
||||
{ label: '数字', value: 'number' },
|
||||
{ label: '文件', value: 'file' },
|
||||
{ label: '图片', value: 'image' },
|
||||
{ label: '网址', value: 'url' },
|
||||
]}
|
||||
value={fieldType}
|
||||
label="报备字段库字段"
|
||||
onChange={(event) => setDrainageFieldId(event.target.value)}
|
||||
options={[{ label: '请选择字段', value: '' }, ...libraryFields.map((field) => ({ label: `${field.name ?? field.code}(${field.code})`, value: field.id }))]}
|
||||
value={drainageFieldId}
|
||||
/>
|
||||
<Select label="报备用途" onChange={(event) => setReportType(event.target.value as typeof reportType)} options={[{ label: '签名报备', value: 'signature' }, { label: '引流信息报备', value: 'drainage' }, { label: '签名+引流', value: 'both' }]} value={reportType} />
|
||||
<Select label="是否必填" onChange={(event) => setRequired(event.target.value === 'true')} options={[{ label: '选填', value: 'false' }, { label: '必填', value: 'true' }]} value={String(required)} />
|
||||
<Textarea label="说明" onChange={(event) => setDescription(event.target.value)} rows={4} value={description} />
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { ChevronDown, ChevronRight, Edit3, FileText, Info, Plus, Search, Trash2, Upload } from 'lucide-react';
|
||||
import { adminApi, type ClientSmsApplication, type ClientSmsSignature, type FileRef, type TenantOption } from '@/api/adminApi';
|
||||
import { adminApi, type ApplicationReportField, type ClientSmsApplication, type ClientSmsSignature, type FileRef, type TenantOption } from '@/api/adminApi';
|
||||
import { Breadcrumb, Button, FileActions, Input, Modal, Pagination, Select, Tabs, Tag, Textarea } from '@/components/ui';
|
||||
import { displayFileName } from '@/utils/fileName';
|
||||
import { formatDateTime } from '@/utils/dateTime';
|
||||
@@ -26,9 +26,11 @@ type DrainageInfo = {
|
||||
telecom: CarrierStatus;
|
||||
submittedAt: string;
|
||||
remark: string;
|
||||
reportValues: ReportValues;
|
||||
};
|
||||
|
||||
type UploadedFileRef = FileRef;
|
||||
type ReportValues = Record<string, string | UploadedFileRef | null>;
|
||||
|
||||
type SignatureProfile = {
|
||||
basis: string;
|
||||
@@ -55,6 +57,7 @@ type SignatureFormState = {
|
||||
mobile: CarrierStatus;
|
||||
unicom: CarrierStatus;
|
||||
telecom: CarrierStatus;
|
||||
reportValues: ReportValues;
|
||||
};
|
||||
|
||||
const statusLabelMap: Record<CarrierStatus, string> = {
|
||||
@@ -88,6 +91,7 @@ function readDrainagePayload(signature: ClientSmsSignature) {
|
||||
telecom: normalizeCarrierStatus(carrierStatus.telecom, fallbackStatus),
|
||||
},
|
||||
signatureProfile: normalizeSignatureProfile(profile, signature),
|
||||
signatureReportValues: normalizeReportValues(payload.signatureReportValues),
|
||||
links: links.map((item) => ({
|
||||
id: String(item.id ?? `drain-${Date.now()}`),
|
||||
siteName: String(item.siteName ?? ''),
|
||||
@@ -107,12 +111,22 @@ function readDrainagePayload(signature: ClientSmsSignature) {
|
||||
telecom: normalizeCarrierStatus(item.telecom, 'filing'),
|
||||
submittedAt: String(item.submittedAt ?? ''),
|
||||
remark: String(item.remark ?? ''),
|
||||
reportValues: normalizeReportValues(item.reportValues),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function buildDrainagePayload(carrierStatus: { mobile: CarrierStatus; unicom: CarrierStatus; telecom: CarrierStatus }, links: DrainageInfo[], signatureProfile?: SignatureProfile) {
|
||||
return { carrierStatus, links, signatureProfile };
|
||||
function buildDrainagePayload(carrierStatus: { mobile: CarrierStatus; unicom: CarrierStatus; telecom: CarrierStatus }, links: DrainageInfo[], signatureProfile?: SignatureProfile, signatureReportValues?: ReportValues) {
|
||||
return { carrierStatus, links, signatureProfile, signatureReportValues };
|
||||
}
|
||||
|
||||
function normalizeReportValues(value: unknown): ReportValues {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
|
||||
return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([key, item]) => [key, normalizeUploadedFile(item) ?? String(item ?? '')]));
|
||||
}
|
||||
|
||||
function hasMissingRequiredReportValue(fields: ApplicationReportField[], values: ReportValues) {
|
||||
return fields.some((field) => field.required && !values[field.code]);
|
||||
}
|
||||
|
||||
function normalizeUploadedFile(value: unknown): UploadedFileRef | null {
|
||||
@@ -224,6 +238,67 @@ function SignatureUploadBox({
|
||||
);
|
||||
}
|
||||
|
||||
function DynamicReportFields({ fields, onChange, title, values }: { fields: ApplicationReportField[]; onChange: (code: string, value: string | UploadedFileRef | null) => void; title: string; values: ReportValues }) {
|
||||
const [explanationOpen, setExplanationOpen] = useState(false);
|
||||
if (fields.length === 0) return null;
|
||||
const channels = Array.from(new Map(fields.flatMap((field) => field.channels).map((channel) => [channel.id, channel])).values());
|
||||
const groups = Array.from(new Map(channels.map((channel) => [channel.groupId, channel.groupName])).entries());
|
||||
const requiredCount = fields.filter((field) => field.required).length;
|
||||
return (
|
||||
<section>
|
||||
<div className="report-requirement-heading">
|
||||
<h3>{title}</h3>
|
||||
<Button icon={<Info size={15} />} onClick={() => setExplanationOpen(true)} size="sm" variant="ghost">为什么需要这些资料?</Button>
|
||||
</div>
|
||||
<div className="signature-alert">
|
||||
<Info size={18} />
|
||||
<span>当前要求由 {groups.length} 个通道组、{channels.length} 个通道合并生成,共 {fields.length} 项,其中 {requiredCount} 项必填。保存时会固化本次要求快照。</span>
|
||||
</div>
|
||||
<div className="signature-form-grid">
|
||||
{fields.map((field) => {
|
||||
const channelHint = field.channels.map((channel) => channel.name).join('、');
|
||||
const requiredChannels = field.required ? field.channels.filter((channel) => channel.required).map((channel) => channel.name).join('、') : '';
|
||||
const label = `${field.required ? '* ' : ''}${field.name}`;
|
||||
const hint = field.required
|
||||
? `由 ${requiredChannels} 要求,至少一个通道配置为必填`
|
||||
: `适用通道:${channelHint}`;
|
||||
return field.fieldType === 'file' || field.fieldType === 'image' ? (
|
||||
<div key={field.id}>
|
||||
<SignatureUploadBox compact file={typeof values[field.code] === 'object' ? values[field.code] as UploadedFileRef : null} label={label} onUploaded={(file) => onChange(field.code, file)} />
|
||||
<small className="report-field-source">{hint}</small>
|
||||
</div>
|
||||
) : (
|
||||
<div key={field.id}>
|
||||
<Input label={label} onChange={(event) => onChange(field.code, event.target.value)} placeholder={field.description ?? `请输入${field.name}`} required={field.required} value={typeof values[field.code] === 'string' ? values[field.code] as string : ''} />
|
||||
<small className="report-field-source">{hint}</small>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Modal footer={<Button onClick={() => setExplanationOpen(false)}>我知道了</Button>} onClose={() => setExplanationOpen(false)} open={explanationOpen} size="xl" title="这些资料从哪里来?">
|
||||
<div className="report-requirement-explanation">
|
||||
<p>资料要求按“企业应用 → 通道组 → 通道 → 通道报备字段”实时计算;相同字段只填写一次,但会按来源通道分别用于报备。</p>
|
||||
{groups.map(([groupId, groupName]) => (
|
||||
<section className="report-source-group" key={groupId}>
|
||||
<h4>通道组:{groupName}</h4>
|
||||
{channels.filter((channel) => channel.groupId === groupId).map((channel) => (
|
||||
<div className="report-source-channel" key={channel.id}>
|
||||
<strong>{channel.name}({channel.code})</strong>
|
||||
<ul>
|
||||
{fields.filter((field) => field.channels.some((source) => source.id === channel.id)).map((field) => (
|
||||
<li key={field.id}>{field.name} · {field.channels.find((source) => source.id === channel.id)?.reportType === 'both' ? '签名和引流共用' : field.channels.find((source) => source.id === channel.id)?.reportType === 'signature' ? '签名报备' : '引流信息报备'} · {field.channels.find((source) => source.id === channel.id)?.required ? '必填' : '选填'}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function SignatureFormModal({
|
||||
applications,
|
||||
item,
|
||||
@@ -247,9 +322,19 @@ function SignatureFormModal({
|
||||
mobile: payload?.carrierStatus.mobile ?? 'filing',
|
||||
unicom: payload?.carrierStatus.unicom ?? 'filing',
|
||||
telecom: payload?.carrierStatus.telecom ?? 'filing',
|
||||
reportValues: payload?.signatureReportValues ?? {},
|
||||
});
|
||||
const [reportFields, setReportFields] = useState<ApplicationReportField[]>([]);
|
||||
const tenantApplications = applications.filter((application) => application.tenantId === form.tenantId && application.status !== 'deleted');
|
||||
|
||||
useEffect(() => {
|
||||
if (!form.applicationId) {
|
||||
setReportFields([]);
|
||||
return;
|
||||
}
|
||||
adminApi.listApplicationReportFields(form.applicationId, 'signature').then(setReportFields).catch(() => setReportFields([]));
|
||||
}, [form.applicationId]);
|
||||
|
||||
function update<Key extends keyof SignatureFormState>(key: Key, value: SignatureFormState[Key]) {
|
||||
setForm((current) => ({ ...current, [key]: value }));
|
||||
}
|
||||
@@ -258,12 +343,16 @@ function SignatureFormModal({
|
||||
setForm((current) => ({ ...current, profile: { ...current.profile, [key]: value } }));
|
||||
}
|
||||
|
||||
function updateReportValue(code: string, value: string | UploadedFileRef | null) {
|
||||
setForm((current) => ({ ...current, reportValues: { ...current.reportValues, [code]: value } }));
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
footer={(
|
||||
<>
|
||||
<Button onClick={onClose} variant="ghost">取消</Button>
|
||||
<Button disabled={!form.tenantId || !form.name} onClick={() => onSubmit(form)}>保存</Button>
|
||||
<Button disabled={!form.tenantId || !form.name || hasMissingRequiredReportValue(reportFields, form.reportValues)} onClick={() => onSubmit(form)}>保存</Button>
|
||||
</>
|
||||
)}
|
||||
onClose={onClose}
|
||||
@@ -347,12 +436,15 @@ function SignatureFormModal({
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<DynamicReportFields fields={reportFields} onChange={updateReportValue} title="应用通道签名报备资料" values={form.reportValues} />
|
||||
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function DrainageFormModal({ item, onClose, onSubmit }: { item?: DrainageInfo; onClose: () => void; onSubmit: (item: DrainageInfo) => void }) {
|
||||
function DrainageFormModal({ applicationId, item, onClose, onSubmit }: { applicationId?: string | null; item?: DrainageInfo; onClose: () => void; onSubmit: (item: DrainageInfo) => void }) {
|
||||
const [reportFields, setReportFields] = useState<ApplicationReportField[]>([]);
|
||||
const [form, setForm] = useState<DrainageInfo>(() => item ?? {
|
||||
id: `drain-${Date.now()}`,
|
||||
siteName: '',
|
||||
@@ -372,18 +464,28 @@ function DrainageFormModal({ item, onClose, onSubmit }: { item?: DrainageInfo; o
|
||||
telecom: 'filing',
|
||||
submittedAt: formatDateTime(new Date()),
|
||||
remark: '',
|
||||
reportValues: {},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!applicationId) return;
|
||||
adminApi.listApplicationReportFields(applicationId, 'drainage').then(setReportFields).catch(() => setReportFields([]));
|
||||
}, [applicationId]);
|
||||
|
||||
function update<Key extends keyof DrainageInfo>(key: Key, value: DrainageInfo[Key]) {
|
||||
setForm((current) => ({ ...current, [key]: value }));
|
||||
}
|
||||
|
||||
function updateReportValue(code: string, value: string | UploadedFileRef | null) {
|
||||
setForm((current) => ({ ...current, reportValues: { ...current.reportValues, [code]: value } }));
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
footer={(
|
||||
<>
|
||||
<Button onClick={onClose} variant="ghost">取消</Button>
|
||||
<Button disabled={!form.url} onClick={() => onSubmit(form)}>保存</Button>
|
||||
<Button disabled={!form.url || hasMissingRequiredReportValue(reportFields, form.reportValues)} onClick={() => onSubmit(form)}>保存</Button>
|
||||
</>
|
||||
)}
|
||||
onClose={onClose}
|
||||
@@ -410,19 +512,11 @@ function DrainageFormModal({ item, onClose, onSubmit }: { item?: DrainageInfo; o
|
||||
</ol>
|
||||
</div>
|
||||
<div className="signature-form-grid">
|
||||
<SignatureUploadBox compact file={form.field1File} label="* 字段名称1" onUploaded={(file) => update('field1File', file)} />
|
||||
<Input label="* 字段名称2" onChange={(event) => update('field2', event.target.value)} placeholder="请输入字段2内容" value={form.field2 ?? ''} />
|
||||
<Input label="* 字段名称3" onChange={(event) => { update('field3', event.target.value); update('siteName', event.target.value); }} placeholder="请输入公司名称" value={form.field3 ?? form.siteName} />
|
||||
<Input label="字段名称4" onChange={(event) => update('field4', event.target.value)} placeholder="请输入统一社会信用代码" value={form.field4 ?? ''} />
|
||||
<Input label="* 字段名称5" onChange={(event) => update('field5', event.target.value)} placeholder="请输入法人姓名" value={form.field5 ?? ''} />
|
||||
<Input label="字段名称6" onChange={(event) => update('field6', event.target.value)} placeholder="请输入法人身份证号" value={form.field6 ?? ''} />
|
||||
<SignatureUploadBox compact file={form.field7File} label="字段名称7" onUploaded={(file) => update('field7File', file)} />
|
||||
<Input label="* 字段名称8" onChange={(event) => update('field8', event.target.value)} placeholder="请输入责任人身份证号" value={form.field8 ?? ''} />
|
||||
<Input label="* 字段名称9" onChange={(event) => update('field9', event.target.value)} placeholder="请输入责任人姓名" value={form.field9 ?? ''} />
|
||||
<Input label="* 字段名称10" onChange={(event) => update('field10', event.target.value)} placeholder="请输入责任人手机号" value={form.field10 ?? ''} />
|
||||
<Input label="* 站名称" onChange={(event) => update('siteName', event.target.value)} placeholder="请输入站名称" value={form.siteName} />
|
||||
<Input label="提交时间" onChange={(event) => update('submittedAt', event.target.value)} value={form.submittedAt} />
|
||||
<Textarea className="signature-form-grid__wide" label="备注" onChange={(event) => update('remark', event.target.value)} rows={4} value={form.remark} />
|
||||
</div>
|
||||
<DynamicReportFields fields={reportFields} onChange={updateReportValue} title="应用通道引流信息报备资料" values={form.reportValues} />
|
||||
</section>
|
||||
</div>
|
||||
</Modal>
|
||||
@@ -544,7 +638,7 @@ export function AdminEnterpriseSignaturesPage() {
|
||||
mobile: state.mobile,
|
||||
unicom: state.unicom,
|
||||
telecom: state.telecom,
|
||||
}, existingPayload.links, state.profile);
|
||||
}, existingPayload.links, state.profile, state.reportValues);
|
||||
try {
|
||||
if (existing) {
|
||||
await adminApi.updateEnterpriseSignature(existing.id, {
|
||||
@@ -579,7 +673,7 @@ export function AdminEnterpriseSignaturesPage() {
|
||||
? payload.links.map((current) => current.id === item.id ? item : current)
|
||||
: [item, ...payload.links];
|
||||
await adminApi.updateEnterpriseSignature(signatureId, {
|
||||
drainageInfo: buildDrainagePayload(payload.carrierStatus, links, payload.signatureProfile),
|
||||
drainageInfo: buildDrainagePayload(payload.carrierStatus, links, payload.signatureProfile, payload.signatureReportValues),
|
||||
});
|
||||
setDrainageModal(null);
|
||||
setExpandedSignatureId(signatureId);
|
||||
@@ -597,7 +691,7 @@ export function AdminEnterpriseSignaturesPage() {
|
||||
if (signature) {
|
||||
const payload = readDrainagePayload(signature);
|
||||
await adminApi.updateEnterpriseSignature(signature.id, {
|
||||
drainageInfo: buildDrainagePayload(payload.carrierStatus, payload.links.filter((item) => item.id !== deleteTarget.id), payload.signatureProfile),
|
||||
drainageInfo: buildDrainagePayload(payload.carrierStatus, payload.links.filter((item) => item.id !== deleteTarget.id), payload.signatureProfile, payload.signatureReportValues),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -740,6 +834,7 @@ export function AdminEnterpriseSignaturesPage() {
|
||||
{signatureReport ? <SignatureReportModal item={signatureReport} onClose={() => setSignatureReport(null)} /> : null}
|
||||
{drainageModal ? (
|
||||
<DrainageFormModal
|
||||
applicationId={signatures.find((item) => item.id === drainageModal.signatureId)?.applicationId}
|
||||
item={drainageModal.item}
|
||||
onClose={() => setDrainageModal(null)}
|
||||
onSubmit={(item) => { void saveDrainage(drainageModal.signatureId, item); }}
|
||||
|
||||
@@ -3060,6 +3060,48 @@ h3 {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.report-requirement-heading {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.report-requirement-heading h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.report-field-source {
|
||||
color: var(--color-text-muted);
|
||||
display: block;
|
||||
line-height: 1.6;
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
.report-requirement-explanation > p {
|
||||
background: var(--color-selected-soft);
|
||||
border: 1px solid #bfdbfe;
|
||||
line-height: 1.7;
|
||||
padding: var(--space-4);
|
||||
}
|
||||
|
||||
.report-source-group {
|
||||
border-left: 3px solid var(--color-selected);
|
||||
margin-top: var(--space-5);
|
||||
padding-left: var(--space-4);
|
||||
}
|
||||
|
||||
.report-source-channel {
|
||||
background: var(--color-bg-subtle);
|
||||
margin-top: var(--space-3);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
}
|
||||
|
||||
.report-source-channel ul {
|
||||
color: var(--color-text-muted);
|
||||
line-height: 1.8;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.signature-upload {
|
||||
align-items: center;
|
||||
border: 2px dashed var(--color-border);
|
||||
|
||||
Reference in New Issue
Block a user