Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	jjb.config.js
#	src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.js
dev
huwei 2026-08-04 15:58:18 +08:00
commit b991405fc9
8 changed files with 169 additions and 119 deletions

View File

@ -10,10 +10,14 @@ module.exports = {
javaGitBranch: "dev",
// 本地联调 safetyEval-servicecontext-path: /safetyEval默认端口 8095
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
// API_HOST: "https://gbs-gateway.qhdsafety.com",
API_HOST: "https://gbs-gateway.qhdsafety.com",
// API_HOST: "http://192.168.0.134",
<<<<<<< HEAD
// API_HOST: "http://192.168.0.150", //太浅
=======
//API_HOST: "http://192.168.0.150", //太浅
>>>>>>> origin/dev
// API_HOST: "http://192.168.0.152",
API_HOST: "http://192.168.0.103", //huwei
},

View File

@ -185,13 +185,27 @@ export default function StaffViewModal({
<Descriptions.Item label="账号">{info.account}</Descriptions.Item>
<Descriptions.Item label="部门">{info.deptName}</Descriptions.Item>
<Descriptions.Item label="岗位">{info.postName}</Descriptions.Item>
<Descriptions.Item label="身份证号">
{info.idCardNo}
</Descriptions.Item>
<Descriptions.Item label="现住地址" >
{info.currentAddress}
</Descriptions.Item>
<Descriptions.Item label="办公地址" >
{info.officeAddress}
</Descriptions.Item>
<Descriptions.Item label="学历类型">
{info.educationTypeName || "-"}
</Descriptions.Item>
<Descriptions.Item label="毕业院校">
{info.graduateSchool}
</Descriptions.Item>
<Descriptions.Item label="学历层次">
{info.educationLevelName || "-"}
</Descriptions.Item>
<Descriptions.Item label="基础学科专业">
{info.basicDisciplineMajorName || "-"}
</Descriptions.Item>
<Descriptions.Item label="是否注册安全工程师">
{REGISTER_ENGINEER_MAP[info.registerEngineerFlag] ?? "-"}
@ -210,19 +224,7 @@ export default function StaffViewModal({
? "否"
: "-"}
</Descriptions.Item>
<Descriptions.Item label="身份证号">
{info.idCardNo}
</Descriptions.Item>
<Descriptions.Item label="现住地址" span={2}>
{info.currentAddress}
</Descriptions.Item>
<Descriptions.Item label="办公地址" span={2}>
{info.officeAddress}
</Descriptions.Item>
<Descriptions.Item label="毕业院校">
{info.graduateSchool}
</Descriptions.Item>
<Descriptions.Item
label="出版学术专著、专利、获奖、发表学术论文等"

View File

@ -430,6 +430,17 @@ function CertModal({
disabled={professionalCapabilityOptions.length === 0}
/>
</Form.Item>
<Form.Item
name="certTypeCode"
label="证书类型"
rules={[{ required: true, message: "请选择证书类型" }]}
>
<Select
placeholder="请选择证书类型"
options={CERT_TYPE_OPTIONS}
onChange={() => form.setFieldValue("cert_level", undefined)}
/>
</Form.Item>
<Form.Item
name="certName"
label="证照名称"
@ -470,17 +481,7 @@ function CertModal({
>
<DatePicker.RangePicker style={{ width: "100%" }} />
</Form.Item>
<Form.Item
name="certTypeCode"
label="证书类型"
rules={[{ required: true, message: "请选择证书类型" }]}
>
<Select
placeholder="请选择证书类型"
options={CERT_TYPE_OPTIONS}
onChange={() => form.setFieldValue("cert_level", undefined)}
/>
</Form.Item>
<Form.Item
name="certLevel"
label="证书等级"
@ -601,10 +602,11 @@ function ViewModal({ open, currentId, staffCertificateInfo, onCancel }) {
<Descriptions bordered column={1} labelStyle={{ width: 160 }}>
<Descriptions.Item label="专业范围">{QUALIFICATION_INDUSTRY_OPTIONS_MAP[info.businessScope] }</Descriptions.Item>
<Descriptions.Item label="专业能力">{info.professionalCapabilityName }</Descriptions.Item>
<Descriptions.Item label="证照名称">{info.certName}</Descriptions.Item>
<Descriptions.Item label="证书类型">
{CERT_TYPE_MAP[info.certTypeCode] || info.certTypeCode}
</Descriptions.Item>
<Descriptions.Item label="证照名称">{info.certName}</Descriptions.Item>
<Descriptions.Item label="证书等级">
{CERT_LEVEL_LABEL_BY_TYPE[info.certTypeCode]?.[info.cert_level] || info.cert_level}
</Descriptions.Item>

View File

@ -646,6 +646,65 @@ function StaffFormModal({
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="currentAddress"
label="现住地址"
rules={[{ max: 300, message: "现住地址不能超过300个字符" }]}
>
<Input placeholder="请输入现住地址" maxLength={300} showCount />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="officeAddress"
label="办公地址"
rules={[{ max: 300, message: "办公地址不能超过300个字符" }]}
>
<Input placeholder="请输入办公地址" maxLength={300} showCount />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="educationTypeCode" label="学历类型">
<Select
placeholder="请选择学历类型"
allowClear
options={EDUCATION_TYPE_OPTIONS}
onChange={(_val, option) => {
form.setFieldValue(
"educationTypeName",
option?.label || undefined,
);
}}
/>
</Form.Item>
<Form.Item name="educationTypeName" noStyle />
</Col>
<Col span={12}>
<Form.Item
name="graduateSchool"
label="毕业院校"
rules={[{ max: 200, message: "毕业院校不能超过200个字符" }]}
>
<Input placeholder="请输入毕业院校" maxLength={200} showCount />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="educationLevelCode" label="学历层次">
<Select
placeholder="请选择学历层次"
allowClear
options={EDUCATION_LEVEL_OPTIONS}
onChange={(val, option) => {
form.setFieldValue(
"educationLevelName",
option?.label || undefined,
);
}}
/>
</Form.Item>
<Form.Item name="educationLevelName" noStyle />
</Col>
<Col span={12}>
<Form.Item name="basicDisciplineMajorId" label="基础学科专业">
<Select
@ -665,38 +724,6 @@ function StaffFormModal({
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="educationTypeCode" label="学历类型">
<Select
placeholder="请选择学历类型"
allowClear
options={EDUCATION_TYPE_OPTIONS}
onChange={(_val, option) => {
form.setFieldValue(
"educationTypeName",
option?.label || undefined,
);
}}
/>
</Form.Item>
<Form.Item name="educationTypeName" noStyle />
</Col>
<Col span={12}>
<Form.Item name="educationLevelCode" label="学历层次">
<Select
placeholder="请选择学历层次"
allowClear
options={EDUCATION_LEVEL_OPTIONS}
onChange={(val, option) => {
form.setFieldValue(
"educationLevelName",
option?.label || undefined,
);
}}
/>
</Form.Item>
<Form.Item name="educationLevelName" noStyle />
</Col>
<Col span={12}>
<AttachmentUpload
name="proofMaterialUrl"
@ -883,34 +910,6 @@ function StaffFormModal({
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="currentAddress"
label="现住地址"
rules={[{ max: 300, message: "现住地址不能超过300个字符" }]}
>
<Input placeholder="请输入现住地址" maxLength={300} showCount />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="officeAddress"
label="办公地址"
rules={[{ max: 300, message: "办公地址不能超过300个字符" }]}
>
<Input placeholder="请输入办公地址" maxLength={300} showCount />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="graduateSchool"
label="毕业院校"
rules={[{ max: 200, message: "毕业院校不能超过200个字符" }]}
>
<Input placeholder="请输入毕业院校" maxLength={200} showCount />
</Form.Item>
</Col>
</Row>
</Form>
</Modal>
@ -961,10 +960,11 @@ function StaffImportModal({ open, onCancel, onSuccess }) {
const headers = { token: sessionStorage.getItem("token") || "" };
const orgInfoId = sessionStorage.getItem("orgInfoId");
if (orgInfoId) headers.orgInfoId = String(orgInfoId);
const resp = await fetch(
`${API_HOST}/safetyEval/org-personnel/import`,
{ method: "POST", headers, body: formData },
);
const resp = await fetch(`${API_HOST}/safetyEval/org-personnel/import`, {
method: "POST",
headers,
body: formData,
});
const json = await resp.json();
if (json && json.success !== false) {
onDone(json.data, file);
@ -1004,8 +1004,7 @@ function StaffImportModal({ open, onCancel, onSuccess }) {
customRequest={customRequest}
disabled={uploading}
beforeUpload={(file) => {
const ext =
"." + (file.name.split(".").pop() || "").toLowerCase();
const ext = "." + (file.name.split(".").pop() || "").toLowerCase();
if (![".xlsx", ".xls"].includes(ext)) {
message.error("仅支持 .xlsx / .xls 格式文件");
return Upload.LIST_IGNORE;

View File

@ -6,14 +6,14 @@ import {
import { FILING_UNIT_TYPE_OPTIONS } from "~/enumerate/qualFilingOptions";
import AttachmentUpload from "~/components/AttachmentUpload";
export default function BasicInfoStep({ form, disabled }) {
export default function BasicInfoStep({ form, disabled, mode }) {
return (
<Form form={form} layout="vertical" disabled={disabled}>
<Row gutter={16}>
<Col span={24}>
<Form.Item
name="businessScope"
label="申请的业务范围"
label={mode === 'filed' ? "已有资质备案" : "申请的业务范围"}
rules={[{ required: !disabled, message: "请选择业务范围" }]}
>
<Select
@ -38,32 +38,32 @@ export default function BasicInfoStep({ form, disabled }) {
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="filingUnitName" label="备案单位">
<Form.Item name="filingUnitName" label="备案单位" rules={[{ required: !disabled, message: "请输入备案单位" }]}>
<Input placeholder="备案单位" maxLength={64} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="filingUnitTypeName" label="备案单位类型">
<Form.Item name="filingUnitTypeName" label="备案单位类型" rules={[{ required: !disabled, message: "请选择备案单位类型" }]}>
<Select options={FILING_UNIT_TYPE_OPTIONS} placeholder="请选择" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="creditCode" label="统一社会信用代码">
<Form.Item name="creditCode" label="统一社会信用代码" rules={[{ required: !disabled, message: "请输入统一社会信用代码" }]}>
<Input placeholder="统一社会信用代码" maxLength={18} />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item name="officeAddress" label="办公地址">
<Form.Item name="officeAddress" label="办公地址" rules={[{ required: !disabled, message: "请输入办公地址" }]}>
<Input placeholder="办公地址" maxLength={128} />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item name="registerAddress" label="注册地址">
<Form.Item name="registerAddress" label="注册地址" rules={[{ required: !disabled, message: "请输入注册地址" }]}>
<Input placeholder="注册地址" maxLength={128} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="qualCertNo" label="资质证书编号">
<Form.Item name="qualCertNo" label="资质证书编号" rules={[{ required: !disabled, message: "请输入资质证书编号" }]}>
<Input placeholder="资质证书编号" maxLength={32} />
</Form.Item>
</Col>
@ -72,6 +72,7 @@ export default function BasicInfoStep({ form, disabled }) {
name="infoDisclosureUrl"
label="信息公开网址"
rules={[
{ required: !disabled, message: "请输入信息公开网址" },
{
pattern: /^https?:\/\/[^\s]+$/,
message: "请输入正确的网址,需以 http:// 或 https:// 开头",
@ -85,8 +86,8 @@ export default function BasicInfoStep({ form, disabled }) {
<Form.Item
name="legalPersonPhone"
label="法人代表电话"
rules={[
{ required: !disabled, message: "请输入法人代表电话" },
{
pattern: /^(1\d{10}|0\d{2,3}-?\d{7,8})$/,
message: "请输入正确的手机号或座机号",
@ -100,8 +101,8 @@ export default function BasicInfoStep({ form, disabled }) {
<Form.Item
name="contactPhone"
label="联系人电话"
rules={[
{ required: !disabled, message: "请输入联系人电话" },
{
pattern: /^(1\d{10}|0\d{2,3}-?\d{7,8})$/,
message: "请输入正确的手机号或座机号",
@ -151,7 +152,7 @@ export default function BasicInfoStep({ form, disabled }) {
<Form.Item
name="archiveRoomArea"
label="档案室面积(㎡)"
rules={[{ required: !disabled, message: "请输入档案室面积" }]}
>
<InputNumber
style={{ width: "100%" }}
@ -166,7 +167,7 @@ export default function BasicInfoStep({ form, disabled }) {
<Form.Item
name="fulltimeEvaluatorCount"
label="专职安全评价师数量(人)"
rules={[{ required: !disabled, message: "请输入专职安全评价师数量" }]}
>
<InputNumber style={{ width: "100%" }} min={0} max={100000000} maxLength={9} placeholder="人" />
</Form.Item>
@ -175,19 +176,20 @@ export default function BasicInfoStep({ form, disabled }) {
<Form.Item
name="registeredEngineerCount"
label="注册安全工程师数量(人)"
rules={[{ required: !disabled, message: "请输入注册安全工程师数量" }]}
>
<InputNumber style={{ width: "100%" }} min={0} max={100000000} maxLength={9} placeholder="人" />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item name="unitIntro" label="单位基本情况介绍">
<Form.Item name="unitIntro" label="单位基本情况介绍" rules={[{ required: !disabled, message: "请输入单位基本情况介绍" }]}>
<Input.TextArea rows={3} placeholder="请输入单位基本情况介绍" maxLength={500} showCount />
</Form.Item>
</Col>
<Col span={24}>
<AttachmentUpload
name="attachmentUrl"
rules={[{ required: !disabled, message: "请上传附件" }]}
label="上传附件"
disabled={disabled}
extra={'最多上传12个PDF、DOC、DOCX格式文件'}

View File

@ -23,7 +23,7 @@ export default function MaterialStep({
dataSource={materials}
columns={[
{ title: "序号", width: 60, render: (_, record, index) => index + 1 },
{ title: "内容", dataIndex: "materialContent", width: 200, ellipsis: true },
{ title: "内容", dataIndex: "materialContent", width: 400, ellipsis: true },
{ title: "上传附件", dataIndex: "attachmentDesc", width: 100 },
{
title: "格式",

View File

@ -36,7 +36,7 @@ const MODE_TITLE = {
function FilingFormPage(props) {
const query = router.query;
const mode = query.mode;
const [loading, setLoading] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [activeStep, setActiveStep] = useState("basic");
const [detail, setDetail] = useState(null);
@ -338,8 +338,8 @@ function FilingFormPage(props) {
const STEP_ITEMS = [
{
key: "basic",
label: "1. 备案基本信息",
children: <BasicInfoStep form={basicForm} disabled={readOnly} />,
label: "1. 基本信息备案",
children: <BasicInfoStep form={basicForm} disabled={readOnly} mode={mode} />,
forceRender: true,
},
{
@ -367,7 +367,7 @@ function FilingFormPage(props) {
},
{
key: "personnel",
label: "4. 备案人员管理",
label: "4. 专职人员备案",
children: (
<PersonnelStep
personnelList={detail?.personnelList || []}
@ -401,7 +401,7 @@ function FilingFormPage(props) {
history={props.history}
previous
>
<Spin spinning={loading || submitting}>
<Spin spinning={ submitting}>
<Tabs
activeKey={activeStep}
items={STEP_ITEMS}

View File

@ -106,6 +106,38 @@ const ProjectDocLibrary = (props) => {
}
};
const handleDownload = async (record) => {
const raw = record.project_doc_url || "";
if (!raw) {
message.warning("文件地址不存在");
return;
}
const fileUrl = /^https?:\/\//.test(raw) ? raw : (window.fileUrl || "") + raw;
const fileName = record.materialName || raw.split("/").pop() || "文件";
const hide = message.loading("正在下载...", 0);
try {
const response = await fetch(fileUrl, {
headers: { token: sessionStorage.getItem("token") || "" },
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const blob = await response.blob();
const blobUrl = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = blobUrl;
link.download = fileName;
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
message.success("下载已开始");
} catch {
message.error("下载失败,请稍后重试");
} finally {
hide();
}
};
const columns = [
{ title: "项目编号", dataIndex: "projectNo", width: 130 },
{
@ -264,9 +296,18 @@ const ProjectDocLibrary = (props) => {
{ title: "文件名称", dataIndex: "materialName", ellipsis: true },
{
title: "操作",
width: 100,
width: 130,
render: (_, record) => (
<Space size={8}>
<PreviewUrlButton url={record.project_doc_url} />
<Button
type="link"
size="small"
onClick={() => handleDownload(record)}
>
下载
</Button>
</Space>
),
},
]}