dev-tmp1
tangjie 2026-08-14 17:32:18 +08:00
parent 8eeb411021
commit 61333696b4
3 changed files with 283 additions and 93 deletions

View File

@ -4,7 +4,7 @@ import { PlusOutlined } from "@ant-design/icons";
const isImage = (url) => const isImage = (url) =>
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || ""); /\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
export default function AttachmentUpload({ name, label, disabled = false, maxCount, accept, extra ,rules, style }) { export default function AttachmentUpload({ name, label, disabled, maxCount, accept, extra ,rules, style }) {
const [previewImage, setPreviewImage] = useState(""); const [previewImage, setPreviewImage] = useState("");
return ( return (

View File

@ -18,6 +18,7 @@ import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
// 二者取值集合完全一致(煤矿/金属非金属矿/陆地油气/油气管道/石油化工医药/烟花爆竹/金属冶炼),无需新增枚举。 // 二者取值集合完全一致(煤矿/金属非金属矿/陆地油气/油气管道/石油化工医药/烟花爆竹/金属冶炼),无需新增枚举。
import { QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate/enterpriseOptions"; import { QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate/enterpriseOptions";
import { NS_ORG_INFO } from "~/enumerate/namespace"; import { NS_ORG_INFO } from "~/enumerate/namespace";
import AttachmentUpload from "~/components/AttachmentUpload";
import { import {
creditCodeRule, creditCodeRule,
@ -73,8 +74,13 @@ function OrgInfoPage(props) {
if (res?.data?.id) { if (res?.data?.id) {
sessionStorage.setItem("orgInfo", JSON.stringify(res.data)); sessionStorage.setItem("orgInfo", JSON.stringify(res.data));
setDetail(res.data); setDetail(res.data);
let attachmentUrls = res.data.attachmentUrls;
if (typeof attachmentUrls === "string" && attachmentUrls) {
try { attachmentUrls = JSON.parse(attachmentUrls); } catch {}
}
form.setFieldsValue({ form.setFieldsValue({
...res.data, ...res.data,
attachmentUrls,
// 2026-08-08 联系人及电话:后端 contactName/contactPhone 两列合并为单字段展示 // 2026-08-08 联系人及电话:后端 contactName/contactPhone 两列合并为单字段展示
contactNamePhone: mergeContactNamePhone( contactNamePhone: mergeContactNamePhone(
res.data.contactName, res.data.contactName,
@ -122,6 +128,9 @@ function OrgInfoPage(props) {
contactPhone, contactPhone,
// 移除仅用于展示的合并字段,避免提交冗余 // 移除仅用于展示的合并字段,避免提交冗余
contactNamePhone: undefined, contactNamePhone: undefined,
attachmentUrls: formValues.attachmentUrls
? JSON.stringify(formValues.attachmentUrls)
: undefined,
infoDisclosureUrl: formValues.infoDisclosureUrl infoDisclosureUrl: formValues.infoDisclosureUrl
? normalizeUrl(formValues.infoDisclosureUrl) ? normalizeUrl(formValues.infoDisclosureUrl)
: undefined, : undefined,
@ -198,6 +207,17 @@ function OrgInfoPage(props) {
/> />
</Form.Item> </Form.Item>
</Col> </Col>
{/* 营业执照附件 */}
<Col span={24}>
<AttachmentUpload
name="attachmentUrls"
label="营业执照"
accept=".pdf,.jpg,.jpeg,.png"
maxCount={5}
extra="支持上传营业执照扫描件限定pdf和图片最多5个。"
rules={[{ required: true, message: "请上传营业执照" }]}
/>
</Col>
{/* 原型注册地址占整行full */} {/* 原型注册地址占整行full */}
<Col span={24}> <Col span={24}>
<Form.Item <Form.Item

View File

@ -1,5 +1,6 @@
import { import {
Button, Button,
Checkbox,
DatePicker, DatePicker,
Descriptions, Descriptions,
Form, Form,
@ -26,14 +27,20 @@ import { QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate/enterpriseOptions";
import { isOrgAccountEnabled } from "~/utils/enterpriseInfo/adapter"; import { isOrgAccountEnabled } from "~/utils/enterpriseInfo/adapter";
import { import {
CHONGQING_DISTRICTS, CHONGQING_DISTRICTS,
ENTERPRISE_SCALE_OPTIONS,
ORG_ACCOUNT_STATE_OPTIONS, ORG_ACCOUNT_STATE_OPTIONS,
ENTERPRISE_STATUS_OPTIONS,
ENTERPRISE_STATUS_OPTIONS_MAP,
QUALIFICATION_INDUSTRY_OPTIONS_MAP QUALIFICATION_INDUSTRY_OPTIONS_MAP
} from "~/enumerate/enterpriseOptions"; } from "~/enumerate/enterpriseOptions";
import { NS_ORG_INFO } from "~/enumerate/namespace"; import { NS_ORG_INFO } from "~/enumerate/namespace";
import AttachmentUpload from "~/components/AttachmentUpload";
import { safeListRequest, safeRequest } from "~/utils"; import { safeListRequest, safeRequest } from "~/utils";
import {
creditCodeRule,
nonNegativeIntegerRule,
normalizeUrl,
phoneRule,
positiveNumberRule,
urlRule,
} from "~/utils/validators";
const { router } = tools; const { router } = tools;
@ -123,8 +130,13 @@ function OrgAccountPage(props) {
setEditOpen(true); setEditOpen(true);
const res = await loadDetail(id); const res = await loadDetail(id);
if (res?.data) { if (res?.data) {
let attachmentUrls = res.data.attachmentUrls;
if (typeof attachmentUrls === "string" && attachmentUrls) {
try { attachmentUrls = JSON.parse(attachmentUrls); } catch {}
}
editForm.setFieldsValue({ editForm.setFieldsValue({
...res.data, ...res.data,
attachmentUrls,
contactNamePhone: mergeContactNamePhone( contactNamePhone: mergeContactNamePhone(
res.data.contactName, res.data.contactName,
res.data.contactPhone, res.data.contactPhone,
@ -214,11 +226,23 @@ function OrgAccountPage(props) {
const onEditSubmit = async () => { const onEditSubmit = async () => {
const formValues = await editForm.validateFields(); const formValues = await editForm.validateFields();
const split = splitContactNamePhone(formValues.contactNamePhone); const split = splitContactNamePhone(formValues.contactNamePhone);
const contactName = split.contactName
? split.contactName.replace(/\s+/g, "")
: undefined;
const contactPhone = split.contactPhone
? split.contactPhone.replace(/\s+/g, "")
: undefined;
const values = { const values = {
...formValues, ...formValues,
contactName: split.contactName, contactName,
contactPhone: split.contactPhone, contactPhone,
contactNamePhone: undefined, contactNamePhone: undefined,
attachmentUrls: formValues.attachmentUrls
? JSON.stringify(formValues.attachmentUrls)
: undefined,
infoDisclosureUrl: formValues.infoDisclosureUrl
? normalizeUrl(formValues.infoDisclosureUrl)
: undefined,
id: currentId, id: currentId,
}; };
setEditLoading(true); setEditLoading(true);
@ -234,9 +258,17 @@ function OrgAccountPage(props) {
}; };
const columns = [ const columns = [
{ title: "机构名称", dataIndex: "unitName", ellipsis: true }, { title: "单位名称", dataIndex: "unitName", ellipsis: true },
{ title: "账号", dataIndex: "account", ellipsis: true }, {
{ title: "属地", dataIndex: "districtName", width: 260 }, title: "拟申请的法定安全评价业务范围",
dataIndex: "safetyIndustryCategoryCode",
ellipsis: true,
render: (codes) =>
(Array.isArray(codes) ? codes : [])
.map((code) => QUALIFICATION_INDUSTRY_OPTIONS_MAP[code] || code)
.join("、") || "-",
},
{ title: "办公地址", dataIndex: "businessAddress", ellipsis: true },
{ {
title: "状态", title: "状态",
width: 80, width: 80,
@ -382,7 +414,7 @@ function OrgAccountPage(props) {
open={viewOpen} open={viewOpen}
destroyOnHidden destroyOnHidden
title="机构信息查看" title="机构信息查看"
width={760} width={900}
loading={detailLoading} loading={detailLoading}
cancelText="关闭" cancelText="关闭"
okButtonProps={{ style: { display: "none" } }} okButtonProps={{ style: { display: "none" } }}
@ -393,38 +425,74 @@ function OrgAccountPage(props) {
> >
{detail && ( {detail && (
<Descriptions bordered column={2} size="small"> <Descriptions bordered column={2} size="small">
<Descriptions.Item label="机构名称"> <Descriptions.Item label="单位名称">
{detail.unitName} {detail.unitName}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="统一社会信用代码"> <Descriptions.Item label="统一社会信用代码">
{detail.creditCode} {detail.creditCode}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="属地"> <Descriptions.Item label="营业执照" span={2}>
{detail.districtName} {(() => {
let files = [];
if (Array.isArray(detail.attachmentUrls)) {
files = detail.attachmentUrls;
} else if (typeof detail.attachmentUrls === "string" && detail.attachmentUrls) {
try { files = JSON.parse(detail.attachmentUrls); } catch {}
}
if (!files.length) return "-";
return files.map((f, i) => (
<a key={i} href={f.url} target="_blank" rel="noreferrer" style={{ marginRight: 8 }}>
{f.name || f.url}
</a>
));
})()}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="所属行业"> <Descriptions.Item label="注册地址" span={2}>
{detail.registerAddress || "-"}
</Descriptions.Item>
<Descriptions.Item label="办公地址" span={2}>
{detail.businessAddress || "-"}
</Descriptions.Item>
<Descriptions.Item label="信息公开网址">
{detail.infoDisclosureUrl || "-"}
</Descriptions.Item>
<Descriptions.Item label="资质证书编号">
{detail.qualificationCertNo || "-"}
</Descriptions.Item>
<Descriptions.Item label="法定代表人">
{detail.legalRepresentative || "-"}
</Descriptions.Item>
<Descriptions.Item label="法定代表人电话">
{detail.legalRepresentativePhone || "-"}
</Descriptions.Item>
<Descriptions.Item label="传真">
{detail.fax || "-"}
</Descriptions.Item>
<Descriptions.Item label="联系人及电话">
{mergeContactNamePhone(detail.contactName, detail.contactPhone) || "-"}
</Descriptions.Item>
<Descriptions.Item label="固定资产总值(万元)">
{detail.fixedAssetsTotal ?? "-"}
</Descriptions.Item>
<Descriptions.Item label="工作场所建筑面积(㎡)">
{detail.workplaceArea ?? "-"}
</Descriptions.Item>
<Descriptions.Item label="档案室面积(㎡)">
{detail.archiveRoomArea ?? "-"}
</Descriptions.Item>
<Descriptions.Item label="专职安全评价师数量">
{detail.fulltimeEvaluatorCount ?? "-"}
</Descriptions.Item>
<Descriptions.Item label="注册安全工程师数量">
{detail.registeredEngineerCount ?? "-"}
</Descriptions.Item>
<Descriptions.Item label="拟申请的法定安全评价业务范围" span={2}>
{(detail.safetyIndustryCategoryCode || []) {(detail.safetyIndustryCategoryCode || [])
.map((code) => QUALIFICATION_INDUSTRY_OPTIONS_MAP[code] || code) .map((code) => QUALIFICATION_INDUSTRY_OPTIONS_MAP[code] || code)
.join("、") || "-"} .join("、") || "-"}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="经营地址" span={2}> <Descriptions.Item label="单位基本情况介绍" span={2}>
{detail.businessAddress} {detail.orgIntro || "-"}
</Descriptions.Item>
<Descriptions.Item label="企业状态">
{ENTERPRISE_STATUS_OPTIONS_MAP[detail.enterpriseStatusCode]}
</Descriptions.Item>
<Descriptions.Item label="联系人及电话" span={2}>
{mergeContactNamePhone(detail.contactName, detail.contactPhone) || "-"}
</Descriptions.Item>
<Descriptions.Item label="法定代表人">
{detail.legalRepresentative}
</Descriptions.Item>
<Descriptions.Item label="法人电话">
{detail.legalRepresentativePhone}
</Descriptions.Item>
<Descriptions.Item label="企业规模" span={2}>
{detail.enterpriseScaleName}
</Descriptions.Item> </Descriptions.Item>
</Descriptions> </Descriptions>
)} )}
@ -434,7 +502,7 @@ function OrgAccountPage(props) {
open={editOpen} open={editOpen}
destroyOnHidden destroyOnHidden
title="编辑机构信息" title="编辑机构信息"
width={760} width={900}
confirmLoading={editLoading} confirmLoading={editLoading}
okText="保存" okText="保存"
cancelText="取消" cancelText="取消"
@ -450,58 +518,94 @@ function OrgAccountPage(props) {
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
name="unitName" name="unitName"
label="机构名称" label="单位名称"
rules={[{ required: true, message: "请输入机构名称" }]} rules={[{ required: true, message: "请输入单位名称" }]}
> >
<Input /> <Input placeholder="请输入单位名称" allowClear maxLength={200} />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
name="creditCode" name="creditCode"
label="统一社会信用代码" label="统一社会信用代码"
rules={[{ required: true, message: "请输入信用代码" }]} rules={[creditCodeRule(true)]}
> >
<Input /> <Input placeholder="请输入统一社会信用代码" allowClear maxLength={18} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="districtName" label="属地">
<Select
options={CHONGQING_DISTRICTS}
placeholder="请选择属地"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="safetyIndustryCategoryCode" label="所属行业">
<Select
mode="multiple"
placeholder="请选择"
allowClear
>
{QUALIFICATION_INDUSTRY_OPTIONS.map((opt) => (
<Select.Option key={opt.value} value={opt.value}>
{opt.label}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item name="businessAddress" label="经营地址">
<Input placeholder="请输入" allowClear />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={24}> <Col span={24}>
<Form.Item name="enterpriseStatusCode" label="企业状态"> <AttachmentUpload
<Select name="attachmentUrls"
options={ENTERPRISE_STATUS_OPTIONS} label="营业执照"
placeholder="请选择" accept=".pdf,.jpg,.jpeg,.png"
allowClear maxCount={5}
/> extra="支持上传营业执照扫描件限定pdf和图片最多5个。"
rules={[{ required: true, message: "请上传营业执照" }]}
/>
</Col>
<Col span={24}>
<Form.Item
name="registerAddress"
label="注册地址"
rules={[{ required: true, message: "请输入注册地址" }]}
>
<Input placeholder="请输入注册地址" maxLength={500} />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
name="businessAddress"
label="办公地址"
rules={[{ required: true, message: "请输入办公地址" }]}
>
<Input.TextArea rows={3} placeholder="请输入办公地址" maxLength={500} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="infoDisclosureUrl"
label="信息公开网址"
rules={[urlRule("信息公开网址", true)]}
normalize={(value) =>
typeof value === "string" ? value.trim() : value
}
>
<Input placeholder="请输入信息公开网址" allowClear maxLength={500} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="qualificationCertNo"
label="资质证书编号"
extra="非必填,初次申请无需填写"
>
<Input placeholder="初次申请无需填写" allowClear maxLength={100} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="legalRepresentative"
label="法定代表人"
rules={[{ required: true, message: "请输入法定代表人" }]}
>
<Input placeholder="请输入法定代表人" allowClear maxLength={50} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="legalRepresentativePhone"
label="法定代表人电话"
rules={[phoneRule("法定代表人电话", true)]}
normalize={(value) =>
typeof value === "string" ? value.trim() : value
}
>
<Input placeholder="请输入法定代表人电话" allowClear maxLength={20} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="fax" label="传真" rules={[{ required: true, message: "请输入传真" }]}>
<Input placeholder="请输入传真" allowClear maxLength={20} />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>
@ -552,33 +656,99 @@ function OrgAccountPage(props) {
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.Item name="legalRepresentative" label="法定代表人"> <Form.Item
<Input placeholder="请选择" allowClear /> name="fixedAssetsTotal"
label="固定资产总值(万元)"
rules={[
{ required: true, message: "请输入固定资产总值" },
{
validator: (_, value) => {
if (value === undefined || value === null || value === "") {
return Promise.resolve();
}
if (Number(value) < 0) {
return Promise.reject(new Error("固定资产总值应为非负数字"));
}
return Promise.resolve();
},
},
]}
>
<InputNumber style={{ width: "100%" }} min={0} precision={4} max={99999999999} allowClear placeholder="请输入固定资产总值" />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
name="legalRepresentativePhone" name="workplaceArea"
label="法人电话" label="工作场所建筑面积(㎡)"
rules={[ rules={[positiveNumberRule("工作场所建筑面积", true)]}
{
pattern: /(^1[3-9]\d{9}$)|(^0\d{2,3}-?\d{7,8}$)/,
message: "请输入正确的手机号或座机号",
},
]}
> >
<Input placeholder="请输入" allowClear /> <InputNumber style={{ width: "100%" }} min={0} max={99999} precision={2} placeholder="请输入工作场所建筑面积" />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.Item name="enterpriseScaleName" label="企业规模"> <Form.Item
<Select name="archiveRoomArea"
placeholder="请选择" label="档案室面积(㎡)"
allowClear rules={[positiveNumberRule("档案室面积", true)]}
options={ENTERPRISE_SCALE_OPTIONS.map((o) => ({ >
label: o.label, <InputNumber style={{ width: "100%" }} min={0} max={99999} precision={2} placeholder="请输入档案室面积" />
value: o.label, </Form.Item>
}))} </Col>
<Col span={12}>
<Form.Item
name="fulltimeEvaluatorCount"
label="专职安全评价师数量"
rules={[nonNegativeIntegerRule("专职安全评价师数量", true)]}
>
<InputNumber style={{ width: "100%" }} min={0} max={99999} precision={0} placeholder="请输入专职安全评价师数量" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="registeredEngineerCount"
label="注册安全工程师数量"
rules={[nonNegativeIntegerRule("注册安全工程师数量", true)]}
>
<InputNumber style={{ width: "100%" }} min={0} max={99999} precision={0} placeholder="请输入注册安全工程师数量" />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
name="safetyIndustryCategoryCode"
label="拟申请的法定安全评价业务范围"
rules={[{ required: true, message: "请选择拟申请的法定安全评价业务范围" }]}
>
<Checkbox.Group
style={{
display: "grid",
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
gap: "0.65rem 1rem",
padding: "1rem",
border: "1px solid #e2e8f0",
borderRadius: 8,
background: "#f8fafc",
}}
>
{QUALIFICATION_INDUSTRY_OPTIONS.map((opt) => (
<Checkbox key={opt.value} value={opt.value}>
{opt.label}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
name="orgIntro"
label="单位基本情况介绍(可附页)"
rules={[{ required: true, message: "请输入单位基本情况介绍" }]}
>
<Input.TextArea
rows={7}
placeholder="请简要介绍单位基本情况,如成立时间、人员构成、技术力量、业绩等"
maxLength={2000}
showCount
/> />
</Form.Item> </Form.Item>
</Col> </Col>