2026-08-10 14:16:49 +08:00
|
|
|
|
import React, { useState, useMemo } from "react";
|
2026-08-10 17:45:12 +08:00
|
|
|
|
import { Button, Image, Input, Select, Table, Tabs, Tag } from "antd";
|
2026-07-02 16:58:07 +08:00
|
|
|
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
2026-08-10 17:45:12 +08:00
|
|
|
|
import { NS_QUAL_REVIEW, NS_EQUIP_INFO } from "~/enumerate/namespace";
|
2026-07-09 09:23:22 +08:00
|
|
|
|
import StaffViewModal from "~/components/StaffViewModal";
|
2026-07-02 16:58:07 +08:00
|
|
|
|
import PreviewUrlButton from "~/components/PreviewUrlButton/index";
|
2026-08-10 17:45:12 +08:00
|
|
|
|
import { EquipViewModal } from "~/pages/Container/EnterpriseInfo/EquipInfo";
|
2026-08-10 17:02:48 +08:00
|
|
|
|
import {
|
|
|
|
|
|
QUALIFICATION_INDUSTRY_OPTIONS,
|
|
|
|
|
|
QUALIFICATION_INDUSTRY_OPTIONS_MAP,
|
|
|
|
|
|
TITLE_LEVEL_MAP,
|
|
|
|
|
|
} from "~/enumerate/enterpriseOptions";
|
2026-08-10 14:16:49 +08:00
|
|
|
|
import { CAPABILITY_MAP } from "~/enumerate/constant";
|
|
|
|
|
|
import { getCalibrationTag } from "../mockData";
|
2026-07-02 16:58:07 +08:00
|
|
|
|
|
2026-08-10 14:16:49 +08:00
|
|
|
|
const { TextArea } = Input;
|
2026-06-30 15:59:15 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2026-08-10 14:16:49 +08:00
|
|
|
|
* 共享组件:资质备案初审 6 个标签页(详情 / 审核共用)
|
|
|
|
|
|
* 对应原型 mod-qual-review-form / mod-qual-review-detail:
|
|
|
|
|
|
* 1. 申请基本信息 2. 管理人员 3. 专职安全评价师
|
|
|
|
|
|
* 4. 申请材料 5. 设备清单 6. 机构负责人签字
|
2026-06-30 15:59:15 +08:00
|
|
|
|
* @param {Object} props
|
|
|
|
|
|
* @param {Object} props.detail - 备案详情数据
|
2026-08-10 14:16:49 +08:00
|
|
|
|
* @param {boolean} [props.isReview=false] - 审核模式(材料表格开启"符合性"列)
|
2026-06-30 15:59:15 +08:00
|
|
|
|
* @param {Object} [props.compliance={}] - 审核模式下的符合性状态
|
|
|
|
|
|
* @param {Function} [props.onComplianceChange] - 符合性变动回调
|
|
|
|
|
|
*/
|
2026-08-05 11:24:39 +08:00
|
|
|
|
function calcAge(birthDate) {
|
|
|
|
|
|
if (!birthDate) return "-";
|
|
|
|
|
|
const birth = new Date(birthDate);
|
|
|
|
|
|
if (isNaN(birth.getTime())) return "-";
|
|
|
|
|
|
const now = new Date();
|
|
|
|
|
|
let age = now.getFullYear() - birth.getFullYear();
|
|
|
|
|
|
const m = now.getMonth() - birth.getMonth();
|
|
|
|
|
|
if (m < 0 || (m === 0 && now.getDate() < birth.getDate())) age--;
|
|
|
|
|
|
return age >= 0 ? age : "-";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-10 17:02:48 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 职称名称:后端 QualFilingPersonnelInfoCO 返回 titleCodeArr(编码数组),
|
|
|
|
|
|
* 转中文名称展示(高级/中级/初级)。变更时间:2026-08-10(对齐后端字段)
|
|
|
|
|
|
*/
|
|
|
|
|
|
function titleNames(titleCodeArr) {
|
|
|
|
|
|
// 兼容:初审返回数组,备案变更返回字符串(可能为 "SENIOR" / "SENIOR,MIDDLE" / JSON 数组字符串)。变更时间:2026-08-10
|
|
|
|
|
|
let list = Array.isArray(titleCodeArr) ? titleCodeArr : [];
|
|
|
|
|
|
if (!Array.isArray(titleCodeArr) && typeof titleCodeArr === "string") {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const parsed = JSON.parse(titleCodeArr);
|
|
|
|
|
|
list = Array.isArray(parsed) ? parsed : [titleCodeArr];
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
list = String(titleCodeArr)
|
|
|
|
|
|
.split(/[,,、]/)
|
|
|
|
|
|
.map((s) => s.trim())
|
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
const names = list
|
|
|
|
|
|
.map((item) => {
|
|
|
|
|
|
const code = typeof item === "string" ? item : item?.titleCode || item?.title;
|
|
|
|
|
|
return item?.titleName || TITLE_LEVEL_MAP[code] || code || "";
|
|
|
|
|
|
})
|
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
|
// 空数组返回空字符串,便于调用方回退 titleName 等字段
|
|
|
|
|
|
return names.length ? names.join("、") : "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 从业年限:后端返回 joinWorkDate(参加工作时间),按当前日期推算年数。
|
|
|
|
|
|
* 变更时间:2026-08-10(对齐后端字段)
|
|
|
|
|
|
*/
|
|
|
|
|
|
function workYearsText(joinWorkDate) {
|
|
|
|
|
|
if (!joinWorkDate) return "-";
|
|
|
|
|
|
const d = new Date(String(joinWorkDate).replace(/-/g, "/"));
|
|
|
|
|
|
if (Number.isNaN(d.getTime())) return "-";
|
|
|
|
|
|
const now = new Date();
|
|
|
|
|
|
let years = now.getFullYear() - d.getFullYear();
|
|
|
|
|
|
const m = now.getMonth() - d.getMonth();
|
|
|
|
|
|
if (m < 0 || (m === 0 && now.getDate() < d.getDate())) years--;
|
|
|
|
|
|
return years >= 0 ? `${years}年` : "-";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-10 14:16:49 +08:00
|
|
|
|
const gridStyle = {
|
|
|
|
|
|
display: "grid",
|
|
|
|
|
|
gridTemplateColumns: "1fr 1fr",
|
|
|
|
|
|
gap: "0.6rem 1rem",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const fieldStyle = { display: "flex", flexDirection: "column", gap: "0.3rem" };
|
|
|
|
|
|
const labelStyle = { fontSize: "0.8rem", color: "#64748b", fontWeight: 500 };
|
|
|
|
|
|
const inputStyle = { background: "#f8fafc" };
|
|
|
|
|
|
const fullSpan = { gridColumn: "1 / -1" };
|
|
|
|
|
|
|
|
|
|
|
|
/** 只读字段 */
|
|
|
|
|
|
function ReadonlyField({ label, value, span }) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div style={span ? { ...fieldStyle, ...span } : fieldStyle}>
|
|
|
|
|
|
<label style={labelStyle}>{label}</label>
|
|
|
|
|
|
<Input readOnly value={value || "-"} style={inputStyle} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-30 15:59:15 +08:00
|
|
|
|
const FilingTabs = ({
|
2026-08-10 14:16:49 +08:00
|
|
|
|
detail = {},
|
2026-06-30 15:59:15 +08:00
|
|
|
|
isReview = false,
|
|
|
|
|
|
compliance = {},
|
|
|
|
|
|
onComplianceChange,
|
2026-08-10 17:02:48 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 额外 Tab(如机构备案详情「7. 变更记录」),数组元素与 antd Tabs items 同构。
|
|
|
|
|
|
* 不传时保持原 6 Tab,不影响初审/确认/专家核验展示。变更时间:2026-08-10
|
|
|
|
|
|
*/
|
|
|
|
|
|
extraTabs,
|
2026-08-10 17:45:12 +08:00
|
|
|
|
equipInfoGet,
|
2026-06-30 15:59:15 +08:00
|
|
|
|
}) => {
|
2026-07-02 16:58:07 +08:00
|
|
|
|
const [viewId, setViewId] = useState("");
|
2026-08-10 17:45:12 +08:00
|
|
|
|
const [equipViewOpen, setEquipViewOpen] = useState(false);
|
|
|
|
|
|
const [equipId, setEquipId] = useState("");
|
2026-07-09 17:00:19 +08:00
|
|
|
|
|
2026-08-10 14:16:49 +08:00
|
|
|
|
const personnelRows = useMemo(
|
|
|
|
|
|
() =>
|
|
|
|
|
|
(detail.personnelList || []).map((item) => ({
|
|
|
|
|
|
...item,
|
|
|
|
|
|
age: item.age ?? calcAge(item.birthDate),
|
|
|
|
|
|
})),
|
|
|
|
|
|
[detail.personnelList],
|
2026-08-05 11:24:39 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
2026-08-10 17:02:48 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 管理人员判定:technicalDirectorFlag(技术负责人) 或 processControlLeaderFlag(过程控制负责人) 为 true;
|
|
|
|
|
|
* 法定代表人/负责人按岗位名兜底归入管理人员。
|
|
|
|
|
|
* 备案变更详情人员 CO(QualFilingPersonnelChangeInfoCO)暂未返回 flag 字段,靠岗位名兜底判断(待后端补充 flag)。
|
|
|
|
|
|
* 变更时间:2026-08-10(按后端人员标识字段拆分)
|
|
|
|
|
|
*/
|
|
|
|
|
|
const isManager = (item) =>
|
|
|
|
|
|
item.technicalDirectorFlag === true ||
|
|
|
|
|
|
item.processControlLeaderFlag === true ||
|
|
|
|
|
|
/法定代表人|负责人/.test(item.positionName || "");
|
|
|
|
|
|
|
2026-08-10 14:16:49 +08:00
|
|
|
|
const managerRows = useMemo(() => {
|
2026-08-10 17:02:48 +08:00
|
|
|
|
return personnelRows.filter(isManager);
|
2026-08-10 14:16:49 +08:00
|
|
|
|
}, [personnelRows]);
|
|
|
|
|
|
|
2026-08-10 17:02:48 +08:00
|
|
|
|
/** 专职安全评价师:非管理人员的其余人员 */
|
2026-08-10 14:16:49 +08:00
|
|
|
|
const evaluatorRows = useMemo(() => {
|
2026-08-10 17:02:48 +08:00
|
|
|
|
return personnelRows.filter((item) => !isManager(item));
|
2026-08-10 14:16:49 +08:00
|
|
|
|
}, [personnelRows]);
|
|
|
|
|
|
|
2026-07-09 17:00:19 +08:00
|
|
|
|
const attachmentList = useMemo(() => {
|
|
|
|
|
|
const v = detail.attachmentUrl;
|
|
|
|
|
|
if (!v) return [];
|
|
|
|
|
|
try {
|
|
|
|
|
|
const p = JSON.parse(v);
|
|
|
|
|
|
return Array.isArray(p) ? p : [];
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [detail.attachmentUrl]);
|
2026-07-02 16:58:07 +08:00
|
|
|
|
|
2026-08-10 14:16:49 +08:00
|
|
|
|
const businessScope = useMemo(() => {
|
|
|
|
|
|
const list = Array.isArray(detail.businessScope) ? detail.businessScope : [];
|
|
|
|
|
|
return QUALIFICATION_INDUSTRY_OPTIONS.map((opt) => ({
|
|
|
|
|
|
...opt,
|
|
|
|
|
|
checked: list.includes(opt.value),
|
|
|
|
|
|
}));
|
|
|
|
|
|
}, [detail.businessScope]);
|
|
|
|
|
|
|
|
|
|
|
|
/** 人员表格列(管理/评价师共用基础) */
|
|
|
|
|
|
const basePersonCols = (withScope) => [
|
2026-06-30 15:59:15 +08:00
|
|
|
|
{ title: "序号", width: 60, render: (_, __, i) => i + 1 },
|
2026-08-10 14:16:49 +08:00
|
|
|
|
{ title: "姓名", dataIndex: "personName", width: 110 },
|
|
|
|
|
|
{ title: "岗位类别", dataIndex: "positionName", width: 140, ellipsis: true },
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "学历/专业",
|
|
|
|
|
|
width: 160,
|
2026-08-10 17:02:48 +08:00
|
|
|
|
// 后端字段:educationName(学历) / basicDisciplineMajorName(专业);变更时间:2026-08-10
|
2026-08-10 14:16:49 +08:00
|
|
|
|
render: (_, r) =>
|
2026-08-10 17:02:48 +08:00
|
|
|
|
[r.educationName, r.basicDisciplineMajorName].filter(Boolean).join(" / ") || "-",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "职称",
|
|
|
|
|
|
width: 120,
|
|
|
|
|
|
render: (_, r) => titleNames(r.titleCodeArr) || r.titleName || "-",
|
2026-08-10 14:16:49 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "从业年限",
|
|
|
|
|
|
width: 100,
|
2026-08-10 17:02:48 +08:00
|
|
|
|
render: (_, r) =>
|
|
|
|
|
|
r.workYears || r.yearsOfWorking || workYearsText(r.joinWorkDate),
|
2026-08-10 14:16:49 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "专业能力",
|
|
|
|
|
|
width: 180,
|
|
|
|
|
|
render: (_, r) => {
|
2026-08-10 17:02:48 +08:00
|
|
|
|
// 能力编码:兼容 professionalCapabilityCodeList 与后端 capabilityAssessment;变更时间:2026-08-10
|
|
|
|
|
|
// 备案变更返回 capabilityAssessment 字符串(JSON 数组),需解析后映射
|
|
|
|
|
|
let capList = r.capabilityAssessment;
|
|
|
|
|
|
if (typeof capList === "string") {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const parsed = JSON.parse(capList);
|
|
|
|
|
|
capList = Array.isArray(parsed) ? parsed : [];
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
capList = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
const codes =
|
|
|
|
|
|
r.professionalCapabilityCodeList ||
|
|
|
|
|
|
(Array.isArray(capList)
|
|
|
|
|
|
? capList.map((c) => (typeof c === "string" ? c : c?.professionalCapabilityCode)).filter(Boolean)
|
|
|
|
|
|
: []) ||
|
|
|
|
|
|
[];
|
2026-08-10 14:16:49 +08:00
|
|
|
|
return codes.length
|
|
|
|
|
|
? codes.map((code) => CAPABILITY_MAP[code] || code).join("、")
|
|
|
|
|
|
: "-";
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
...(withScope
|
|
|
|
|
|
? [
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "申请业务范围",
|
|
|
|
|
|
width: 200,
|
|
|
|
|
|
ellipsis: true,
|
|
|
|
|
|
render: (_, r) => {
|
|
|
|
|
|
const scopes = r.appliedScope || r.scopeList;
|
|
|
|
|
|
if (Array.isArray(scopes)) {
|
|
|
|
|
|
return scopes.map((s) => QUALIFICATION_INDUSTRY_OPTIONS_MAP[s] || s).join("、");
|
|
|
|
|
|
}
|
|
|
|
|
|
return scopes || "-";
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
: []),
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "操作",
|
|
|
|
|
|
width: 80,
|
|
|
|
|
|
fixed: "right",
|
|
|
|
|
|
render: (_, record) => (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
onClick={() => setViewId(record.sourcePersonnelId || record.id)}
|
|
|
|
|
|
>
|
|
|
|
|
|
查看
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
2026-06-30 15:59:15 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const materialCols = isReview
|
|
|
|
|
|
? [
|
2026-08-10 14:16:49 +08:00
|
|
|
|
{ title: "序号", width: 60, render: (_, __, i) => i + 1 },
|
|
|
|
|
|
{ title: "申请材料", dataIndex: "materialContent", ellipsis: true },
|
2026-07-02 16:58:07 +08:00
|
|
|
|
{ title: "格式", dataIndex: "materialFormat", width: 80 },
|
2026-08-10 14:16:49 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: "状态",
|
|
|
|
|
|
width: 100,
|
|
|
|
|
|
render: (_, record) =>
|
|
|
|
|
|
record.uploadStatusCode === 2 ? (
|
|
|
|
|
|
<Tag color="success">{record.uploadStatusName || "已上传"}</Tag>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<Tag color="warning">{record.uploadStatusName || "待上传"}</Tag>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "符合性",
|
|
|
|
|
|
width: 110,
|
2026-06-30 15:59:15 +08:00
|
|
|
|
render: (_, record) => (
|
|
|
|
|
|
<Select
|
|
|
|
|
|
style={{ width: "100%", fontSize: "0.75rem" }}
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
placeholder="请选择"
|
|
|
|
|
|
value={compliance[record.id]}
|
2026-07-08 16:20:43 +08:00
|
|
|
|
allowClear
|
2026-08-10 14:16:49 +08:00
|
|
|
|
defaultValue="pass"
|
2026-06-30 15:59:15 +08:00
|
|
|
|
onChange={(v) => onComplianceChange?.(record.id, v)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Select.Option value="pass">符合</Select.Option>
|
|
|
|
|
|
<Select.Option value="fail">不符合</Select.Option>
|
|
|
|
|
|
</Select>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
2026-08-10 14:16:49 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: "操作",
|
|
|
|
|
|
width: 80,
|
|
|
|
|
|
render: (_, record) => (
|
|
|
|
|
|
<PreviewUrlButton url={record.attachmentUrl}>预览</PreviewUrlButton>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
2026-06-30 15:59:15 +08:00
|
|
|
|
]
|
|
|
|
|
|
: [
|
2026-08-10 14:16:49 +08:00
|
|
|
|
{ title: "序号", width: 60, render: (_, __, i) => i + 1 },
|
|
|
|
|
|
{ title: "申请材料", dataIndex: "materialContent", ellipsis: true },
|
2026-07-02 16:58:07 +08:00
|
|
|
|
{ title: "格式", dataIndex: "materialFormat", width: 80 },
|
2026-08-10 14:16:49 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: "操作",
|
|
|
|
|
|
width: 80,
|
|
|
|
|
|
render: (_, record) => (
|
|
|
|
|
|
<PreviewUrlButton url={record.attachmentUrl}>预览</PreviewUrlButton>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
2026-06-30 15:59:15 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
2026-08-10 14:16:49 +08:00
|
|
|
|
const commitment = detail.commitment || {};
|
2026-07-02 16:58:07 +08:00
|
|
|
|
|
2026-06-30 15:59:15 +08:00
|
|
|
|
const items = [
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "info",
|
2026-08-10 14:16:49 +08:00
|
|
|
|
label: "1. 申请基本信息",
|
2026-06-30 15:59:15 +08:00
|
|
|
|
children: (
|
2026-08-10 14:16:49 +08:00
|
|
|
|
<div style={gridStyle}>
|
|
|
|
|
|
<div style={{ ...fieldStyle, ...fullSpan }}>
|
|
|
|
|
|
<label style={labelStyle}>拟申请的法定安全评价业务范围 *</label>
|
|
|
|
|
|
<div
|
|
|
|
|
|
style={{
|
|
|
|
|
|
display: "flex",
|
|
|
|
|
|
flexWrap: "wrap",
|
|
|
|
|
|
gap: "0.3rem 1rem",
|
|
|
|
|
|
padding: "0.45rem 0.75rem",
|
|
|
|
|
|
border: "1px solid #d9d9d9",
|
|
|
|
|
|
borderRadius: 6,
|
|
|
|
|
|
background: "#f8fafc",
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{businessScope.map((opt) => (
|
|
|
|
|
|
<label
|
|
|
|
|
|
key={opt.value}
|
|
|
|
|
|
style={{ fontSize: "0.82rem", color: "#1e293b", cursor: "not-allowed" }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<input type="checkbox" checked={opt.checked} disabled /> {opt.label}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<ReadonlyField label="备案属地 *" value={detail.filingTerritoryName} />
|
|
|
|
|
|
<ReadonlyField label="备案单位 *" value={detail.filingUnitName} />
|
|
|
|
|
|
<ReadonlyField label="备案单位类型 *" value={detail.filingUnitTypeName} />
|
|
|
|
|
|
<ReadonlyField label="统一社会信用代码 *" value={detail.creditCode} />
|
|
|
|
|
|
<ReadonlyField label="注册地址 *" value={detail.registerAddress} span={fullSpan} />
|
|
|
|
|
|
<ReadonlyField label="办公地址 *" value={detail.officeAddress} span={fullSpan} />
|
|
|
|
|
|
<ReadonlyField label="信息公开网址 *" value={detail.infoDisclosureUrl} />
|
|
|
|
|
|
<div style={fieldStyle}>
|
|
|
|
|
|
<label style={labelStyle}>资质证书编号 *</label>
|
|
|
|
|
|
<Input readOnly value={detail.qualCertNo || "-"} style={inputStyle} />
|
|
|
|
|
|
<span style={{ color: "#8b95a5", fontSize: "0.72rem" }}>
|
|
|
|
|
|
初次申请无需填写,已有资质备案时填写
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
2026-08-10 17:02:48 +08:00
|
|
|
|
{/* 法定代表人:后端 CO 字段为 legalRepresentative(表 legal_representative);变更时间:2026-08-10 */}
|
|
|
|
|
|
<ReadonlyField
|
|
|
|
|
|
label="法定代表人 *"
|
|
|
|
|
|
value={detail.legalRepresentative || detail.legalPersonName || detail.legalPerson}
|
|
|
|
|
|
/>
|
2026-08-10 14:16:49 +08:00
|
|
|
|
<ReadonlyField label="法定代表人电话 *" value={detail.legalPersonPhone} />
|
|
|
|
|
|
<ReadonlyField label="传真 *" value={detail.fax} />
|
|
|
|
|
|
<ReadonlyField label="联系人及电话 *" value={detail.contactPhone} />
|
|
|
|
|
|
<ReadonlyField label="固定资产总值(万元) *" value={detail.fixedAssetAmount} />
|
|
|
|
|
|
<ReadonlyField label="工作场所建筑面积(㎡) *" value={detail.workplaceArea} />
|
|
|
|
|
|
<ReadonlyField label="档案室面积(㎡) *" value={detail.archiveRoomArea} />
|
|
|
|
|
|
<ReadonlyField label="专职安全评价师数量 *" value={detail.fulltimeEvaluatorCount} />
|
|
|
|
|
|
<ReadonlyField label="注册安全工程师数量 *" value={detail.registeredEngineerCount} />
|
|
|
|
|
|
<div style={{ ...fieldStyle, ...fullSpan }}>
|
|
|
|
|
|
<label style={labelStyle}>单位基本情况介绍(可附页) *</label>
|
|
|
|
|
|
<TextArea readOnly rows={5} value={detail.unitIntro || ""} style={inputStyle} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ ...fieldStyle, ...fullSpan }}>
|
|
|
|
|
|
<label style={labelStyle}>营业执照</label>
|
|
|
|
|
|
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
|
|
|
|
|
|
{attachmentList.length ? (
|
|
|
|
|
|
<>
|
2026-08-10 17:02:48 +08:00
|
|
|
|
<PreviewUrlButton url={attachmentList[0].url} size="small">
|
2026-08-10 14:16:49 +08:00
|
|
|
|
查看营业执照
|
2026-08-10 17:02:48 +08:00
|
|
|
|
</PreviewUrlButton>
|
2026-08-10 14:16:49 +08:00
|
|
|
|
<span style={{ color: "#8b95a5", fontSize: "0.75rem" }}>
|
|
|
|
|
|
{attachmentList[0].name || "营业执照.pdf"}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<span style={{ color: "#8b95a5", fontSize: "0.8rem" }}>—</span>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-06-30 15:59:15 +08:00
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-08-10 14:16:49 +08:00
|
|
|
|
key: "management",
|
|
|
|
|
|
label: "2. 管理人员基本情况汇总表",
|
2026-06-30 15:59:15 +08:00
|
|
|
|
children: (
|
2026-08-10 14:16:49 +08:00
|
|
|
|
<Table
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
bordered
|
|
|
|
|
|
rowKey="id"
|
|
|
|
|
|
pagination={false}
|
|
|
|
|
|
dataSource={managerRows}
|
|
|
|
|
|
scroll={{ x: 900 }}
|
|
|
|
|
|
columns={basePersonCols(false)}
|
|
|
|
|
|
/>
|
2026-06-30 15:59:15 +08:00
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-08-10 14:16:49 +08:00
|
|
|
|
key: "evaluators",
|
|
|
|
|
|
label: "3. 专职安全评价师基本情况汇总表",
|
2026-06-30 15:59:15 +08:00
|
|
|
|
children: (
|
2026-08-10 14:16:49 +08:00
|
|
|
|
<Table
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
bordered
|
|
|
|
|
|
rowKey="id"
|
|
|
|
|
|
pagination={false}
|
|
|
|
|
|
dataSource={evaluatorRows}
|
|
|
|
|
|
scroll={{ x: 1100 }}
|
|
|
|
|
|
columns={basePersonCols(true)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "materials",
|
|
|
|
|
|
label: "4. 申请材料",
|
|
|
|
|
|
children: (
|
|
|
|
|
|
<Table
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
bordered
|
|
|
|
|
|
rowKey="id"
|
|
|
|
|
|
pagination={false}
|
|
|
|
|
|
dataSource={Array.isArray(detail.materials) ? detail.materials : []}
|
|
|
|
|
|
columns={materialCols}
|
|
|
|
|
|
/>
|
2026-06-30 15:59:15 +08:00
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-08-10 14:16:49 +08:00
|
|
|
|
key: "equipment",
|
|
|
|
|
|
label: "5. 设备清单",
|
2026-06-30 15:59:15 +08:00
|
|
|
|
children: (
|
2026-08-10 14:16:49 +08:00
|
|
|
|
<Table
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
bordered
|
|
|
|
|
|
rowKey="id"
|
|
|
|
|
|
pagination={false}
|
|
|
|
|
|
dataSource={Array.isArray(detail.equipmentList) ? detail.equipmentList : []}
|
2026-06-30 15:59:15 +08:00
|
|
|
|
columns={[
|
|
|
|
|
|
{ title: "序号", width: 60, render: (_, __, i) => i + 1 },
|
2026-08-10 14:16:49 +08:00
|
|
|
|
{ title: "装备名称", dataIndex: "deviceName", ellipsis: true },
|
|
|
|
|
|
{ title: "规格型号", dataIndex: "deviceModel", ellipsis: true },
|
|
|
|
|
|
{ title: "生产厂家", dataIndex: "manufacturer" },
|
2026-08-05 11:24:39 +08:00
|
|
|
|
{
|
2026-08-10 14:16:49 +08:00
|
|
|
|
title: "计量检定情况",
|
|
|
|
|
|
width: 120,
|
2026-08-05 11:24:39 +08:00
|
|
|
|
render: (_, record) => {
|
2026-08-10 14:16:49 +08:00
|
|
|
|
const tag = getCalibrationTag(record);
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Tag color={tag.status === "success" ? "success" : "warning"}>
|
|
|
|
|
|
{tag.label}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
);
|
2026-08-05 11:24:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
2026-08-10 14:16:49 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: "操作",
|
|
|
|
|
|
width: 80,
|
2026-07-02 16:58:07 +08:00
|
|
|
|
render: (_, record) => (
|
2026-08-10 17:45:12 +08:00
|
|
|
|
<Button
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setEquipId(record.sourceEquipmentId);
|
|
|
|
|
|
setEquipViewOpen(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-06-30 15:59:15 +08:00
|
|
|
|
查看
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
]}
|
|
|
|
|
|
/>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-08-10 14:16:49 +08:00
|
|
|
|
key: "signature",
|
|
|
|
|
|
label: "6. 机构负责人签字",
|
2026-06-30 15:59:15 +08:00
|
|
|
|
children: (
|
2026-08-10 14:16:49 +08:00
|
|
|
|
<div
|
|
|
|
|
|
style={{
|
|
|
|
|
|
border: "1px solid #d9d9d9",
|
|
|
|
|
|
borderRadius: 8,
|
|
|
|
|
|
padding: "1.25rem 1.5rem",
|
|
|
|
|
|
background: "#fafafa",
|
|
|
|
|
|
fontSize: "0.85rem",
|
|
|
|
|
|
lineHeight: 1.9,
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<p style={{ textAlign: "center", fontWeight: 600, fontSize: "0.95rem", marginBottom: "0.75rem" }}>
|
|
|
|
|
|
机构负责人签字确认
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
机构端已完成资质申请第六项负责人签字,确认本次提交的基础信息、管理人员、专职安全评价师、申请材料和设备清单真实、准确、完整。
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div style={gridStyle}>
|
|
|
|
|
|
<ReadonlyField label="签字人" value={commitment.legalRepName} />
|
|
|
|
|
|
<ReadonlyField label="职务" value="法定代表人 / 机构负责人" />
|
|
|
|
|
|
<ReadonlyField label="签字方式" value="APP认证签字" />
|
|
|
|
|
|
<ReadonlyField label="签字时间" value={commitment.signDate} />
|
|
|
|
|
|
<div style={{ ...fieldStyle, ...fullSpan }}>
|
|
|
|
|
|
<label style={labelStyle}>签字文件</label>
|
|
|
|
|
|
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
|
|
|
|
|
|
{commitment.legalRepSignatureUrl ? (
|
|
|
|
|
|
<>
|
2026-08-10 17:02:48 +08:00
|
|
|
|
<PreviewUrlButton url={commitment.legalRepSignatureUrl} size="small">
|
2026-08-10 14:16:49 +08:00
|
|
|
|
查看签字件
|
2026-08-10 17:02:48 +08:00
|
|
|
|
</PreviewUrlButton>
|
2026-08-10 14:16:49 +08:00
|
|
|
|
<Tag color="success">已签字</Tag>
|
|
|
|
|
|
</>
|
2026-08-05 11:24:39 +08:00
|
|
|
|
) : (
|
2026-08-10 14:16:49 +08:00
|
|
|
|
<Tag color="warning">未签字</Tag>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{commitment.legalRepSignatureUrl && (
|
|
|
|
|
|
<Image
|
|
|
|
|
|
src={commitment.legalRepSignatureUrl}
|
|
|
|
|
|
style={{ width: 120, height: 120, objectFit: "contain", marginTop: 8 }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-06-30 15:59:15 +08:00
|
|
|
|
),
|
|
|
|
|
|
},
|
2026-08-10 17:02:48 +08:00
|
|
|
|
...(Array.isArray(extraTabs) ? extraTabs : []),
|
2026-06-30 15:59:15 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Tabs items={items} />
|
|
|
|
|
|
|
2026-08-10 14:16:49 +08:00
|
|
|
|
<StaffViewModal
|
|
|
|
|
|
open={!!viewId}
|
|
|
|
|
|
currentId={viewId}
|
|
|
|
|
|
onCancel={() => setViewId("")}
|
|
|
|
|
|
/>
|
2026-06-30 15:59:15 +08:00
|
|
|
|
|
2026-08-10 17:45:12 +08:00
|
|
|
|
{equipViewOpen && (
|
|
|
|
|
|
<EquipViewModal
|
|
|
|
|
|
open={equipViewOpen}
|
|
|
|
|
|
currentId={equipId}
|
|
|
|
|
|
equipInfoGet={equipInfoGet}
|
|
|
|
|
|
onCancel={() => {
|
|
|
|
|
|
setEquipViewOpen(false);
|
|
|
|
|
|
setEquipId("");
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2026-08-10 14:16:49 +08:00
|
|
|
|
</>
|
2026-06-30 15:59:15 +08:00
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-08-10 17:45:12 +08:00
|
|
|
|
export default Connect([NS_QUAL_REVIEW, NS_EQUIP_INFO], true)(FilingTabs);
|