import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Button, Checkbox, Col, Form, Input, InputNumber, message, Row, Space, } from "antd"; import { useEffect, useState } from "react"; import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout"; // 2026-08-08 变更:按原型 V1.6 institution.html 的 #mod-org-info 模块 1:1 还原机构基础信息表单。 // 复用既有「安全生产监管行业类别」枚举作为「拟申请的法定安全评价业务范围」候选项, // 二者取值集合完全一致(煤矿/金属非金属矿/陆地油气/油气管道/石油化工医药/烟花爆竹/金属冶炼),无需新增枚举。 import { QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate/enterpriseOptions"; import { NS_ORG_INFO } from "~/enumerate/namespace"; import { creditCodeRule, nonNegativeIntegerRule, normalizeUrl, phoneRule, positiveNumberRule, urlRule, } from "~/utils/validators"; /** * 2026-08-08 新增:业务范围在后端为 varchar(500) 的「逗号分隔字符串」(apply_business_scope), * 而原型要求多选交互,故在页面出入口做数组/字符串互转。 * 不做字段名映射,仅做同名字段的值形态转换,保持前后端字段名一致。 */ const splitBusinessScope = (value) => { if (Array.isArray(value)) return value; if (!value) return undefined; return String(value) .split(",") .map((item) => item.trim()) .filter(Boolean); }; const joinBusinessScope = (value) => Array.isArray(value) ? value.join(",") : value || undefined; /** * 2026-08-08 联系人及电话合并/拆分工具(交互约定见下方说明): * 录入:单输入框,值格式「联系人/电话」,用 / 分隔。 * 提交前:按首个 / 拆为 contactName(/前)+ contactPhone(/后)两字段,传给后端。 * 回显:后端 contactName/contactPhone 用 / 合并回单输入框。 * 后端落库:contact_name varchar(50)、contact_phone varchar(20);原 contact_name_phone 列保留不删、不再写入。 * 复用要点:其他页面如需同样交互,保持「单输入框 + / 分隔 + 提交拆分存两列 + 回显合并」即可。 * 注意:分隔符为单斜杠「/」(无空格),与录入示例"张三/13800138000"一致。 */ const CONTACT_SEP = "/"; const mergeContactNamePhone = (name, phone) => { if (!name && !phone) return undefined; return [name, phone].filter(Boolean).join(CONTACT_SEP); }; const splitContactNamePhone = (value) => { if (!value) return { contactName: undefined, contactPhone: undefined }; const idx = String(value).indexOf(CONTACT_SEP); if (idx >= 0) { return { // 按首个 / 拆分;/后全部作为电话(剔除首尾空白),多余字符交给校验/提交时处理 contactName: String(value).slice(0, idx).trim(), contactPhone: String(value).slice(idx + 1).trim(), }; } // 无分隔符:整体作为联系人姓名,电话置空(由校验拦截) return { contactName: String(value).trim(), contactPhone: undefined }; }; function OrgInfoPage(props) { const [form] = Form.useForm(); const { orgInfoLoading } = props.orgInfo; const [editing, setEditing] = useState(true); const [submitting, setSubmitting] = useState(false); const [detail, setDetail] = useState({}); /** 是否已存在机构数据(有 id 视为已入库,只能修改) */ const [hasExistingData, setHasExistingData] = useState(false); const loadDetail = async () => { try { const res = await props.orgInfoGet(); if (res?.data?.id) { setDetail(res.data); form.setFieldsValue({ ...res.data, // 2026-08-08 业务范围回填为数组以适配多选控件 applyBusinessScope: splitBusinessScope(res.data.applyBusinessScope), // 2026-08-08 联系人及电话:后端 contactName/contactPhone 两列合并为单字段展示 contactNamePhone: mergeContactNamePhone( res.data.contactName, res.data.contactPhone, ), }); setHasExistingData(true); setEditing(false); } else { setDetail({}); setHasExistingData(false); setEditing(true); } } catch (err) { console.warn("[OrgInfo] loadDetail failed:", err); setDetail({}); setHasExistingData(false); setEditing(true); } }; useEffect(() => { loadDetail(); }, []); const handleCancelEdit = () => { setEditing(false); }; const handleSave = async (submitType) => { try { const formValues = await form.validateFields(); // 2026-08-08 联系人及电话:单字段拆分为后端 contactName / contactPhone 两列提交 const split = splitContactNamePhone(formValues.contactNamePhone); // 剔除多余字符:姓名/电话做 trim 与空白清理(校验已保证格式,此处仅兜底) const contactName = split.contactName ? split.contactName.replace(/\s+/g, "") : undefined; const contactPhone = split.contactPhone ? split.contactPhone.replace(/\s+/g, "") : undefined; const values = { ...formValues, contactName, contactPhone, // 移除仅用于展示的合并字段,避免提交冗余 contactNamePhone: undefined, // 2026-08-08 多选业务范围按后端 varchar(500) 约定以逗号拼接提交 applyBusinessScope: joinBusinessScope(formValues.applyBusinessScope), infoDisclosureUrl: formValues.infoDisclosureUrl ? normalizeUrl(formValues.infoDisclosureUrl) : undefined, authStatusCode: submitType === "draft" ? 0 : 1, authStatusName: submitType === "draft" ? "草稿" : "已提交", }; setSubmitting(true); let request; if (hasExistingData) { request = props.orgInfoModify; values.id = detail.id; } else { request = submitType === "draft" ? props.orgInfoDraft : props.orgInfoSave; } const res = await request(values); if (res?.success !== false) { message.success( hasExistingData ? "修改成功" : submitType === "draft" ? "暂存成功" : "提交成功", ); setEditing(false); loadDetail(); } } finally { setSubmitting(false); } }; return ( {/* 2026-08-08 标题对齐原型 institution.html #mod-org-info(副标题按需求移除) */} 基础信息管理 } extra={ hasExistingData && !editing && ( ) } > {/* 2026-08-08 布局对齐原型 institution.html: 原型 .form-grid 为两列网格、label 在控件上方(纵向),gap 1rem。 此处使用 antd Form layout="vertical" + Row/Col 两列还原,整行字段 span=24。 2026-08-08 按原型 .info-card 增加带边框容器并整体居中(border-radius 8px、padding 1.25rem、maxWidth 960px 居中)。 */}
{/* 2026-08-08 字段顺序严格对齐原型 institution.html 434-450 行 */} {/* 原型:注册地址占整行(full) */} {/* 原型:办公地址占整行(full),TextArea rows=3 */} typeof value === "string" ? value.trim() : value } > {/* 原型:资质证书编号非必填,带"非必填,初次申请无需填写"提示 */} {/* 2026-08-08 标签对齐原型「法定代表人电话」 */} typeof value === "string" ? value.trim() : value } > {/* 2026-08-08 联系人及电话(交互约定): 单输入框录入,格式「联系人/电话」(用 / 分隔),如:张三/13800138000; 提交前按首个 / 拆为 contactName(/前)+ contactPhone(/后)存两列; 回显时后端 contactName/contactPhone 用 / 合并回填。 校验:拆分后 contactName 非空且≤50、contactPhone 非空且匹配手机/座机格式且≤20。 */} { const { contactName, contactPhone } = splitContactNamePhone(value); if (!contactName) { return Promise.reject(new Error("联系人不能为空")); } if (contactName.length > 50) { return Promise.reject(new Error("联系人不能超过50字")); } if (!contactPhone) { return Promise.reject( new Error("请使用/分隔联系人和电话,并填写电话"), ); } // 复用既有 PHONE_PATTERN:手机11位 / 固话10~12位(去空白后) if ( !/^(1[3-9]\d{9}|\d{10,12})$/.test( String(contactPhone).replace(/\s+/g, ""), ) ) { return Promise.reject( new Error("电话格式不正确(手机11位或固话区号+号码)"), ); } if (contactPhone.length > 20) { return Promise.reject(new Error("电话不能超过20字")); } return Promise.resolve(); }, }, ]} extra="请使用/分隔联系人和电话,如:张三/13800138000" > { if (value === undefined || value === null || value === "") { return Promise.resolve(); } if (Number(value) < 0) { return Promise.reject( new Error("固定资产总值应为非负数字"), ); } return Promise.resolve(); }, }, ]} > {/* 2026-08-08 业务范围: 原型为 checkbox 网格(2列、灰底 #f8fafc、边框容器),此处用 Checkbox.Group + Row/Col 还原视觉。 */} {QUALIFICATION_INDUSTRY_OPTIONS.map((opt) => ( {opt.label} ))} {/* 原型:单位基本情况介绍(可附页),TextArea rows=7 */} {editing && (
{!hasExistingData && ( )}
)}
); } export default Connect([NS_ORG_INFO], true)(OrgInfoPage);