safety-eval-service-frontend/src/pages/Container/EnterpriseInfo/OrgInfo/index.js

552 lines
17 KiB
JavaScript
Raw Normal View History

2026-06-23 18:07:30 +08:00
import { Connect } from "@cqsjjb/jjb-dva-runtime";
2026-06-29 16:45:58 +08:00
import {
Button,
Col,
DatePicker,
Form,
2026-06-30 17:43:05 +08:00
Image,
2026-06-29 16:45:58 +08:00
Input,
InputNumber,
message,
Row,
Select,
Space,
Upload,
} from "antd";
2026-06-30 17:43:05 +08:00
import { PlusOutlined } from "@ant-design/icons";
2026-06-29 16:45:58 +08:00
import dayjs from "dayjs";
2026-06-23 18:07:30 +08:00
import { useEffect, useState } from "react";
2026-06-29 16:45:58 +08:00
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import {
CHONGQING_DISTRICTS,
ENTERPRISE_SCALE_OPTIONS,
ENTERPRISE_STATUS_OPTIONS,
FILING_RECORD_STATUS_OPTIONS,
FILING_TYPE_OPTIONS,
} from "~/enumerate/enterpriseOptions";
2026-06-23 18:07:30 +08:00
import { NS_ORG_INFO } from "~/enumerate/namespace";
2026-06-26 14:27:49 +08:00
import { mockUploadFileList } from "~/utils/mockUpload";
2026-06-29 16:45:58 +08:00
import {
creditCodeRule,
latitudeRule,
longitudeRule,
nonNegativeIntegerRule,
phoneRule,
positiveNumberRule,
urlRule,
} from "~/utils/validators";
2026-06-24 11:58:57 +08:00
2026-06-23 18:07:30 +08:00
function OrgInfoPage(props) {
const [form] = Form.useForm();
2026-06-29 16:45:58 +08:00
const { orgInfoLoading } = props.orgInfo;
2026-06-23 18:07:30 +08:00
const [editing, setEditing] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [detail, setDetail] = useState({});
2026-06-30 17:43:05 +08:00
const [previewImage, setPreviewImage] = useState("");
2026-06-24 11:58:57 +08:00
/** 是否已存在机构数据(有 id 视为已入库,只能修改) */
const [hasExistingData, setHasExistingData] = useState(false);
2026-06-23 18:07:30 +08:00
2026-06-29 16:45:58 +08:00
const selectDistrictOptions = CHONGQING_DISTRICTS.map((v) => ({
label: v,
value: v,
}));
2026-06-26 17:28:06 +08:00
2026-06-23 18:07:30 +08:00
const loadDetail = async () => {
try {
2026-06-29 16:45:58 +08:00
const res = await props.orgInfoGet();
2026-06-24 11:58:57 +08:00
if (res?.data?.id) {
2026-06-23 18:07:30 +08:00
setDetail(res.data);
2026-06-29 16:45:58 +08:00
form.setFieldsValue({
...res.data,
productionDate: res.data.productionDate
? dayjs(res.data.productionDate)
: undefined,
});
2026-06-24 11:58:57 +08:00
setHasExistingData(true);
2026-06-23 18:07:30 +08:00
setEditing(false);
2026-06-29 16:45:58 +08:00
} else {
2026-06-24 11:58:57 +08:00
setDetail({});
setHasExistingData(false);
2026-06-23 18:07:30 +08:00
setEditing(true);
2026-06-26 14:27:49 +08:00
form.setFieldsValue({
filingType: "确认备案",
filingRecordStatus: "未备案",
attachments: mockUploadFileList("资质申请书.pdf"),
});
2026-06-23 18:07:30 +08:00
}
2026-06-29 16:45:58 +08:00
} catch (err) {
2026-06-23 18:07:30 +08:00
console.warn("[OrgInfo] loadDetail failed:", err);
2026-06-24 11:58:57 +08:00
setDetail({});
setHasExistingData(false);
2026-06-23 18:07:30 +08:00
setEditing(true);
}
};
useEffect(() => {
loadDetail();
}, []);
2026-06-24 11:58:57 +08:00
const handleCancelEdit = () => {
if (hasExistingData) {
2026-06-29 16:45:58 +08:00
form.setFieldsValue({
...detail,
productionDate: detail.productionDate
? dayjs(detail.productionDate)
: undefined,
});
2026-06-24 11:58:57 +08:00
setEditing(false);
2026-06-29 16:45:58 +08:00
} else {
2026-06-24 11:58:57 +08:00
form.resetFields();
}
};
2026-06-23 18:07:30 +08:00
const handleSave = async (submitType) => {
try {
2026-06-29 16:45:58 +08:00
const formValues = await form.validateFields();
const values = {
...formValues,
productionDate: formValues.productionDate
? dayjs(formValues.productionDate).format("YYYY-MM-DD")
: undefined,
};
2026-06-23 18:07:30 +08:00
setSubmitting(true);
2026-06-24 11:58:57 +08:00
let request;
if (hasExistingData) {
// 已有数据:仅走修改接口
request = props.orgInfoSave;
values.id = detail.id;
2026-06-29 16:45:58 +08:00
} else {
2026-06-24 11:58:57 +08:00
// 无数据:暂存、提交均走保存接口(草稿/已提交由 authStatusCode 区分)
2026-06-29 16:45:58 +08:00
request =
submitType === "draft" ? props.orgInfoDraft : props.orgInfoSave;
2026-06-24 11:58:57 +08:00
}
2026-06-23 18:07:30 +08:00
const res = await request(values);
if (res?.success !== false) {
2026-06-24 11:58:57 +08:00
message.success(
hasExistingData
? "修改成功"
2026-06-29 16:45:58 +08:00
: submitType === "draft"
? "暂存成功"
: "提交成功",
2026-06-24 11:58:57 +08:00
);
2026-06-23 18:07:30 +08:00
setEditing(false);
loadDetail();
}
2026-06-29 16:45:58 +08:00
} catch (err) {
2026-06-23 18:07:30 +08:00
if (err?.errorFields) {
return;
}
2026-06-29 16:45:58 +08:00
message.error(
hasExistingData ? "修改失败,请稍后重试" : "保存失败,请稍后重试",
);
} finally {
2026-06-23 18:07:30 +08:00
setSubmitting(false);
}
};
return (
2026-06-29 16:45:58 +08:00
<PageLayout
2026-06-30 18:30:30 +08:00
title={
<div>
<span>机构信息管理</span>
<div
className="pageLayout-extra"
>
新成立或首次使用系统的安全评价机构可通过系统提供的引导页面详细填写机构的基本信息
</div>
</div>
}
2026-06-29 16:45:58 +08:00
extra={
hasExistingData &&
!editing && (
2026-06-30 17:43:05 +08:00
<Button
type="primary"
loading={orgInfoLoading}
onClick={() => setEditing(true)}
>
2026-06-23 18:07:30 +08:00
修改
</Button>
2026-06-29 16:45:58 +08:00
)
}
>
<Form form={form} labelCol={{ span: 10 }} disabled={!editing}>
<Row gutter={[16, 16]}>
<Col span={12}>
<Form.Item
name="orgName"
label="生产经营单位名称"
rules={[{ required: true, message: "请输入生产经营单位名称" }]}
>
<Input placeholder="请输入生产经营单位名称" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="creditCode"
label="统一社会信用代码"
rules={[creditCodeRule(true)]}
>
<Input placeholder="请输入统一社会信用代码" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="safetyIndustryCategory"
label="安全生产监管行业类别"
rules={[
{ required: true, message: "请输入安全生产监管行业类别" },
]}
>
<Input placeholder="请输入安全生产监管行业类别" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="regionCountyName"
label="属地"
rules={[{ required: true, message: "请选择属地" }]}
>
2026-06-30 17:43:05 +08:00
<Select
options={selectDistrictOptions}
placeholder="请选择属地"
allowClear
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="regionStreetName"
label="所属镇、街道"
rules={[{ required: true, message: "请输入所属镇街道" }]}
>
<Input placeholder="请输入所属镇街道" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="regionCommunityName" label="属村(社区)">
<Input placeholder="请输入属村(社区)" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="longitude"
label="所在地坐标经度"
rules={[longitudeRule(false)]}
>
<InputNumber
style={{ width: "100%" }}
min={-180}
max={180}
precision={6}
placeholder="请输入经度"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="latitude"
label="所在地坐标纬度"
rules={[latitudeRule(false)]}
>
<InputNumber
style={{ width: "100%" }}
min={-90}
max={90}
precision={6}
placeholder="请输入纬度"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="registerAddress"
label="注册地址"
rules={[{ required: true, message: "请输入注册地址" }]}
>
<Input.TextArea rows={3} placeholder="请输入注册地址" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="businessAddress"
label="经营地址"
rules={[{ required: true, message: "请输入经营地址" }]}
>
<Input.TextArea rows={3} placeholder="请输入经营地址" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="ownershipType" label="归属类型">
<Input placeholder="请输入归属类型" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="gbIndustryCode"
label="国民经济行业分类(GB/T4754-2017)"
>
<Input placeholder="请输入国民经济行业分类" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="legalRepresentative"
label="法定代表人"
rules={[{ required: true, message: "请输入法定代表人" }]}
>
<Input placeholder="请输入法定代表人" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="legalRepPhone"
label="法定代表人联系电话"
rules={[phoneRule("法定代表人联系电话", false)]}
>
<Input placeholder="请输入法定代表人联系电话" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="principal"
label="主要负责人"
rules={[{ required: true, message: "请输入主要负责人" }]}
>
<Input placeholder="请输入主要负责人" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="principalPhone"
label="主要负责人联系电话"
rules={[phoneRule("主要负责人联系电话", true)]}
>
<Input placeholder="请输入主要负责人联系电话" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="safetyDeptHead" label="安全管理部门负责人">
<Input placeholder="请输入安全管理部门负责人" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="safetyDeptPhone"
label="安全管理部门负责人联系电话"
rules={[phoneRule("安全管理部门负责人联系电话", false)]}
>
2026-06-30 17:43:05 +08:00
<Input
placeholder="请输入安全管理部门负责人联系电话"
allowClear
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="safetyVp" label="主管安全副总">
<Input placeholder="请输入主管安全副总" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="safetyVpPhone"
label="主管安全副总联系电话"
rules={[phoneRule("主管安全副总联系电话", false)]}
>
<Input placeholder="请输入主管安全副总联系电话" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="productionDate" label="投产日期">
2026-06-30 17:43:05 +08:00
<DatePicker
style={{ width: "100%" }}
placeholder="请选择投产日期"
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="businessStatus" label="企业经营状态">
<Input placeholder="请输入企业经营状态" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="disclosureUrl"
label="信息公开网址"
rules={[urlRule("信息公开网址", false)]}
>
<Input placeholder="请输入信息公开网址" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="workplaceArea"
label="工作场所建筑面积"
rules={[positiveNumberRule("工作场所建筑面积", false)]}
>
2026-06-30 17:43:05 +08:00
<InputNumber
style={{ width: "100%" }}
min={0}
precision={2}
placeholder="请输入工作场所建筑面积"
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="archiveRoomArea"
label="档案室面积"
rules={[positiveNumberRule("档案室面积", false)]}
>
2026-06-30 17:43:05 +08:00
<InputNumber
style={{ width: "100%" }}
min={0}
precision={2}
placeholder="请输入档案室面积"
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="fullTimeEvaluatorCount"
label="专职安全评价师数量"
rules={[nonNegativeIntegerRule("专职安全评价师数量", false)]}
>
2026-06-30 17:43:05 +08:00
<InputNumber
style={{ width: "100%" }}
min={0}
precision={0}
placeholder="请输入专职安全评价师数量"
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="registeredSafetyEngineerCount"
label="注册安全工程师数量"
rules={[nonNegativeIntegerRule("注册安全工程师数量", false)]}
>
2026-06-30 17:43:05 +08:00
<InputNumber
style={{ width: "100%" }}
min={0}
precision={0}
placeholder="请输入注册安全工程师数量"
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="enterpriseStatus" label="企业状态">
2026-06-30 17:43:05 +08:00
<Select
options={ENTERPRISE_STATUS_OPTIONS}
placeholder="请选择企业状态"
allowClear
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="enterpriseScale" label="企业规模">
2026-06-30 17:43:05 +08:00
<Select
options={ENTERPRISE_SCALE_OPTIONS}
placeholder="请选择企业规模"
allowClear
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="filingType" label="备案类型">
2026-06-30 17:43:05 +08:00
<Select
options={FILING_TYPE_OPTIONS}
placeholder="请选择备案类型"
allowClear
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="filingRecordStatus" label="备案状态">
2026-06-30 17:43:05 +08:00
<Select
options={FILING_RECORD_STATUS_OPTIONS}
placeholder="请选择备案状态"
allowClear
/>
2026-06-29 16:45:58 +08:00
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="attachments"
label="上传附件"
valuePropName="fileList"
2026-06-30 17:43:05 +08:00
getValueFromEvent={({ fileList }) => {
return (
fileList?.map((file) => {
return {
url: file.response?.data.url || file.url,
uid: file.uid,
name: file.name,
};
}) || []
);
}}
2026-06-29 16:45:58 +08:00
>
2026-06-30 17:43:05 +08:00
<Upload
maxCount={5}
onPreview={(file) => {
2026-06-30 18:30:30 +08:00
const isImage =
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(
file.name || file.url || "",
);
2026-06-30 17:43:05 +08:00
if (isImage) {
setPreviewImage(file.url || file.thumbUrl);
} else {
window.open(file.url || file.thumbUrl);
}
}}
listType="picture-card"
action={`${window.process.env.app.API_HOST}/safety-eval/file/upload`}
>
<button style={{ border: 0, background: "none" }} type="button">
<PlusOutlined />
<div style={{ marginTop: 8 }}>上传附件</div>
</button>
2026-06-29 16:45:58 +08:00
</Upload>
</Form.Item>
</Col>
</Row>
</Form>
2026-06-23 18:07:30 +08:00
{editing && (
<div style={{ marginTop: 24, textAlign: "center" }}>
<Space>
2026-06-29 16:45:58 +08:00
<Button onClick={handleCancelEdit}>取消</Button>
2026-06-24 11:58:57 +08:00
{!hasExistingData && (
<Button loading={submitting} onClick={() => handleSave("draft")}>
暂存
</Button>
)}
2026-06-29 16:45:58 +08:00
<Button
type="primary"
loading={submitting}
onClick={() => handleSave("submit")}
>
2026-06-23 18:07:30 +08:00
提交
</Button>
</Space>
</div>
)}
2026-06-30 17:43:05 +08:00
<Image
style={{ display: "none" }}
preview={{
visible: !!previewImage,
src: previewImage,
onVisibleChange: (visible) => {
if (!visible) setPreviewImage("");
},
}}
/>
2026-06-29 16:45:58 +08:00
</PageLayout>
2026-06-23 18:07:30 +08:00
);
}
export default Connect([NS_ORG_INFO], true)(OrgInfoPage);