tangjie 2026-08-10 17:15:42 +08:00
commit 35452ae9a2
10 changed files with 1485 additions and 1682 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,8 @@
packages: ["."]
packages: [ "." ]
allowBuilds:
es5-ext: set this to true or false
zy-react-library: set this to true or false
core-js: false
es5-ext: false
zy-react-library: false
hoist: true
nodeLinker: hoisted
onlyBuiltDependencies:

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import { Button, Form, Select, Switch, Table, Tag } from "antd";
import { Button, Form, Select, Table, Tag } from "antd";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
@ -20,7 +20,6 @@ import {
import {
getQualReviewMockEnabled,
mockQueryExpertList,
setQualReviewMockEnabled,
} from "../mockData";
import "../QualReview/index.less";
@ -36,7 +35,8 @@ const ExpertVerification = (props) => {
const [form] = Form.useForm();
const { qualReview, queryReviewList } = props;
const { qualReviewList, qualReviewTotal, qualReviewLoading } = qualReview || {};
const [mockEnabled, setMockEnabled] = useState(getQualReviewMockEnabled());
// mock 默认关闭(真实接口);?mock=1 可临时开启演示。变更时间2026-08-10
const mockEnabled = getQualReviewMockEnabled();
const loadList = (useMock = mockEnabled) => {
if (useMock) {
@ -74,13 +74,6 @@ const ExpertVerification = (props) => {
loadList();
}, []);
const handleToggleMock = (v) => {
setQualReviewMockEnabled(v);
setMockEnabled(v);
router.query = { ...router.query, current: 1 };
loadList(v);
};
const renderConfirmStatus = (record) => {
const name = record.filingStatusName || record.statusName;
if (name) {
@ -225,14 +218,6 @@ const ExpertVerification = (props) => {
<span className="totalText">
<strong>{qualReviewTotal || 0}</strong>
</span>
<Tag
className={mockEnabled ? "tag_warning" : "tag_default"}
style={{ marginRight: 0 }}
>
{mockEnabled ? "Mock 演示中" : "真实接口"}
</Tag>
<span style={{ fontSize: "0.78rem", color: "#64748b" }}>Mock 数据</span>
<Switch size="small" checked={mockEnabled} onChange={handleToggleMock} />
</div>
</div>

View File

@ -4,7 +4,11 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_QUAL_REVIEW } from "~/enumerate/namespace";
import StaffViewModal from "~/components/StaffViewModal";
import PreviewUrlButton from "~/components/PreviewUrlButton/index";
import { QUALIFICATION_INDUSTRY_OPTIONS, QUALIFICATION_INDUSTRY_OPTIONS_MAP } from "~/enumerate/enterpriseOptions";
import {
QUALIFICATION_INDUSTRY_OPTIONS,
QUALIFICATION_INDUSTRY_OPTIONS_MAP,
TITLE_LEVEL_MAP,
} from "~/enumerate/enterpriseOptions";
import { CAPABILITY_MAP } from "~/enumerate/constant";
import { getCalibrationTag } from "../mockData";
@ -32,6 +36,49 @@ function calcAge(birthDate) {
return age >= 0 ? age : "-";
}
/**
* 职称名称后端 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}` : "-";
}
const gridStyle = {
display: "grid",
gridTemplateColumns: "1fr 1fr",
@ -124,10 +171,14 @@ const FilingTabs = ({
isReview = false,
compliance = {},
onComplianceChange,
/**
* 额外 Tab如机构备案详情7. 变更记录数组元素与 antd Tabs items 同构
* 不传时保持原 6 Tab不影响初审/确认/专家核验展示变更时间2026-08-10
*/
extraTabs,
}) => {
const [viewId, setViewId] = useState("");
const [equipRecord, setEquipRecord] = useState(null);
const [previewImage, setPreviewImage] = useState("");
const personnelRows = useMemo(
() =>
@ -138,20 +189,24 @@ const FilingTabs = ({
[detail.personnelList],
);
/** 管理人员:岗位含"法定代表人/负责人";无匹配时回退展示全部人员 */
/**
* 管理人员判定technicalDirectorFlag(技术负责人) processControlLeaderFlag(过程控制负责人) true
* 法定代表人/负责人按岗位名兜底归入管理人员
* 备案变更详情人员 COQualFilingPersonnelChangeInfoCO暂未返回 flag 字段靠岗位名兜底判断待后端补充 flag
* 变更时间2026-08-10按后端人员标识字段拆分
*/
const isManager = (item) =>
item.technicalDirectorFlag === true ||
item.processControlLeaderFlag === true ||
/法定代表人|负责人/.test(item.positionName || "");
const managerRows = useMemo(() => {
const filtered = personnelRows.filter((item) =>
/法定代表人|技术负责人|过程控制负责人|负责人/.test(item.positionName || ""),
);
return filtered.length ? filtered : personnelRows;
return personnelRows.filter(isManager);
}, [personnelRows]);
/** 专职安全评价师:评价师证书号存在或岗位含"评价师";无匹配时回退展示全部人员 */
/** 专职安全评价师:非管理人员的其余人员 */
const evaluatorRows = useMemo(() => {
const filtered = personnelRows.filter(
(item) => item.evaluatorCertNo || /评价师/.test(item.positionName || ""),
);
return filtered.length ? filtered : personnelRows;
return personnelRows.filter((item) => !isManager(item));
}, [personnelRows]);
const attachmentList = useMemo(() => {
@ -173,16 +228,6 @@ const FilingTabs = ({
}));
}, [detail.businessScope]);
const openPreview = (file) => {
const url = file?.url;
if (!url) return;
if (/\.(jpe?g|png|gif|webp|bmp)(\?|#|$)/i.test(`${file.name || ""} ${url}`.toLowerCase())) {
setPreviewImage(url);
} else {
window.open(url, "_blank");
}
};
/** 人员表格列(管理/评价师共用基础) */
const basePersonCols = (withScope) => [
{ title: "序号", width: 60, render: (_, __, i) => i + 1 },
@ -191,20 +236,42 @@ const FilingTabs = ({
{
title: "学历/专业",
width: 160,
// 后端字段educationName(学历) / basicDisciplineMajorName(专业)变更时间2026-08-10
render: (_, r) =>
[r.educationLevelName, r.majorName || r.educationTypeName].filter(Boolean).join(" / ") || "-",
[r.educationName, r.basicDisciplineMajorName].filter(Boolean).join(" / ") || "-",
},
{
title: "职称",
width: 120,
render: (_, r) => titleNames(r.titleCodeArr) || r.titleName || "-",
},
{ title: "职称", dataIndex: "titleName", width: 120, render: (v) => v || "-" },
{
title: "从业年限",
width: 100,
render: (_, r) => r.workYears || r.yearsOfWorking || "-",
render: (_, r) =>
r.workYears || r.yearsOfWorking || workYearsText(r.joinWorkDate),
},
{
title: "专业能力",
width: 180,
render: (_, r) => {
const codes = r.professionalCapabilityCodeList || [];
// 能力编码:兼容 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)
: []) ||
[];
return codes.length
? codes.map((code) => CAPABILITY_MAP[code] || code).join("、")
: "-";
@ -341,7 +408,11 @@ const FilingTabs = ({
初次申请无需填写已有资质备案时填写
</span>
</div>
<ReadonlyField label="法定代表人 *" value={detail.legalPersonName || detail.legalPerson} />
{/* 法定代表人:后端 CO 字段为 legalRepresentative表 legal_representative变更时间2026-08-10 */}
<ReadonlyField
label="法定代表人 *"
value={detail.legalRepresentative || detail.legalPersonName || detail.legalPerson}
/>
<ReadonlyField label="法定代表人电话 *" value={detail.legalPersonPhone} />
<ReadonlyField label="传真 *" value={detail.fax} />
<ReadonlyField label="联系人及电话 *" value={detail.contactPhone} />
@ -359,12 +430,9 @@ const FilingTabs = ({
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
{attachmentList.length ? (
<>
<Button
size="small"
onClick={() => openPreview(attachmentList[0])}
>
<PreviewUrlButton url={attachmentList[0].url} size="small">
查看营业执照
</Button>
</PreviewUrlButton>
<span style={{ color: "#8b95a5", fontSize: "0.75rem" }}>
{attachmentList[0].name || "营业执照.pdf"}
</span>
@ -491,17 +559,9 @@ const FilingTabs = ({
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
{commitment.legalRepSignatureUrl ? (
<>
<Button
size="small"
onClick={() =>
openPreview({
url: commitment.legalRepSignatureUrl,
name: "机构负责人签字件",
})
}
>
<PreviewUrlButton url={commitment.legalRepSignatureUrl} size="small">
查看签字件
</Button>
</PreviewUrlButton>
<Tag color="success">已签字</Tag>
</>
) : (
@ -519,6 +579,7 @@ const FilingTabs = ({
</div>
),
},
...(Array.isArray(extraTabs) ? extraTabs : []),
];
return (
@ -532,15 +593,6 @@ const FilingTabs = ({
/>
<EquipViewModal record={equipRecord} onCancel={() => setEquipRecord(null)} />
<Image
src={previewImage}
preview={{
visible: !!previewImage,
onVisibleChange: (v) => !v && setPreviewImage(""),
}}
style={{ display: "none" }}
/>
</>
);
};

View File

@ -2,23 +2,62 @@ import React, { useEffect } from "react";
import { Button, Form, Select, Table, Tag } from "antd";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
import { tools } from "@cqsjjb/jjb-common-lib";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_QUAL_REVIEW } from "~/enumerate/namespace";
import { FILING_STATUS_MAP, FILING_STATUS_MAP_CHANGE } from "~/enumerate/constant";
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP } from "~/enumerate/enterpriseOptions";
import { REVIEW_BUSINESS_SCOPE_OPTIONS, REVIEW_STATUS_CLASS } from "../reviewPageQuery";
import {
getQualReviewMockEnabled,
mockQueryChangeList,
} from "../mockData";
import "../QualReview/index.less";
const { router } = tools;
/** 变更状态筛选(原型:停滞中/待审核/已审核;对应备案状态码 3 已打回/4 变更审核中/1 已备案) */
const CHANGE_STATUS_OPTIONS = [
{ label: "待审核", value: 4 },
{ label: "停滞中", value: 3 },
{ label: "已审核", value: 1 },
];
/** 变更状态配色(对齐原型 tag-warning/danger/success */
function changeStatusColor(name) {
if (/待审核|审核中|变更审核中/.test(name || "")) return "warning";
if (/已打回|停滞|退回/.test(name || "")) return "error";
if (/已审核|已备案|通过/.test(name || "")) return "success";
return "default";
}
/**
* 备案变更管理原型 mod-qual-change
* - 搜索机构名称 / 备案编号 / 安全评价业务范围 / 变更状态
* - 复选框序号机构名称机构类型备案编号业务范围变更项变更状态操作查看 / 审核
* - 查看/审核与资质初审备案一致查看 FilingDetail?mode=change审核 QualReviewForm?mode=change
* - 变更项后端列表 CO 未聚合明细在 qual_filing_change_detail.change_item_name真实接口暂显示 "-"
* - Mock 演示默认开启所有行展示 查看 + 审核 按钮
*/
const QualChange = (props) => {
const [form] = Form.useForm();
const { qualReview, queryQualChangePage } = props;
const { qualReviewList, qualReviewTotal, qualReviewLoading } = qualReview || {};
// mock 默认关闭(真实接口);?mock=1 可临时开启演示。变更时间2026-08-10
const mockEnabled = getQualReviewMockEnabled();
const handleSearch = () => {
const loadList = (useMock = mockEnabled) => {
if (useMock) {
const res = mockQueryChangeList(router.query);
props.resetModelState(NS_QUAL_REVIEW, {
qualReviewList: res.data,
qualReviewTotal: res.total,
qualReviewLoading: false,
});
return;
}
queryQualChangePage({
...router.query,
current: router.query.current || 1,
@ -26,6 +65,10 @@ const QualChange = (props) => {
});
};
const handleSearch = () => {
loadList();
};
const handleReset = (values) => {
router.query = {
...router.query,
@ -38,7 +81,7 @@ const QualChange = (props) => {
useEffect(() => {
form.setFieldsValue(router.query);
handleSearch();
loadList();
}, []);
const columns = [
@ -50,22 +93,41 @@ const QualChange = (props) => {
},
{ title: "机构名称", dataIndex: "filingUnitName", width: 220, ellipsis: true },
{ title: "机构类型", dataIndex: "filingUnitTypeName", width: 100 },
{ title: "备案编号", dataIndex: "id", width: 140, ellipsis: true },
{ title: "经营范围", dataIndex: "businessScope", width: 180, ellipsis: true,
{ title: "备案编号", dataIndex: "filingNo", width: 140, ellipsis: true, render: (v) => v || "-" },
{
title: "安全评价业务范围",
dataIndex: "businessScope",
width: 180,
ellipsis: true,
render: (_, record) => {
if(Array.isArray(record.businessScope)){
return record.businessScope.map(item => QUALIFICATION_INDUSTRY_OPTIONS_MAP[item]).join("、");
if (Array.isArray(record.businessScope)) {
return record.businessScope
.map((item) => QUALIFICATION_INDUSTRY_OPTIONS_MAP[item] || item)
.join("、");
}
return record.businessScope || "-";
}
},
},
},
{
title: "备案状态",
title: "变更项",
dataIndex: "changeItemNames",
width: 160,
ellipsis: true,
// 后端列表 CO 未聚合变更项(明细在 qual_filing_change_detail.change_item_name待后端补充mock 有值
render: (v) => v || "-",
},
{
title: "变更状态",
dataIndex: "filingStatusCode",
width: 100,
render: (code) => {
const config = FILING_STATUS_MAP[code];
return config ? <Tag color={config.color}>{config.label}</Tag> : "--";
width: 110,
render: (_, record) => {
const name = record.filingStatusName || record.statusName;
const color = changeStatusColor(name);
return name ? (
<Tag className={REVIEW_STATUS_CLASS[color] || "tag_default"}>{name}</Tag>
) : (
"--"
);
},
},
{
@ -73,12 +135,21 @@ const QualChange = (props) => {
width: 140,
fixed: "right",
render: (_, record) => (
<TableAction>
<Button type="link" size="small" onClick={() => props.history.push(`FilingDetail?id=${record.id}&mode=change`)}>
<TableAction>
<Button
type="link"
size="small"
onClick={() => props.history.push(`FilingDetail?id=${record.id}&mode=change`)}
>
查看
</Button>
{(record.filingStatusCode == 2 || record.filingStatusCode == 4) && (
<Button type="link" size="small" onClick={() => props.history.push(`QualReviewForm?id=${record.id}&mode=change`)}>
{/* mock 演示模式展示全部“审核”按钮;真实模式仅 审核中/变更审核中 可审核 */}
{(mockEnabled || [2, 4].includes(Number(record.filingStatusCode))) && (
<Button
type="link"
size="small"
onClick={() => props.history.push(`QualReviewForm?id=${record.id}&mode=change`)}
>
审核
</Button>
)}
@ -92,22 +163,34 @@ const QualChange = (props) => {
<SearchForm
style={{ marginBottom: 24 }}
form={form}
defaultExpand
loading={qualReviewLoading}
formLine={[
<Form.Item key="id" name="id" normalize={(v) => (v ? v.replace(/\D/g, "").slice(0, 20) : v)}>
<ControlWrapper.Input label="备案编号" placeholder="请输入" allowClear maxLength={20} />
<Form.Item key="filingUnitName" name="filingUnitName">
<ControlWrapper.Input label="机构名称" placeholder="请输入机构名称" allowClear />
</Form.Item>,
<Form.Item key="filingNo" name="filingNo">
<ControlWrapper.Input label="备案编号" placeholder="请输入备案编号" allowClear />
</Form.Item>,
<Form.Item key="businessScope" name="businessScope">
<ControlWrapper.Select
label="安全评价业务范围"
placeholder="请选择"
allowClear
showSearch
optionFilterProp="label"
style={{ width: "100%" }}
options={REVIEW_BUSINESS_SCOPE_OPTIONS}
/>
</Form.Item>,
<Form.Item key="filingStatusCode" name="filingStatusCode">
<ControlWrapper.Select
label="备案状态"
label="变更状态"
placeholder="请选择"
allowClear
style={{ width: "100%" }}
>
{Object.entries(FILING_STATUS_MAP_CHANGE).map(([code, config]) => (
<Select.Option key={code} value={Number(code)}>{config.label}</Select.Option>
))}
</ControlWrapper.Select>
options={CHANGE_STATUS_OPTIONS}
/>
</Form.Item>,
]}
onReset={handleReset}
@ -122,11 +205,20 @@ const QualChange = (props) => {
}}
/>
<div className="toolbar">
<div className="toolbarLeft">
<span className="totalText">
<strong>{qualReviewTotal || 0}</strong>
</span>
</div>
</div>
<Table
rowKey="id"
columns={columns}
dataSource={qualReviewList}
scroll={{ y: props.scrollY }}
dataSource={Array.isArray(qualReviewList) ? qualReviewList : []}
rowSelection={{}}
scroll={{ x: 1100, y: props.scrollY }}
loading={qualReviewLoading}
pagination={{
total: qualReviewTotal,
@ -149,4 +241,4 @@ const QualChange = (props) => {
);
};
export default Connect([NS_QUAL_REVIEW], true)(AntdTableFuncControl(QualChange));
export default Connect([NS_QUAL_REVIEW], true)(AntdTableFuncControl(QualChange));

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import { Button, Form, Select, Switch, Table, Tag } from "antd";
import { Button, Form, Select, Table, Tag } from "antd";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
@ -20,7 +20,6 @@ import {
import {
getQualReviewMockEnabled,
mockQueryConfirmList,
setQualReviewMockEnabled,
} from "../mockData";
import "../QualReview/index.less";
@ -37,7 +36,8 @@ const QualConfirm = (props) => {
const [form] = Form.useForm();
const { qualReview, queryReviewList } = props;
const { qualReviewList, qualReviewTotal, qualReviewLoading } = qualReview || {};
const [mockEnabled, setMockEnabled] = useState(getQualReviewMockEnabled());
// mock 默认关闭(真实接口);?mock=1 可临时开启演示。变更时间2026-08-10
const mockEnabled = getQualReviewMockEnabled();
const loadList = (useMock = mockEnabled) => {
if (useMock) {
@ -75,13 +75,6 @@ const QualConfirm = (props) => {
loadList();
}, []);
const handleToggleMock = (v) => {
setQualReviewMockEnabled(v);
setMockEnabled(v);
router.query = { ...router.query, current: 1 };
loadList(v);
};
const renderConfirmStatus = (record) => {
const name = record.filingStatusName || record.statusName;
if (name) {
@ -224,14 +217,6 @@ const QualConfirm = (props) => {
<span className="totalText">
<strong>{qualReviewTotal || 0}</strong>
</span>
<Tag
className={mockEnabled ? "tag_warning" : "tag_default"}
style={{ marginRight: 0 }}
>
{mockEnabled ? "Mock 演示中" : "真实接口"}
</Tag>
<span style={{ fontSize: "0.78rem", color: "#64748b" }}>Mock 数据</span>
<Switch size="small" checked={mockEnabled} onChange={handleToggleMock} />
</div>
</div>

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import { Button, Form, Select, Switch, Table, Tag } from "antd";
import React, { useEffect } from "react";
import { Button, Form, Select, Table, Tag } from "antd";
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
@ -20,7 +20,6 @@ import {
import {
getQualReviewMockEnabled,
mockQueryReviewList,
setQualReviewMockEnabled,
} from "../mockData";
import "./index.less";
@ -36,7 +35,8 @@ const QualReview = (props) => {
const [form] = Form.useForm();
const { qualReview, queryReviewList } = props;
const { qualReviewList, qualReviewTotal, qualReviewLoading } = qualReview || {};
const [mockEnabled, setMockEnabled] = useState(getQualReviewMockEnabled());
// mock 默认关闭(真实接口);?mock=1 可临时开启演示。变更时间2026-08-10
const mockEnabled = getQualReviewMockEnabled();
/**
* 数据加载mock 模式走本地高内聚数据不请求后端
@ -78,14 +78,6 @@ const QualReview = (props) => {
loadList();
}, []);
/** 随时切换 mock/真实接口 */
const handleToggleMock = (v) => {
setQualReviewMockEnabled(v);
setMockEnabled(v);
router.query = { ...router.query, current: 1 };
loadList(v);
};
/** 审核状态渲染:优先后端文案,缺失时按状态编码推断 */
const renderStatus = (record) => {
const name = record.filingStatusName || record.statusName;
@ -266,14 +258,6 @@ const QualReview = (props) => {
<span className="totalText">
<strong>{qualReviewTotal || 0}</strong>
</span>
<Tag
className={mockEnabled ? "tag_warning" : "tag_default"}
style={{ marginRight: 0 }}
>
{mockEnabled ? "Mock 演示中" : "真实接口"}
</Tag>
<span style={{ fontSize: "0.78rem", color: "#64748b" }}>Mock 数据</span>
<Switch size="small" checked={mockEnabled} onChange={handleToggleMock} />
</div>
</div>

View File

@ -12,8 +12,11 @@
* 开关优先级URL ?mock= > localStorage(qualReviewMockEnabled) > 默认开启
*/
/** 默认是否启用 mocktrue=演示模式false=真实后端) */
export const QUAL_REVIEW_MOCK_DEFAULT_ENABLED = true;
/**
* 默认是否启用 mocktrue=演示模式false=真实后端
* 2026-08-10 按用户要求默认切换为真实接口mock 代码/数据保留仍可通过 ?mock=1 localStorage 临时开启演示
*/
export const QUAL_REVIEW_MOCK_DEFAULT_ENABLED = false;
const MOCK_STORAGE_KEY = "qualReviewMockEnabled";
@ -240,12 +243,14 @@ export const MOCK_QUAL_FILING_DETAIL = {
{ id: 10, materialContent: "机构内部管理制度(非受控版).pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m10.pdf" },
],
personnelList: [
{ id: 101, sourcePersonnelId: 1001, personName: "郑远平", positionName: "法定代表人", titleName: "高级工程师", educationLevelName: "本科", majorName: "安全工程", workYears: "18年", genderCode: 1 },
{ id: 102, sourcePersonnelId: 1002, personName: "张建国", positionName: "技术负责人", titleName: "正高级工程师", educationLevelName: "本科", majorName: "化工工艺", workYears: "16年", genderCode: 1 },
{ id: 103, sourcePersonnelId: 1003, personName: "王丽萍", positionName: "过程控制负责人", titleName: "高级工程师", educationLevelName: "本科", majorName: "安全管理", workYears: "12年", genderCode: 2 },
{ id: 201, sourcePersonnelId: 2001, personName: "李明华", positionName: "专职安全评价师", titleName: "高级工程师", educationLevelName: "硕士", majorName: "采矿工程", workYears: "12年", evaluatorCertNo: "SJP-2020-001", professionalCapabilityCodeList: ["MINING"], appliedScope: ["METAL_NONMETAL_MINING"], genderCode: 1 },
{ id: 202, sourcePersonnelId: 2002, personName: "陈志强", positionName: "专职安全评价师", titleName: "高级工程师", educationLevelName: "本科", majorName: "化工工艺", workYears: "10年", evaluatorCertNo: "SJP-2019-023", professionalCapabilityCodeList: ["CHEMICAL_PROCESS"], appliedScope: ["PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL"], genderCode: 1 },
{ id: 203, sourcePersonnelId: 2003, personName: "刘海", positionName: "专职安全评价师", titleName: "工程师", educationLevelName: "本科", majorName: "机械工程", workYears: "8年", evaluatorCertNo: "SJP-2021-045", professionalCapabilityCodeList: ["MECHANICAL"], appliedScope: ["METAL_SMELTING"], genderCode: 1 },
// technicalDirectorFlag/processControlLeaderFlagtrue 归管理人员(用户要求 2026-08-10
// 学历/专业字段educationName / basicDisciplineMajorName用户要求 2026-08-10
{ id: 101, sourcePersonnelId: 1001, personName: "郑远平", positionName: "法定代表人", titleName: "高级工程师", educationName: "本科", basicDisciplineMajorName: "安全工程", workYears: "18年", genderCode: 1, technicalDirectorFlag: false, processControlLeaderFlag: false },
{ id: 102, sourcePersonnelId: 1002, personName: "张建国", positionName: "技术负责人", titleName: "正高级工程师", educationName: "本科", basicDisciplineMajorName: "化工工艺", workYears: "16年", genderCode: 1, technicalDirectorFlag: true, processControlLeaderFlag: false },
{ id: 103, sourcePersonnelId: 1003, personName: "王丽萍", positionName: "过程控制负责人", titleName: "高级工程师", educationName: "本科", basicDisciplineMajorName: "安全管理", workYears: "12年", genderCode: 2, technicalDirectorFlag: false, processControlLeaderFlag: true },
{ id: 201, sourcePersonnelId: 2001, personName: "李明华", positionName: "专职安全评价师", titleName: "高级工程师", educationName: "硕士", basicDisciplineMajorName: "采矿工程", workYears: "12年", evaluatorCertNo: "SJP-2020-001", professionalCapabilityCodeList: ["MINING"], appliedScope: ["METAL_NONMETAL_MINING"], genderCode: 1, technicalDirectorFlag: false, processControlLeaderFlag: false },
{ id: 202, sourcePersonnelId: 2002, personName: "陈志强", positionName: "专职安全评价师", titleName: "高级工程师", educationName: "本科", basicDisciplineMajorName: "化工工艺", workYears: "10年", evaluatorCertNo: "SJP-2019-023", professionalCapabilityCodeList: ["CHEMICAL_PROCESS"], appliedScope: ["PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL"], genderCode: 1, technicalDirectorFlag: false, processControlLeaderFlag: false },
{ id: 203, sourcePersonnelId: 2003, personName: "刘海", positionName: "专职安全评价师", titleName: "工程师", educationName: "本科", basicDisciplineMajorName: "机械工程", workYears: "8年", evaluatorCertNo: "SJP-2021-045", professionalCapabilityCodeList: ["MECHANICAL"], appliedScope: ["METAL_SMELTING"], genderCode: 1, technicalDirectorFlag: false, processControlLeaderFlag: false },
],
equipmentList: [
{ id: 301, deviceName: "气相色谱仪", deviceModel: "GC-2018", manufacturer: "岛津", equipmentValue: 120000, calibrationReportUrl: "https://example.com/cal1.pdf" },
@ -346,10 +351,38 @@ export function mockQueryExpertList(params = {}) {
return filterRowsByParams(MOCK_QUAL_EXPERT_LIST, params);
}
/**
* 备案变更管理列表 mock原型 mod-qual-change4 QualFilingChangeCO 同构
* 变更时间2026-08-10
*/
export const MOCK_QUAL_CHANGE_LIST = [
{ id: 301, filingId: 1, filingUnitName: "重庆安评技术研究院有限公司", filingUnitTypeName: "本地单位", filingNo: "BA-2026-001", businessScope: ["PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL"], changeItemNames: "法定代表人变更", filingStatusCode: 4, filingStatusName: "变更审核中" },
{ id: 302, filingId: 2, filingUnitName: "重庆恒安安全评价有限公司", filingUnitTypeName: "本地单位", filingNo: "BA-2025-032", businessScope: ["PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL", "FIREWORKS_FIRECRACKERS"], changeItemNames: "注册地址变更", filingStatusCode: 3, filingStatusName: "已打回" },
{ id: 303, filingId: 3, filingUnitName: "北京中安评价中心(重庆分公司)", filingUnitTypeName: "异地单位", filingNo: "BA-2026-015", businessScope: ["METAL_NONMETAL_MINING"], changeItemNames: "业务范围变更", filingStatusCode: 4, filingStatusName: "变更审核中" },
{ id: 304, filingId: 4, filingUnitName: "重庆渝安风险评估中心", filingUnitTypeName: "本地单位", filingNo: "BA-2026-008", businessScope: ["PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL"], changeItemNames: "技术负责人变更", filingStatusCode: 1, filingStatusName: "已备案" },
];
/** 备案变更 mock 查询:机构名称/备案编号/业务范围/变更状态 过滤 + 分页 */
export function mockQueryChangeList(params = {}) {
const keyword = String(params.filingUnitName || "").trim().toLowerCase();
const noKeyword = String(params.filingNo || "").trim().toLowerCase();
let rows = MOCK_QUAL_CHANGE_LIST.filter((item) => {
if (keyword && !item.filingUnitName.toLowerCase().includes(keyword)) return false;
if (noKeyword && !String(item.filingNo || "").toLowerCase().includes(noKeyword)) return false;
if (params.businessScope && !(item.businessScope || []).includes(params.businessScope)) return false;
if (params.filingStatusCode !== undefined && params.filingStatusCode !== null && params.filingStatusCode !== "" && Number(item.filingStatusCode) !== Number(params.filingStatusCode)) return false;
return true;
});
const current = Number(params.current || 1);
const size = Number(params.size || 10);
const start = (current - 1) * size;
return { data: rows.slice(start, start + size), total: rows.length };
}
/** mock 详情(按 id 返回,找不到时返回默认详情) */
export function mockGetFilingDetail(id) {
const row =
[...MOCK_QUAL_REVIEW_LIST, ...MOCK_QUAL_CONFIRM_LIST, ...MOCK_QUAL_EXPERT_LIST].find(
[...MOCK_QUAL_REVIEW_LIST, ...MOCK_QUAL_CONFIRM_LIST, ...MOCK_QUAL_EXPERT_LIST, ...MOCK_QUAL_CHANGE_LIST].find(
(item) => String(item.id) === String(id),
);
return {

View File

@ -1,31 +1,24 @@
import {
Button,
Image,
Input,
Spin,
Table,
Tabs,
Tag,
message,
} from "antd";
import { useEffect, useMemo, useState } from "react";
import { Button, Spin, Table, Tag } from "antd";
import { useEffect, useState } from "react";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import {
fetchRegisteredOrgDetail,
fetchRegisteredOrgPersonnelList,
fetchRegisteredOrgQualificationGroups,
} from "~/utils/regulatorOrgInfo";
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP } from "~/enumerate/enterpriseOptions";
import StaffViewModal from "~/components/StaffViewModal";
import { Get } from "@cqsjjb/jjb-common-lib/http";
import PreviewUrlButton from "~/components/PreviewUrlButton/index";
import FilingTabs from "~/pages/Container/QualificationReview/FilingTabs";
const LIST_PATH = "/container/supervision/basicInfo/registeredOrg/list";
const { TextArea } = Input;
/** Mock 开关存储键(与机构备案管理列表共享,默认开启便于与原型对比) */
const ORG_MOCK_STORAGE_KEY = "registeredOrgMockEnabled";
function getOrgMockEnabled() {
try {
// URL 参数优先(?mock=1/0并持久化到 localStorage。变更时间2026-08-10
const qs = new URLSearchParams(window.location.search);
if (qs.has("mock")) {
const v = qs.get("mock") === "1";
localStorage.setItem(ORG_MOCK_STORAGE_KEY, v ? "1" : "0");
return v;
}
const stored = localStorage.getItem(ORG_MOCK_STORAGE_KEY);
if (stored === "1") return true;
if (stored === "0") return false;
@ -35,20 +28,25 @@ function getOrgMockEnabled() {
return true;
}
/** 机构基础信息详情 mock原型 mod-filing-detail 七项 Tab 数据) */
/**
* 机构基础信息详情 mock与资质备案初审 qual-filing/detail 返回结构同构便于复用 FilingTabs Tab 展示
* 变更时间2026-08-10参考初审详情取数方式重构
*/
const ORG_DETAIL_MOCK = {
unitName: "重庆安评技术研究院有限公司",
creditCode: "915001072028699512",
isLocalUnit: 1,
id: 1,
filingStatusCode: 1,
filingStatusName: "已备案",
businessScope: ["COAL_MINING", "METAL_NONMETAL_MINING", "PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL", "METAL_SMELTING"],
filingTerritoryName: "重庆市沙坪坝区",
filingUnitName: "重庆安评技术研究院有限公司",
filingUnitTypeName: "本地单位",
creditCode: "915001072028699512",
registerAddress: "重庆市沙坪坝区大学城东路20号",
businessAddress: "重庆市沙坪坝区大学城东路20号重庆科技学院第39栋西南角实习用房三楼",
infoDisclosureUrl: "https://cqap.example.com",
officeAddress: "重庆市沙坪坝区大学城东路20号重庆科技学院第39栋西南角实习用房三楼",
qualCertNo: "API-2026-001",
infoDisclosureUrl: "https://cqap.example.com",
legalRepresentative: "郑远平",
legalRepresentativePhone: "023-68705577",
legalPersonPhone: "023-68705577",
fax: "023-68705578",
contactPhone: "陈芳 / 139****9012",
fixedAssetAmount: 1200,
@ -57,36 +55,38 @@ const ORG_DETAIL_MOCK = {
fulltimeEvaluatorCount: 25,
registeredEngineerCount: 12,
unitIntro: "重庆安评技术研究院成立于2015年是一家专业从事安全评价、安全咨询、安全技术开发与转让的综合性安全技术服务机构。",
attachmentUrl: [{ name: "营业执照副本.pdf", url: "https://example.com/license.pdf" }],
personnel: [
{ id: 1001, userName: "郑远平", positionName: "法定代表人", titleName: "高级工程师", educationLevelName: "本科", majorName: "安全工程", workYears: "18年" },
{ id: 1002, userName: "张建国", positionName: "技术负责人", titleName: "正高级工程师", educationLevelName: "本科", majorName: "化工工艺", workYears: "16年" },
{ id: 1003, userName: "王丽萍", positionName: "过程控制负责人", titleName: "高级工程师", educationLevelName: "本科", majorName: "安全管理", workYears: "12年" },
{ id: 2001, userName: "李明华", positionName: "专职安全评价师", titleName: "高级工程师", educationLevelName: "硕士", majorName: "采矿工程", workYears: "12年", capabilityName: "非煤矿山安全评价", scopeName: "金属、非金属矿及其他矿采选业" },
{ id: 2002, userName: "陈志强", positionName: "专职安全评价师", titleName: "高级工程师", educationLevelName: "本科", majorName: "化工工艺", workYears: "10年", capabilityName: "化工工艺安全风险评估", scopeName: "石油加工业,化学原料、化学品及医药制造业" },
{ id: 2003, userName: "刘海", positionName: "专职安全评价师", titleName: "工程师", educationLevelName: "本科", majorName: "机械工程", workYears: "8年", capabilityName: "金属冶炼安全评价", scopeName: "金属冶炼" },
attachmentUrl: JSON.stringify([{ name: "营业执照副本.pdf", url: "https://example.com/license.pdf" }]),
personnelList: [
// technicalDirectorFlag/processControlLeaderFlagtrue 归管理人员(用户要求 2026-08-10
// 学历/专业字段educationName / basicDisciplineMajorName用户要求 2026-08-10
{ id: 1001, sourcePersonnelId: 1001, personName: "郑远平", positionName: "法定代表人", titleName: "高级工程师", educationName: "本科", basicDisciplineMajorName: "安全工程", workYears: "18年", technicalDirectorFlag: false, processControlLeaderFlag: false },
{ id: 1002, sourcePersonnelId: 1002, personName: "张建国", positionName: "技术负责人", titleName: "正高级工程师", educationName: "本科", basicDisciplineMajorName: "化工工艺", workYears: "16年", technicalDirectorFlag: true, processControlLeaderFlag: false },
{ id: 1003, sourcePersonnelId: 1003, personName: "王丽萍", positionName: "过程控制负责人", titleName: "高级工程师", educationName: "本科", basicDisciplineMajorName: "安全管理", workYears: "12年", technicalDirectorFlag: false, processControlLeaderFlag: true },
{ id: 2001, sourcePersonnelId: 2001, personName: "李明华", positionName: "专职安全评价师", titleName: "高级工程师", educationName: "硕士", basicDisciplineMajorName: "采矿工程", workYears: "12年", evaluatorCertNo: "SJP-2020-001", professionalCapabilityCodeList: ["MINING"], appliedScope: ["METAL_NONMETAL_MINING"], technicalDirectorFlag: false, processControlLeaderFlag: false },
{ id: 2002, sourcePersonnelId: 2002, personName: "陈志强", positionName: "专职安全评价师", titleName: "高级工程师", educationName: "本科", basicDisciplineMajorName: "化工工艺", workYears: "10年", evaluatorCertNo: "SJP-2019-023", professionalCapabilityCodeList: ["CHEMICAL_PROCESS"], appliedScope: ["PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL"], technicalDirectorFlag: false, processControlLeaderFlag: false },
{ id: 2003, sourcePersonnelId: 2003, personName: "刘海", positionName: "专职安全评价师", titleName: "工程师", educationName: "本科", basicDisciplineMajorName: "机械工程", workYears: "8年", evaluatorCertNo: "SJP-2021-045", professionalCapabilityCodeList: ["MECHANICAL"], appliedScope: ["METAL_SMELTING"], technicalDirectorFlag: false, processControlLeaderFlag: false },
],
materials: [
{ id: 1, name: "申请材料目录.pdf", format: "pdf", url: "https://example.com/m1.pdf" },
{ id: 2, name: "安全评价机构资质申请书及材料清单.pdf", format: "pdf", url: "https://example.com/m2.pdf" },
{ id: 3, name: "法人证明.pdf", format: "pdf", url: "https://example.com/m3.pdf" },
{ id: 4, name: "截至申请之日三年内无重大违法失信记录的查询证明.pdf", format: "pdf", url: "https://example.com/m4.pdf" },
{ id: 5, name: "申请单位法定代表人承诺书.pdf", format: "pdf", url: "https://example.com/m5.pdf" },
{ id: 6, name: "固定资产法定证明材料.pdf", format: "pdf", url: "https://example.com/m6.pdf" },
{ id: 7, name: "工作场所及档案室面积证明材料.pdf", format: "pdf", url: "https://example.com/m7.pdf" },
{ id: 8, name: "安全评价师专业能力证明.pdf", format: "pdf", url: "https://example.com/m8.pdf" },
{ id: 9, name: "专职技术负责人和支撑过程控制负责人证明材料.pdf", format: "pdf", url: "https://example.com/m9.pdf" },
{ id: 10, name: "机构内部管理制度(非受控版).pdf", format: "pdf", url: "https://example.com/m10.pdf" },
{ id: 1, materialContent: "申请材料目录.pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m1.pdf" },
{ id: 2, materialContent: "安全评价机构资质申请书及材料清单.pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m2.pdf" },
{ id: 3, materialContent: "法人证明.pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m3.pdf" },
{ id: 4, materialContent: "截至申请之日三年内无重大违法失信记录的查询证明.pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m4.pdf" },
{ id: 5, materialContent: "申请单位法定代表人承诺书.pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m5.pdf" },
{ id: 6, materialContent: "固定资产法定证明材料.pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m6.pdf" },
{ id: 7, materialContent: "工作场所及档案室面积证明材料.pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m7.pdf" },
{ id: 8, materialContent: "安全评价师专业能力证明.pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m8.pdf" },
{ id: 9, materialContent: "专职技术负责人和支撑过程控制负责人证明材料.pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m9.pdf" },
{ id: 10, materialContent: "机构内部管理制度(非受控版).pdf", materialFormat: "pdf", uploadStatusCode: 2, uploadStatusName: "已上传", attachmentUrl: "https://example.com/m10.pdf" },
],
equipment: [
{ id: 301, deviceName: "气相色谱仪", deviceModel: "GC-2018", manufacturer: "岛津", calibration: { label: "已检定", status: "success" } },
{ id: 302, deviceName: "噪声计", deviceModel: "HS-6288", manufacturer: "杭州爱华", calibration: { label: "已检定", status: "success" } },
{ id: 303, deviceName: "粉尘采样器", deviceModel: "FC-5A", manufacturer: "北京劳保所", calibration: { label: "待检定", status: "warning" } },
equipmentList: [
{ id: 301, deviceName: "气相色谱仪", deviceModel: "GC-2018", manufacturer: "岛津", equipmentValue: 120000, calibrationReportUrl: "https://example.com/cal1.pdf" },
{ id: 302, deviceName: "噪声计", deviceModel: "HS-6288", manufacturer: "杭州爱华", equipmentValue: 8000, calibrationReportUrl: "https://example.com/cal2.pdf" },
{ id: 303, deviceName: "粉尘采样器", deviceModel: "FC-5A", manufacturer: "北京劳保所", equipmentValue: 9000, calibrationReportUrl: "" },
],
commitment: {
legalRepName: "郑远平",
legalRepSignatureUrl: "https://example.com/sign.png",
signDate: "2026-06-25 16:42",
signWay: "APP认证签字",
},
changes: [
{ id: 1, submitTime: "2026-07-18 10:24", submitType: "注册提交", changeFields: "基础信息全量提交", submitter: "机构管理员", statusName: "待审核", statusColor: "warning", url: "https://example.com/c1.pdf" },
@ -94,120 +94,63 @@ const ORG_DETAIL_MOCK = {
],
};
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>
);
}
function parseQueryId(location) {
const search = location?.search || window.location.search || "";
return new URLSearchParams(search).get("id");
}
/**
* 机构基础信息详情原型 mod-filing-detail
* - 数据获取使用变更接口按变更记录 id GET /safetyEval/qual-filing-change/detail5 表聚合
* 六项 Tab 直接复用 FilingTabs基础信息/管理人员/专职评价师/申请材料/设备清单/机构负责人签字
* - 变更记录 TabGET /safetyEval/qual-filing-change/history?filingId=filingId 取详情返回的 filingId
* 返回 changeTime/changeItemName/operatorName/changeCount原型提交类型/审核状态/查看提交件后端未返回待补充
* 变更时间2026-08-10按用户要求改用带 change 的详情接口
*/
function RegisteredOrgDetailPage(props) {
const id = useMemo(
() => parseQueryId(props.location),
[props.location?.search],
);
const [orgLoading, setOrgLoading] = useState(Boolean(id));
const [extrasLoading, setExtrasLoading] = useState(Boolean(id));
const [orgName, setOrgName] = useState("");
const [info, setInfo] = useState({});
const [personnel, setPersonnel] = useState([]);
const [materials, setMaterials] = useState([]);
const [previewImage, setPreviewImage] = useState("");
const [viewPersonnelId, setViewPersonnelId] = useState("");
const id = parseQueryId(props.location);
const [detail, setDetail] = useState({});
const [changes, setChanges] = useState([]);
const [loading, setLoading] = useState(Boolean(id));
const mockEnabled = getOrgMockEnabled();
const openPreview = (file) => {
const url = file?.url;
if (!url) return;
if (/\.(jpe?g|png|gif|webp|bmp)(\?|#|$)/i.test(`${file.name || ""} ${url}`.toLowerCase())) {
setPreviewImage(url);
} else {
window.open(url, "_blank");
}
};
useEffect(() => {
if (!id) {
setOrgLoading(false);
setExtrasLoading(false);
setLoading(false);
return;
}
if (mockEnabled) {
// Mock 演示:本地注入原型完整七项 Tab 数据
setInfo(ORG_DETAIL_MOCK);
setPersonnel(ORG_DETAIL_MOCK.personnel);
setMaterials(ORG_DETAIL_MOCK.materials);
setOrgName(ORG_DETAIL_MOCK.unitName);
setOrgLoading(false);
setExtrasLoading(false);
setDetail(ORG_DETAIL_MOCK);
setChanges(ORG_DETAIL_MOCK.changes);
setLoading(false);
return;
}
let cancelled = false;
setOrgLoading(true);
setExtrasLoading(true);
setLoading(true);
(async () => {
try {
const [orgRes, groups, staffList] = await Promise.all([
fetchRegisteredOrgDetail(id),
fetchRegisteredOrgQualificationGroups(id).catch((err) => {
console.warn("[RegisteredOrgDetail] qualification load failed:", err);
return { data: {} };
}),
fetchRegisteredOrgPersonnelList(id).catch((err) => {
console.warn("[RegisteredOrgDetail] personnel load failed:", err);
return [];
}),
]);
// 详情变更聚合接口QualFilingChangeAggregationCO基础信息 + materials/equipmentList/commitment/personnelList
const detailRes = await Get(
"/safetyEval/qual-filing-change/detail",
{ id },
{ token: sessionStorage.getItem("token") },
).catch(() => ({ data: {} }));
if (cancelled) return;
const detail = orgRes?.data || {};
setInfo(detail);
setOrgName(detail.unitName || "");
// 申请材料:资质材料组 → 扁平列表(名称/格式/附件地址)
const flatMaterials = [];
Object.values(groups?.data || {}).forEach((list) => {
(Array.isArray(list) ? list : []).forEach((item, idx) => {
const url = item.certImageUrl || item.attachmentUrl || "";
flatMaterials.push({
id: item.id || `${item.licenseTypeName}-${idx}`,
name: item.certName || item.licenseTypeName || "-",
format: /\.(png|jpe?g|gif|webp|bmp)/i.test(String(url)) ? "图片" : "pdf",
url,
});
});
});
setMaterials(flatMaterials);
setPersonnel(Array.isArray(staffList) ? staffList : []);
} catch (err) {
if (!cancelled) {
console.warn("[RegisteredOrgDetail] load failed:", err);
message.error("加载机构详情失败");
}
const filingId = detailRes?.data?.filingId || id;
// 变更记录history 按原备案 filingId 查询(优先取详情返回的 filingId
const historyRes = await Get(
"/safetyEval/qual-filing-change/history",
{ filingId },
{ token: sessionStorage.getItem("token") },
).catch(() => ({ data: {} }));
if (cancelled) return;
setDetail(detailRes?.data || {});
// 变更记录history 返回 { changeCount, records:[{id, changeItemName, changeTime, operatorName}] }
setChanges(historyRes?.data?.records || []);
} finally {
if (!cancelled) {
setOrgLoading(false);
setExtrasLoading(false);
}
if (!cancelled) setLoading(false);
}
})();
return () => {
cancelled = true;
};
@ -227,352 +170,50 @@ function RegisteredOrgDetailPage(props) {
if (!id) {
return (
<PageLayout
title="机构基础信息详情"
extra={<Button onClick={goBack}>返回</Button>}
>
<p style={{ margin: 0, marginBottom: 24, color: "rgba(0, 0, 0, 0.45)" }}>
缺少机构 ID 参数
</p>
<PageLayout title="机构基础信息详情" extra={<Button onClick={goBack}>返回</Button>}>
<p style={{ margin: 0, marginBottom: 24, color: "rgba(0, 0, 0, 0.45)" }}>缺少机构 ID 参数</p>
</PageLayout>
);
}
/** 管理人员:负责人岗位优先,无匹配回退全部人员 */
const managerRows = useMemo(() => {
const filtered = personnel.filter((item) =>
/法定代表人|技术负责人|过程控制负责人|负责人/.test(
item.positionName || item.postName || item.position || "",
),
);
return filtered.length ? filtered : personnel;
}, [personnel]);
// mock 演示下 changes 为原型完整列;真实接口仅返回 变更时间/变更项/操作人
const changeColumns =
mockEnabled || changes[0]?.submitTime
? [
{ title: "提交时间", dataIndex: "submitTime", width: 150 },
{ title: "提交类型", dataIndex: "submitType", width: 100 },
{ title: "变更字段", dataIndex: "changeFields", ellipsis: true },
{ title: "提交人", dataIndex: "submitter", width: 100 },
{ title: "审核状态", width: 100, render: (_, record) => <Tag color={record.statusColor || "default"}>{record.statusName || "-"}</Tag> },
{
title: "操作",
width: 110,
render: (_, record) => (
<PreviewUrlButton url={record.url} size="small">
查看提交件
</PreviewUrlButton>
),
},
]
: [
{ title: "变更时间", dataIndex: "changeTime", width: 160 },
{ title: "变更项", dataIndex: "changeItemName", ellipsis: true },
{ title: "操作人", dataIndex: "operatorName", width: 110 },
];
/** 专职评价师:评价师证书/岗位匹配,无匹配回退全部人员 */
const evaluatorRows = useMemo(() => {
const filtered = personnel.filter((item) =>
item.evaluatorCertNo || /评价师/.test(item.positionName || item.postName || item.position || ""),
);
return filtered.length ? filtered : personnel;
}, [personnel]);
const businessScope = useMemo(() => {
const list = Array.isArray(info.businessScope) ? info.businessScope : [];
return [
{ label: "煤炭开采业", value: "COAL_MINING" },
{ label: "金属、非金属矿及其他矿采选业", value: "METAL_NONMETAL_MINING" },
{ label: "陆地石油和天然气开采业", value: "ONSHORE_OIL_GAS" },
{ label: "陆上油气管道运输业", value: "ONSHORE_OIL_GAS_PIPELINE" },
{ label: "石油加工业,化学原料、化学品及医药制造业", value: "PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL" },
{ label: "烟花爆竹制造业", value: "FIREWORKS_FIRECRACKERS" },
{ label: "金属冶炼", value: "METAL_SMELTING" },
].map((opt) => ({ ...opt, checked: list.includes(opt.value) }));
}, [info.businessScope]);
const equipmentList = info.equipment || [];
const commitment = info.commitment || {};
const changes = info.changes || [];
const tabItems = [
{
key: "info",
label: "基础信息",
children: (
<Spin spinning={orgLoading}>
<div style={gridStyle}>
<div style={{ ...fieldStyle, ...fullSpan }}>
<label style={labelStyle}>拟申请的法定安全评价业务范围</label>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(2, minmax(260px, 1fr))",
gap: "0.3rem 0.75rem",
padding: "0.75rem",
border: "1px solid #e2e8f0",
borderRadius: 8,
background: "#f8fafc",
}}
>
{businessScope.map((opt) => (
<label key={opt.value} style={{ fontWeight: 400, fontSize: "0.82rem" }}>
<input type="checkbox" checked={opt.checked} disabled /> {opt.label}
</label>
))}
</div>
</div>
<ReadonlyField label="备案属地" value={info.filingTerritoryName || info.districtName} />
<ReadonlyField label="备案单位" value={info.unitName} />
<ReadonlyField label="备案单位类型" value={info.filingUnitTypeName || (info.isLocalUnit === 1 ? "本地单位" : "异地单位")} />
<ReadonlyField label="统一社会信用代码" value={info.creditCode} />
<ReadonlyField label="注册地址" value={info.registerAddress} span={fullSpan} />
<ReadonlyField label="办公地址" value={info.businessAddress} span={fullSpan} />
<ReadonlyField label="信息公开网址" value={info.infoDisclosureUrl} />
<ReadonlyField label="资质证书编号" value={info.qualCertNo} />
<ReadonlyField label="法定代表人" value={info.legalRepresentative} />
<ReadonlyField label="法定代表人电话" value={info.legalRepresentativePhone} />
<ReadonlyField label="传真" value={info.fax} />
<ReadonlyField label="联系人及电话" value={info.contactPhone || info.principalPhone} />
<ReadonlyField label="固定资产总值(万元)" value={info.fixedAssetAmount} />
<ReadonlyField label="工作场所建筑面积(㎡)" value={info.workplaceArea} />
<ReadonlyField label="档案室面积(㎡)" value={info.archiveRoomArea} />
<ReadonlyField label="专职安全评价师数量" value={info.fulltimeEvaluatorCount} />
<ReadonlyField label="注册安全工程师数量" value={info.registeredEngineerCount} />
<div style={{ ...fieldStyle, ...fullSpan }}>
<label style={labelStyle}>单位基本情况介绍可附页</label>
<TextArea readOnly rows={4} value={info.unitIntro || ""} style={inputStyle} />
</div>
<div style={{ ...fieldStyle, ...fullSpan }}>
<label style={labelStyle}>营业执照</label>
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
{(info.attachmentUrl || []).length ? (
<>
<Button
size="small"
onClick={() => {
const first = (info.attachmentUrl || [])[0];
openPreview({
name: first.name || "营业执照",
url: first.url || (typeof first === "string" ? first : ""),
});
}}
>
查看营业执照
</Button>
<Tag color="success">{(info.attachmentUrl || [])[0]?.name || "营业执照.pdf"}</Tag>
</>
) : (
<span style={{ color: "#64748b", fontSize: "0.8rem" }}></span>
)}
</div>
</div>
</div>
</Spin>
),
},
{
key: "management",
label: "管理人员",
children: (
<Table
size="small"
bordered
rowKey="id"
pagination={false}
loading={extrasLoading}
dataSource={managerRows}
scroll={{ x: 900 }}
columns={[
{ title: "序号", width: 60, render: (_, __, i) => i + 1 },
{ title: "姓名", dataIndex: "userName", width: 110 },
{ title: "岗位", dataIndex: "positionName", ellipsis: true },
{ title: "职称", dataIndex: "titleName", width: 120, render: (v) => v || "-" },
{
title: "学历/专业",
width: 160,
render: (_, r) =>
[r.educationLevelName, r.majorName || r.educationTypeName].filter(Boolean).join(" / ") || "-",
},
{
title: "从业年限",
width: 100,
render: (_, r) => r.workYears || r.workYear || "-",
},
{
title: "操作",
width: 80,
fixed: "right",
render: (_, record) => (
<Button
type="link"
size="small"
onClick={() => setViewPersonnelId(record.id || record.sourcePersonnelId)}
>
查看
</Button>
),
},
]}
/>
),
},
{
key: "evaluators",
label: "专职评价师",
children: (
<Table
size="small"
bordered
rowKey="id"
pagination={false}
loading={extrasLoading}
dataSource={evaluatorRows}
scroll={{ x: 1100 }}
columns={[
{ title: "序号", width: 60, render: (_, __, i) => i + 1 },
{ title: "人员姓名", dataIndex: "userName", width: 110 },
{ title: "岗位类别", dataIndex: "positionName", width: 140, ellipsis: true },
{
title: "学历/专业",
width: 160,
render: (_, r) =>
[r.educationLevelName, r.majorName || r.educationTypeName].filter(Boolean).join(" / ") || "-",
},
{ title: "职称", dataIndex: "titleName", width: 120, render: (v) => v || "-" },
{
title: "专业能力",
width: 180,
render: (_, r) => r.capabilityName || r.capability || QUALIFICATION_INDUSTRY_OPTIONS_MAP[r.qualScope] || r.qualScope || "-",
},
{
title: "申请业务范围",
width: 200,
ellipsis: true,
render: (_, r) => r.scopeName || r.appliedScopeName || QUALIFICATION_INDUSTRY_OPTIONS_MAP[r.qualScope] || r.qualScope || "-",
},
{
title: "操作",
width: 80,
fixed: "right",
render: (_, record) => (
<Button
type="link"
size="small"
onClick={() => setViewPersonnelId(record.id || record.sourcePersonnelId)}
>
查看
</Button>
),
},
]}
/>
),
},
{
key: "materials",
label: "申请材料",
children: (
<Table
size="small"
bordered
rowKey="id"
pagination={false}
loading={extrasLoading}
dataSource={materials}
columns={[
{ title: "序号", width: 60, render: (_, __, i) => i + 1 },
{ title: "申请材料", dataIndex: "name", ellipsis: true },
{ title: "格式", dataIndex: "format", width: 80 },
{
title: "操作",
width: 80,
render: (_, record) => (
<Button type="link" size="small" onClick={() => openPreview(record)}>
预览
</Button>
),
},
]}
/>
),
},
{
key: "equipment",
label: "设备清单",
children: (
<Table
size="small"
bordered
rowKey="id"
pagination={false}
loading={extrasLoading}
dataSource={equipmentList}
columns={[
{ title: "序号", width: 60, render: (_, __, i) => i + 1 },
{ title: "设备名称", dataIndex: "deviceName", ellipsis: true },
{ title: "规格型号", dataIndex: "deviceModel", ellipsis: true },
{ title: "生产厂家", dataIndex: "manufacturer" },
{
title: "计量检定情况",
width: 120,
render: (_, record) => {
const cal = record.calibration || {};
return cal.label ? <Tag color={cal.status === "success" ? "success" : "warning"}>{cal.label}</Tag> : "-";
},
},
]}
/>
),
},
{
key: "signature",
label: "机构负责人签字",
children: (
<div
style={{
border: "1px solid #e2e8f0",
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={commitment.signWay || "APP认证签字"} />
<ReadonlyField label="签字时间" value={commitment.signDate} />
<div style={{ ...fieldStyle, ...fullSpan }}>
<label style={labelStyle}>签字文件</label>
<div>
{commitment.legalRepSignatureUrl ? (
<Button size="small" onClick={() => openPreview({ name: "机构负责人签字件", url: commitment.legalRepSignatureUrl })}>
查看签字件
</Button>
) : (
<span style={{ color: "#64748b", fontSize: "0.8rem" }}></span>
)}
</div>
</div>
</div>
</div>
),
},
/** 第 7 个 Tab变更记录原型 mod-filing-detail 变更记录 Tab。变更时间2026-08-10 */
const extraTabs = [
{
key: "changes",
label: "变更记录",
label: "7. 变更记录",
children: (
<Table
size="small"
bordered
rowKey="id"
pagination={false}
loading={extrasLoading}
dataSource={changes}
columns={[
{ title: "提交时间", dataIndex: "submitTime", width: 150 },
{ title: "提交类型", dataIndex: "submitType", width: 100 },
{ title: "变更字段", dataIndex: "changeFields", ellipsis: true },
{ title: "提交人", dataIndex: "submitter", width: 100 },
{
title: "审核状态",
width: 100,
render: (_, record) => <Tag color={record.statusColor || "default"}>{record.statusName || "-"}</Tag>,
},
{
title: "操作",
width: 110,
render: (_, record) => (
<Button type="link" size="small" onClick={() => openPreview({ name: "提交件", url: record.url })}>
查看提交件
</Button>
),
},
]}
dataSource={Array.isArray(changes) ? changes : []}
columns={changeColumns}
/>
),
},
@ -588,27 +229,10 @@ function RegisteredOrgDetailPage(props) {
history={props.history}
previous
>
<p style={{ margin: 0, marginBottom: 24, color: "rgba(0, 0, 0, 0.45)" }}>
{orgName || undefined}
</p>
<Tabs items={tabItems} />
{viewPersonnelId && (
<StaffViewModal
open={!!viewPersonnelId}
currentId={viewPersonnelId}
onCancel={() => setViewPersonnelId("")}
/>
)}
<Image
src={previewImage}
preview={{
visible: !!previewImage,
onVisibleChange: (v) => !v && setPreviewImage(""),
}}
style={{ display: "none" }}
/>
<Spin spinning={loading}>
{/* 七项 Tab前六项复用资质备案初审详情组件qual-filing/detail 数据),第 7 项为变更记录 */}
<FilingTabs detail={detail || {}} extraTabs={extraTabs} />
</Spin>
</PageLayout>
);
}

View File

@ -1,14 +1,13 @@
import { Button, Form, Switch, Table, Tag } from "antd";
import { Button, Form, Table, Tag } from "antd";
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Get } from "@cqsjjb/jjb-common-lib/http";
import { useEffect, useState } from "react";
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
import {
REGISTERED_ORG_FILING_RECORD_STATUS_OPTIONS,
REGISTERED_ORG_FILING_TYPE_SEARCH_OPTIONS,
} from "~/enumerate/enterpriseOptions";
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP } from "~/enumerate/enterpriseOptions";
import { QUAL_FILING_STATUS_OPTIONS } from "~/enumerate/qualFilingOptions";
import { NS_ORG_INFO } from "~/enumerate/namespace";
/** 约定式路由首字母小写registeredOrg/detail */
@ -19,13 +18,21 @@ const ORG_MOCK_STORAGE_KEY = "registeredOrgMockEnabled";
function getOrgMockEnabled() {
try {
// URL 参数优先(?mock=1/0并持久化到 localStorage与资质备案模块开关行为一致。变更时间2026-08-10
const qs = new URLSearchParams(window.location.search);
if (qs.has("mock")) {
const v = qs.get("mock") === "1";
setOrgMockEnabled(v);
return v;
}
const stored = localStorage.getItem(ORG_MOCK_STORAGE_KEY);
if (stored === "1") return true;
if (stored === "0") return false;
} catch {
// ignore
}
return true;
// 2026-08-10 按用户要求默认真实接口mock 代码/数据保留,?mock=1 可临时开启
return false;
}
function setOrgMockEnabled(enabled) {
@ -36,49 +43,49 @@ function setOrgMockEnabled(enabled) {
}
}
/** 机构备案管理 mock 数据(原型 mod-institution-filing覆盖 已提交/待补正/待备案 等状态) */
/**
* 机构备案管理列表 mock与后端 GET /safetyEval/qual-filing-change/page QualFilingChangeCO 同构
* 字段filingUnitName/creditCode/filingUnitTypeName/businessScope/legalRepresentative/filingStatusCode/filingStatusName
* 变更时间2026-08-10列表查询切换为 /qual-filing-change/pagemock 同步对齐
*/
const ORG_MOCK_LIST = [
{ id: 1, unitName: "重庆安评技术研究院有限公司", creditCode: "915001072028699512", isLocalUnit: 1, businessScopeName: "煤炭开采业;金属、非金属矿及其他矿采选业;石油加工业,化学原料、化学品及医药制造业;金属冶炼", legalRepresentative: "郑远平", filingTypeCode: "1", filingTypeName: "审核备案", filingRecordStatusCode: 1, filingRecordStatusName: "已提交" },
{ id: 2, unitName: "重庆恒安安全评价有限公司", creditCode: "91500103MA60H002", isLocalUnit: 1, businessScopeName: "石油加工业,化学原料、化学品及医药制造业;金属冶炼", legalRepresentative: "刘强", filingTypeCode: "1", filingTypeName: "审核备案", filingRecordStatusCode: 2, filingRecordStatusName: "待补正" },
{ id: 3, unitName: "北京中安评价中心(重庆分公司)", creditCode: "91110108MA01XXXXX", isLocalUnit: 0, businessScopeName: "金属、非金属矿及其他矿采选业", legalRepresentative: "周海峰", filingTypeCode: "2", filingTypeName: "确认备案", filingRecordStatusCode: 3, filingRecordStatusName: "待备案" },
{ id: 4, unitName: "重庆渝安风险评估中心", creditCode: "91500107MA5K1111", isLocalUnit: 1, businessScopeName: "化工、建筑施工", legalRepresentative: "陈明", filingTypeCode: "1", filingTypeName: "审核备案", filingRecordStatusCode: 1, filingRecordStatusName: "已提交" },
{ id: 5, unitName: "四川天府安全评价有限公司(重庆分公司)", creditCode: "91510100MA61XXXXX", isLocalUnit: 0, businessScopeName: "烟花爆竹制造业;煤炭开采业", legalRepresentative: "李伟", filingTypeCode: "2", filingTypeName: "确认备案", filingRecordStatusCode: 3, filingRecordStatusName: "待备案" },
{ id: 6, unitName: "重庆两江安全技术服务有限公司", creditCode: "91500000MA5UYYYYY", isLocalUnit: 1, businessScopeName: "金属冶炼;陆上油气管道运输业", legalRepresentative: "王静", filingTypeCode: "1", filingTypeName: "审核备案", filingRecordStatusCode: 2, filingRecordStatusName: "待补正" },
{ id: 7, unitName: "云南康安评价中心", creditCode: "91530000MA6PZZZZZ", isLocalUnit: 0, businessScopeName: "煤炭开采业", legalRepresentative: "赵东升", filingTypeCode: "2", filingTypeName: "确认备案", filingRecordStatusCode: 3, filingRecordStatusName: "待备案" },
{ id: 8, unitName: "重庆科工安全评价中心", creditCode: "91500112MA5WAAAAA", isLocalUnit: 1, businessScopeName: "陆地石油和天然气开采业", legalRepresentative: "陈海林", filingTypeCode: "1", filingTypeName: "审核备案", filingRecordStatusCode: 1, filingRecordStatusName: "已提交" },
{ id: 9, unitName: "贵州黔安风险评估有限公司(重庆分公司)", creditCode: "91520100MA6HBBBBB", isLocalUnit: 0, businessScopeName: "石油加工业,化学原料、化学品及医药制造业", legalRepresentative: "张建国", filingTypeCode: "2", filingTypeName: "确认备案", filingRecordStatusCode: 2, filingRecordStatusName: "待补正" },
{ id: 10, unitName: "重庆辰安检测评价有限公司", creditCode: "91500105MA5UCCCCC", isLocalUnit: 1, businessScopeName: "烟花爆竹制造业;煤炭开采业", legalRepresentative: "王丽萍", filingTypeCode: "1", filingTypeName: "审核备案", filingRecordStatusCode: 1, filingRecordStatusName: "已提交" },
{ id: 11, unitName: "湖南湖湘安全评价中心(重庆分公司)", creditCode: "91430100MA4LDDDDD", isLocalUnit: 0, businessScopeName: "陆上油气管道运输业;金属、非金属矿及其他矿采选业", legalRepresentative: "李明华", filingTypeCode: "2", filingTypeName: "确认备案", filingRecordStatusCode: 3, filingRecordStatusName: "待备案" },
{ id: 12, unitName: "重庆巴渝安全评价有限公司", creditCode: "91500109MA5WEEEEE", isLocalUnit: 1, businessScopeName: "石油加工业,化学原料、化学品及医药制造业;金属、非金属矿及其他矿采选业", legalRepresentative: "刘海", filingTypeCode: "1", filingTypeName: "审核备案", filingRecordStatusCode: 1, filingRecordStatusName: "已提交" },
{ id: 1, filingId: 1, filingUnitName: "重庆安评技术研究院有限公司", creditCode: "915001072028699512", filingUnitTypeName: "本地单位", businessScope: ["COAL_MINING", "METAL_NONMETAL_MINING", "PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL", "METAL_SMELTING"], legalRepresentative: "郑远平", filingStatusCode: 1, filingStatusName: "已备案" },
{ id: 2, filingId: 2, filingUnitName: "重庆恒安安全评价有限公司", creditCode: "91500103MA60H002", filingUnitTypeName: "本地单位", businessScope: ["PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL", "METAL_SMELTING"], legalRepresentative: "刘强", filingStatusCode: 2, filingStatusName: "审核中" },
{ id: 3, filingId: 3, filingUnitName: "北京中安评价中心(重庆分公司)", creditCode: "91110108MA01XXXXX", filingUnitTypeName: "异地单位", businessScope: ["METAL_NONMETAL_MINING"], legalRepresentative: "周海峰", filingStatusCode: 3, filingStatusName: "已打回" },
{ id: 4, filingId: 4, filingUnitName: "重庆渝安风险评估中心", creditCode: "91500107MA5K1111", filingUnitTypeName: "本地单位", businessScope: ["PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL"], legalRepresentative: "陈明", filingStatusCode: 4, filingStatusName: "变更审核中" },
{ id: 5, filingId: 5, filingUnitName: "四川天府安全评价有限公司(重庆分公司)", creditCode: "91510100MA61XXXXX", filingUnitTypeName: "异地单位", businessScope: ["FIREWORKS_FIRECRACKERS", "COAL_MINING"], legalRepresentative: "李伟", filingStatusCode: 1, filingStatusName: "已备案" },
{ id: 6, filingId: 6, filingUnitName: "重庆两江安全技术服务有限公司", creditCode: "91500000MA5UYYYYY", filingUnitTypeName: "本地单位", businessScope: ["METAL_SMELTING", "ONSHORE_OIL_GAS_PIPELINE"], legalRepresentative: "王静", filingStatusCode: 5, filingStatusName: "暂存" },
{ id: 7, filingId: 7, filingUnitName: "云南康安评价中心", creditCode: "91530000MA6PZZZZZ", filingUnitTypeName: "异地单位", businessScope: ["COAL_MINING"], legalRepresentative: "赵东升", filingStatusCode: 2, filingStatusName: "审核中" },
{ id: 8, filingId: 8, filingUnitName: "重庆科工安全评价中心", creditCode: "91500112MA5WAAAAA", filingUnitTypeName: "本地单位", businessScope: ["ONSHORE_OIL_GAS"], legalRepresentative: "陈海林", filingStatusCode: 1, filingStatusName: "已备案" },
{ id: 9, filingId: 9, filingUnitName: "贵州黔安风险评估有限公司(重庆分公司)", creditCode: "91520100MA6HBBBBB", filingUnitTypeName: "异地单位", businessScope: ["PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL"], legalRepresentative: "张建国", filingStatusCode: 4, filingStatusName: "变更审核中" },
{ id: 10, filingId: 10, filingUnitName: "重庆辰安检测评价有限公司", creditCode: "91500105MA5UCCCCC", filingUnitTypeName: "本地单位", businessScope: ["FIREWORKS_FIRECRACKERS", "COAL_MINING"], legalRepresentative: "王丽萍", filingStatusCode: 1, filingStatusName: "已备案" },
{ id: 11, filingId: 11, filingUnitName: "湖南湖湘安全评价中心(重庆分公司)", creditCode: "91430100MA4LDDDDD", filingUnitTypeName: "异地单位", businessScope: ["ONSHORE_OIL_GAS_PIPELINE", "METAL_NONMETAL_MINING"], legalRepresentative: "李明华", filingStatusCode: 3, filingStatusName: "已打回" },
{ id: 12, filingId: 12, filingUnitName: "重庆巴渝安全评价有限公司", creditCode: "91500109MA5WEEEEE", filingUnitTypeName: "本地单位", businessScope: ["PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL", "METAL_NONMETAL_MINING"], legalRepresentative: "刘海", filingStatusCode: 1, filingStatusName: "已备案" },
];
/** mock 查询:单位名称/信用代码/备案类型/状态 过滤 + 分页,返回 { success, data, totalCount } */
/** mock 查询:单位名称/状态 过滤 + 分页,返回 { success, data, total }(与接口解包一致) */
function mockQueryRegisteredOrgPage(params = {}) {
const keywordOf = (v) => String(v || "").trim().toLowerCase();
const unitName = keywordOf(params.unitName);
const creditCode = keywordOf(params.creditCode);
const keyword = String(params.filingUnitName || "").trim().toLowerCase();
let rows = ORG_MOCK_LIST.filter((item) => {
if (unitName && !item.unitName.toLowerCase().includes(unitName)) return false;
if (creditCode && !item.creditCode.toLowerCase().includes(creditCode)) return false;
if (params.filingTypeCode && item.filingTypeCode !== String(params.filingTypeCode)) return false;
if (params.filingRecordStatusCode !== undefined && params.filingRecordStatusCode !== null && params.filingRecordStatusCode !== "" && Number(item.filingRecordStatusCode) !== Number(params.filingRecordStatusCode)) return false;
if (keyword && !item.filingUnitName.toLowerCase().includes(keyword)) return false;
if (params.filingStatusCode !== undefined && params.filingStatusCode !== null && params.filingStatusCode !== "" && Number(item.filingStatusCode) !== Number(params.filingStatusCode)) return false;
return true;
});
const current = Number(params.current ?? params.pageIndex ?? 1);
const size = Number(params.size ?? params.pageSize ?? 10);
const current = Number(params.current ?? 1);
const size = Number(params.size ?? 10);
const start = (current - 1) * size;
return {
success: true,
data: rows.slice(start, start + size),
totalCount: rows.length,
total: rows.length,
};
}
/** 提交状态配色(原型 tag-success/tag-warning/tag-info */
function getStatusColor(name, code) {
if (/已提交|已备案|通过/.test(name || "")) return "success";
if (/待补正|打回|退回/.test(name || "")) return "warning";
if (/待备案|备案中/.test(name || "")) return code === 3 ? "warning" : "processing";
/** 提交状态配色(对齐原型 tag-success/warning/danger/info */
function getStatusColor(name) {
if (/已备案|通过/.test(name || "")) return "success";
if (/打回|退回/.test(name || "")) return "error";
if (/审核中|变更审核中/.test(name || "")) return "processing";
return "default";
}
@ -89,8 +96,13 @@ function RegisteredOrgListPage(props) {
const [loading, setLoading] = useState(false);
const [pageIndex, setPageIndex] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [mockEnabled, setMockEnabled] = useState(getOrgMockEnabled());
// mock 默认关闭(真实接口);?mock=1 可临时开启演示。变更时间2026-08-10
const mockEnabled = getOrgMockEnabled();
/**
* 列表查询mock 模式走本地数据真实模式调用 GET /safetyEval/qual-filing-change/page
* 返回 QualFilingChangeCO字段与 mock 同构变更时间2026-08-10
*/
const fetchData = async (page = 1, size = 10, useMock = mockEnabled) => {
setLoading(true);
try {
@ -98,13 +110,17 @@ function RegisteredOrgListPage(props) {
if (useMock) {
const res = mockQueryRegisteredOrgPage({ ...formData, current: page, size });
setDataSource(res.data || []);
setTotal(res.totalCount || 0);
setTotal(res.total || 0);
return;
}
const res = await props.registeredOrgList({ ...formData, current: page, size });
const res = await Get(
"/safetyEval/qual-filing-change/page",
{ ...formData, current: page, size },
{ token: sessionStorage.getItem("token") },
);
if (res?.success !== false) {
setDataSource(res?.data || []);
setTotal(res?.totalCount || 0);
setTotal(res?.total || 0);
}
} catch {
setDataSource([]);
@ -118,17 +134,11 @@ function RegisteredOrgListPage(props) {
fetchData();
}, []);
const handleToggleMock = (v) => {
setOrgMockEnabled(v);
setMockEnabled(v);
setPageIndex(1);
fetchData(1, pageSize, v);
};
const goDetail = (id) => {
if (!id) {
return;
}
const goDetail = (record) => {
// 详情使用变更接口 GET /safetyEval/qual-filing-change/detail参数为变更记录 idrecord.id
// 变更时间2026-08-10用户要求“查看基础信息”使用带 change 的接口)
const id = record.id;
if (!id) return;
const query = `${DETAIL_PATH}?id=${encodeURIComponent(id)}`;
if (props.history?.push) {
props.history.push(query);
@ -144,17 +154,16 @@ function RegisteredOrgListPage(props) {
loading={loading}
defaultExpand
formLine={[
<Form.Item key="unitName" name="unitName">
<Form.Item key="filingUnitName" name="filingUnitName">
<ControlWrapper.Input label="单位名称" allowClear placeholder="关键字搜索" />
</Form.Item>,
<Form.Item key="creditCode" name="creditCode">
<ControlWrapper.Input label="统一社会信用代码" allowClear placeholder="关键字搜索" />
</Form.Item>,
<Form.Item key="filingTypeCode" name="filingTypeCode">
<ControlWrapper.Select label="备案类型" placeholder="请选择" allowClear options={REGISTERED_ORG_FILING_TYPE_SEARCH_OPTIONS} />
</Form.Item>,
<Form.Item key="filingRecordStatusCode" name="filingRecordStatusCode">
<ControlWrapper.Select label="状态" placeholder="请选择" allowClear options={REGISTERED_ORG_FILING_RECORD_STATUS_OPTIONS} />
<Form.Item key="filingStatusCode" name="filingStatusCode">
<ControlWrapper.Select
label="状态"
placeholder="请选择"
allowClear
options={QUAL_FILING_STATUS_OPTIONS.filter((item) => item.value !== "")}
/>
</Form.Item>,
]}
onFinish={() => {
@ -172,11 +181,6 @@ function RegisteredOrgListPage(props) {
<span style={{ fontSize: "0.85rem", color: "#64748b" }}>
<strong style={{ color: "#2563eb" }}>{total || 0}</strong>
</span>
<Tag color={mockEnabled ? "warning" : "default"}>
{mockEnabled ? "Mock 演示中" : "真实接口"}
</Tag>
<span style={{ fontSize: "0.78rem", color: "#64748b" }}>Mock 数据</span>
<Switch size="small" checked={mockEnabled} onChange={handleToggleMock} />
</div>
<Table
@ -199,20 +203,31 @@ function RegisteredOrgListPage(props) {
}}
columns={[
{ title: "序号", width: 60, render: (_, __, index) => index + 1 },
{ title: "单位名称", dataIndex: "unitName", ellipsis: true },
{ title: "单位名称", dataIndex: "filingUnitName", ellipsis: true },
{ title: "统一社会信用代码", dataIndex: "creditCode", width: 170, ellipsis: true },
{
title: "是否本地单位",
width: 100,
// 由 filingUnitTypeName本地单位/异地单位判断变更时间2026-08-10
render: (_, record) =>
record.isLocalUnitName || (record.isLocalUnit === 1 ? "是" : record.isLocalUnit === 0 ? "否" : "-"),
record.filingUnitTypeName === "本地单位"
? "是"
: record.filingUnitTypeName === "异地单位"
? "否"
: "-",
},
{
title: "拟申请/具备法定安全评价业务范围",
dataIndex: "businessScopeName",
dataIndex: "businessScope",
ellipsis: true,
render: (v, record) =>
v || record.safetyIndustryCategoryName || (Array.isArray(record.businessScope) ? record.businessScope.join("") : "-"),
render: (_, record) => {
if (Array.isArray(record.businessScope)) {
return record.businessScope
.map((item) => QUALIFICATION_INDUSTRY_OPTIONS_MAP[item] || item)
.join("");
}
return record.businessScope || "-";
},
},
{
title: "法定代表人",
@ -221,10 +236,10 @@ function RegisteredOrgListPage(props) {
},
{
title: "提交状态",
width: 100,
width: 110,
render: (_, record) => (
<Tag color={getStatusColor(record.filingRecordStatusName, record.filingRecordStatusCode)}>
{record.filingRecordStatusName || "-"}
<Tag color={getStatusColor(record.filingStatusName)}>
{record.filingStatusName || "-"}
</Tag>
),
},
@ -233,7 +248,7 @@ function RegisteredOrgListPage(props) {
width: 130,
fixed: "right",
render: (_, record) => (
<Button type="link" onClick={() => goDetail(record.id)}>
<Button type="link" onClick={() => goDetail(record)}>
查看基础信息
</Button>
),