tangjie 2026-07-02 16:58:07 +08:00
parent ba0a5e8610
commit eb144fef1c
15 changed files with 219 additions and 277 deletions

View File

@ -101,30 +101,14 @@ export function fromFilingBasicForm(values = {}) {
export function toFilingDetail(data = {}) { export function toFilingDetail(data = {}) {
return { return {
...toFilingBasicForm(data), ...toFilingBasicForm(data),
materials: (data.materials || []).map(toFilingMaterialRow), materials: (data.materials || []),
commitment: data.commitment ? toFilingCommitmentForm(data.commitment, data.filingUnitName) : null, commitment: data.commitment ? toFilingCommitmentForm(data.commitment, data.filingUnitName) : null,
personnelList: (data.personnelList || []).map(toFilingPersonnelRow), personnelList: (data.personnelList || []).map(toFilingPersonnelRow),
equipmentList: (data.equipmentList || []).map(toFilingEquipmentRow), equipmentList: (data.equipmentList || []).map(toFilingEquipmentRow),
}; };
} }
// ─── 字段映射:材料 ───
export function toFilingMaterialRow(data = {}) {
return {
id: asId(data.id),
filingId: asId(data.filingId),
sortOrder: data.sortOrder,
materialContent: data.materialContent,
materialFormat: data.materialFormat,
requiredFlag: data.requiredFlag,
uploadStatusCode: data.uploadStatusCode,
uploadStatusName: data.uploadStatusName,
attachmentDesc: data.attachmentDesc,
attachmentUrl: data.attachmentUrl,
materialRemark: data.materialRemark,
};
}
// ─── 字段映射:承诺书 ─── // ─── 字段映射:承诺书 ───

View File

@ -8,6 +8,12 @@ export const queryReviewList = declareRequest(
export const queryComplianceCheck = declareRequest( export const queryComplianceCheck = declareRequest(
"qualReviewLoading", "qualReviewLoading",
"Get > /safety-eval/qual-filing/compliance-check > id", "Get > /safetyEval/qual-filing/compliance-check",
'complianceCheckItems: [] | res.data || []', 'complianceCheckItems: [] | res.data || []',
);
export const fetchQualFilingDetail = declareRequest(
"fetchQualFilingDetailLoading",
"Get > /safetyEval/qual-filing/detail",
'qualFilingDetail: {} | res.data || {}',
); );

View File

@ -0,0 +1,39 @@
import { useState } from "react";
import { Button, Image } from "antd";
const isImage = (url) =>
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
export default function PreviewUrlButton({ url, children = "预览", ...btnProps }) {
const [previewImage, setPreviewImage] = useState("");
return (
<>
<Button
type="link"
size="small"
disabled={!url}
onClick={() => {
if (isImage(url)) {
setPreviewImage(url);
} else {
window.open(url);
}
}}
{...btnProps}
>
{children}
</Button>
{previewImage && (
<Image
src={previewImage}
style={{ display: "none" }}
preview={{
visible: true,
onVisibleChange: (v) => { if (!v) setPreviewImage(""); },
}}
/>
)}
</>
);
}

View File

@ -18,7 +18,7 @@ export const VERIFY_STATUS_MAP = {
/** 审核状态 */ /** 审核状态 */
export const REVIEW_STATUS_MAP = { export const REVIEW_STATUS_MAP = {
pending: { label: "待审核", color: "warning" },
2: { label: "审核中", color: "processing" }, 2: { label: "审核中", color: "processing" },
3: { label: "打回", color: "error" }, 3: { label: "打回", color: "error" },
}; };
@ -50,3 +50,8 @@ export const EXPERT_LEVEL_MAP = {
2: "二级", 2: "二级",
3: "三级", 3: "三级",
}; };
export const LEGAL_STATUS_STYLE = {
PASS: { icon: "✅", borderColor: "#059669", bg: "#f0fdf4" },
WARN: { icon: "⚠️", borderColor: "#d97706", bg: "#fffbeb" },
NOT_PASS: { icon: "❌", borderColor: "#dc2626", bg: "#fef2f2" },
};

View File

@ -34,11 +34,26 @@ export const CHANGE_STATUS_OPTIONS = [
]; ];
export const QUAL_FILING_STATUS_COLOR = { export const QUAL_FILING_STATUS_COLOR = {
5: "cyan", 5: {
1: "success", color: "cyan",
2: "processing", label: "暂存",
3: "error", },
4: "warning", 1: {
color: "success",
label: "已备案",
},
2: {
color: "processing",
label: "审核中",
},
3: {
color: "error",
label: "已打回",
},
4: {
color: "warning",
label: "变更审核中",
},
}; };
export function getFilingStatusOptions(mode) { export function getFilingStatusOptions(mode) {

View File

@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from "react";
import FilePreviewModal, { FilePreviewContent } from "~/components/FilePreviewModal"; import FilePreviewModal, { FilePreviewContent } from "~/components/FilePreviewModal";
import { resolvePreviewSrc } from "~/components/CertPreviewImg"; import { resolvePreviewSrc } from "~/components/CertPreviewImg";
import { fetchStaffCertDetail, fetchStaffCertListByPersonnelId } from "~/api/staffCertificate"; import { fetchStaffCertDetail, fetchStaffCertListByPersonnelId } from "~/api/staffCertificate";
import { fetchOrgPersonnelDetail } from "~/api/qualFiling/personnelHelper";
import { safeRequest } from "~/utils"; import { safeRequest } from "~/utils";
const GENDER_MAP = { 1: "男", 2: "女" }; const GENDER_MAP = { 1: "男", 2: "女" };
@ -11,7 +12,7 @@ const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
export default function StaffViewModal({ export default function StaffViewModal({
open, open,
currentId, currentId,
requestDetails, requestDetails = fetchOrgPersonnelDetail,
requestCertList = fetchStaffCertListByPersonnelId, requestCertList = fetchStaffCertListByPersonnelId,
requestCertDetails = fetchStaffCertDetail, requestCertDetails = fetchStaffCertDetail,
onCancel, onCancel,

View File

@ -57,7 +57,7 @@ export default function EquipmentStep({
!disabled && ( !disabled && (
<Upload <Upload
showUploadList={false} showUploadList={false}
action={`${window.process.env.app.API_HOST}/safety-eval/file/upload`} action={`${window.process.env.app.API_HOST}/safetyEval/file/upload`}
onChange={(info) => { onChange={(info) => {
if (info.file.status === "done") { if (info.file.status === "done") {
const data = info.file.response?.data; const data = info.file.response?.data;

View File

@ -1,7 +1,7 @@
import { Button, Modal, Space, Table } from "antd"; import { Button, Modal, Space, Table } from "antd";
import { useState } from "react"; import { useState } from "react";
import StaffViewModal from "~/pages/Container/EnterpriseInfo/PersonnelInfo/StaffViewModal"; import StaffViewModal from "~/pages/Container/EnterpriseInfo/PersonnelInfo/StaffViewModal";
import { fetchOrgPersonnelDetail } from "~/api/qualFiling/personnelHelper";
import OrgPersonnelSelectModal from "./OrgPersonnelSelectModal"; import OrgPersonnelSelectModal from "./OrgPersonnelSelectModal";
export default function PersonnelStep({ export default function PersonnelStep({
@ -71,7 +71,7 @@ export default function PersonnelStep({
<StaffViewModal <StaffViewModal
open={!!viewId} open={!!viewId}
currentId={viewId} currentId={viewId}
requestDetails={fetchOrgPersonnelDetail}
onCancel={() => setViewId("")} onCancel={() => setViewId("")}
/> />
</> </>

View File

@ -75,11 +75,16 @@ export default function FilingListTable({
title: "备案状态", title: "备案状态",
dataIndex: "filingStatusName", dataIndex: "filingStatusName",
width: 110, width: 110,
render: (_, record) => ( render: (_, record) => {
<Tag color={QUAL_FILING_STATUS_COLOR[record.filingStatusCode] || "default"}> const obj= QUAL_FILING_STATUS_COLOR[record.filingStatusCode]
{record.filingStatusName || "-"} const color = obj?.color || "default";
</Tag> const name= obj?.label || "-";
), return (
obj?<Tag color={color}>
{name || "-"}
</Tag>:"--"
);
},
}); });
columns.push({ columns.push({

View File

@ -1,16 +1,16 @@
/** 备案材料通用模板(对齐后端 QualFilingMaterialTemplateProvider / 原型 10 项) */ /** 备案材料通用模板(对齐后端 QualFilingMaterialTemplateProvider / 原型 10 项) */
export const FILING_MATERIAL_TEMPLATE = [ export const FILING_MATERIAL_TEMPLATE = [
{id: 1, materialType: 1, materialContent: "申请材料目录(原件)", materialFormat: "pdf", attachmentDesc: "纸质版扫描件", materialRemark: null, requiredFlag: 1 }, {id: 1, materialType: 1, materialContent: "申请材料目录(原件)", materialFormat: null, attachmentDesc: "纸质版扫描件", materialRemark: null, requiredFlag: 1 },
{id: 2, materialType: 2, materialContent: "申请书(原件)", materialFormat: "pdf", attachmentDesc: "纸质版扫描件", materialRemark: "法定代表人亲笔签名并加盖单位公章", requiredFlag: 1 }, {id: 2, materialType: 2, materialContent: "申请书(原件)", materialFormat: null, attachmentDesc: "纸质版扫描件", materialRemark: "法定代表人亲笔签名并加盖单位公章", requiredFlag: 1 },
{id: 3, materialType: 3, materialContent: "法人证明(复印件)", materialFormat: "pdf", attachmentDesc: "纸质版扫描件", materialRemark: "营业执照或事业单位法人证书等", requiredFlag: 1 }, {id: 3, materialType: 3, materialContent: "法人证明(复印件)", materialFormat: null, attachmentDesc: "纸质版扫描件", materialRemark: "营业执照或事业单位法人证书等", requiredFlag: 1 },
{id: 4, materialType: 4, materialContent: "三年内无重大违法失信记录查询证明(原件)", materialFormat: "pdf", attachmentDesc: "纸质版扫描件", materialRemark: "法定代表人亲笔签名并加盖单位公章", requiredFlag: 1 }, {id: 4, materialType: 4, materialContent: "三年内无重大违法失信记录查询证明(原件)", materialFormat: null, attachmentDesc: "纸质版扫描件", materialRemark: "法定代表人亲笔签名并加盖单位公章", requiredFlag: 1 },
{id: 5, materialType: 5, materialContent: "法定代表人承诺书(原件)", materialFormat: "pdf", attachmentDesc: "纸质版扫描件", materialRemark: "法定代表人亲笔签名", requiredFlag: 1 }, {id: 5, materialType: 5, materialContent: "法定代表人承诺书(原件)", materialFormat: null, attachmentDesc: "纸质版扫描件", materialRemark: "法定代表人亲笔签名", requiredFlag: 1 },
{id: 6, materialType: 6, materialContent: "固定资产法定证明材料或书面承诺", materialFormat: "pdf", attachmentDesc: "纸质版扫描件", materialRemark: null, requiredFlag: 1 }, {id: 6, materialType: 6, materialContent: "固定资产法定证明材料或书面承诺", materialFormat: null, attachmentDesc: "纸质版扫描件", materialRemark: null, requiredFlag: 1 },
{id: 7, materialType: 7, materialContent: "工作场所及档案室面积证明资料(复印件)", materialFormat: "pdf", attachmentDesc: "纸质版扫描件", materialRemark: "房产证、租赁协议等", requiredFlag: 1 }, {id: 7, materialType: 7, materialContent: "工作场所及档案室面积证明资料(复印件)", materialFormat: null, attachmentDesc: "纸质版扫描件", materialRemark: "房产证、租赁协议等", requiredFlag: 1 },
{id: 8, materialType: 8, materialContent: "安全评价师专业能力证明(彩色复印件)", materialFormat: "pdf", attachmentDesc: "纸质版扫描件", materialRemark: "学历证书、职称证及其他相关证明材料", requiredFlag: 1 }, {id: 8, materialType: 8, materialContent: "安全评价师专业能力证明(彩色复印件)", materialFormat: null, attachmentDesc: "纸质版扫描件", materialRemark: "学历证书、职称证及其他相关证明材料", requiredFlag: 1 },
{id: 9, materialType: 9, materialContent: "相关负责人证明材料(复印件)", materialFormat: "pdf", attachmentDesc: "纸质版扫描件", materialRemark: "任命文件、简历、职称证等", requiredFlag: 1 }, {id: 9, materialType: 9, materialContent: "相关负责人证明材料(复印件)", materialFormat: null, attachmentDesc: "纸质版扫描件", materialRemark: "任命文件、简历、职称证等", requiredFlag: 1 },
{id: 10, materialType: 10, materialContent: "机构内部管理制度(非受控版)", materialFormat: "pdf", attachmentDesc: "纸质版扫描件", materialRemark: null, requiredFlag: 1 }, {id: 10, materialType: 10, materialContent: "机构内部管理制度(非受控版)", materialFormat: null, attachmentDesc: "纸质版扫描件", materialRemark: null, requiredFlag: 1 },
]; ];
export function createLocalMaterials() { export function createLocalMaterials() {

View File

@ -1,20 +1,29 @@
import React, { useMemo } from "react"; import React, { useEffect } from "react";
import { Tag, Space } from "antd"; import { Tag, Space, Spin } from "antd";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout"; import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import { REVIEW_STATUS_MAP } from "~/enumerate/constant"; import { REVIEW_STATUS_MAP } from "~/enumerate/constant";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_QUAL_REVIEW } from "~/enumerate/namespace";
import { tools } from "@cqsjjb/jjb-common-lib";
import { MOCK_DETAIL } from "../mockData"; import { MOCK_DETAIL } from "../mockData";
import FilingTabs from "../FilingTabs"; import FilingTabs from "../FilingTabs";
function parseQueryId(location) { const {router}=tools;
const search = location?.search || window.location.search || "";
return new URLSearchParams(search).get("id");
}
const FilingDetail = (props) => { const FilingDetail = (props) => {
const id = useMemo(() => parseQueryId(props.location), [props.location?.search]); const { qualReview, fetchQualFilingDetail } = props;
const { fetchQualFilingDetailLoading, qualFilingDetail } = qualReview;
const detail = MOCK_DETAIL; const detail = MOCK_DETAIL;
const statusConfig = REVIEW_STATUS_MAP[detail.reviewStatus]; const statusConfig = REVIEW_STATUS_MAP[detail.reviewStatus];
useEffect(() => {
fetchQualFilingDetail({
id: router.query.id,
});
}, []);
return ( return (
<PageLayout <PageLayout
title={ title={
@ -26,9 +35,15 @@ const FilingDetail = (props) => {
history={props.history} history={props.history}
previous previous
> >
<FilingTabs detail={detail} /> <Spin spinning={fetchQualFilingDetailLoading}>
<FilingTabs detail={qualFilingDetail} />
</Spin>
</PageLayout> </PageLayout>
); );
}; };
export default FilingDetail;
export default Connect(
[NS_QUAL_REVIEW],
true,
)(FilingDetail);

View File

@ -1,12 +1,12 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import { Button, Descriptions, Table, Tag, Tabs, Select, Modal, Space } from "antd"; import { Button, Descriptions, Table, Tag, Tabs, Select, Modal, Space, Image } from "antd";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_QUAL_REVIEW } from "~/enumerate/namespace";
import StaffViewModal from "~/pages/Container/EnterpriseInfo/PersonnelInfo/StaffViewModal";
import PreviewUrlButton from "~/components/PreviewUrlButton/index";
import { import {
MOCK_MATERIALS,
MOCK_PERSONNEL,
MOCK_EQUIPMENT,
MOCK_EQUIP_DETAIL, MOCK_EQUIP_DETAIL,
MOCK_PERSONNEL_DETAIL,
MOCK_CERTIFICATES,
CALIBRATION_MAP, CALIBRATION_MAP,
} from "../mockData"; } from "../mockData";
@ -28,21 +28,24 @@ const FilingTabs = ({
onPersonnelView, onPersonnelView,
onEquipView, onEquipView,
}) => { }) => {
const [personnelVisible, setPersonnelVisible] = useState(false);
const [equipVisible, setEquipVisible] = useState(false); const [equipVisible, setEquipVisible] = useState(false);
const [selectedEquip, setSelectedEquip] = useState(null); const [selectedEquip, setSelectedEquip] = useState(null);
const [viewId, setViewId] = useState("");
const baseCols = [ const baseCols = [
{ title: "序号", width: 60, render: (_, __, i) => i + 1 }, { title: "序号", width: 60, render: (_, __, i) => i + 1 },
{ title: "材料名称", dataIndex: "name" }, { title: "材料名称", dataIndex: "materialContent" },
]; ];
const materialCols = isReview const materialCols = isReview
? [ ? [
...baseCols, ...baseCols,
{ title: "上传附件", dataIndex: "attachment", width: 120 }, { title: "上传附件", dataIndex: "attachmentDesc", width: 120 },
{ title: "格式", dataIndex: "format", width: 80 }, { title: "格式", dataIndex: "materialFormat", width: 80 },
{ title: "状态", dataIndex: "status", width: 100, render: () => <Tag color="success">已上传</Tag> }, { title: "状态", width: 100, render: (_, record) => record.uploadStatusCode === 2 ? <Tag color="success">{record.uploadStatusName || "已上传"}</Tag> : <Tag color="warning">{record.uploadStatusName || ""}</Tag> },
{ title: "符合性", width: 100, { title: "符合性", width: 100,
render: (_, record) => ( render: (_, record) => (
<Select <Select
@ -60,34 +63,36 @@ const FilingTabs = ({
] ]
: [ : [
...baseCols, ...baseCols,
{ title: "格式", dataIndex: "format", width: 80 }, { title: "格式", dataIndex: "materialFormat", width: 80 },
{ title: "状态", dataIndex: "status", width: 100, render: () => <Tag color="success">已上传</Tag> }, { title: "状态", width: 100, render: (_, record) => record.uploadStatusCode === 2 ? <Tag color="success">{record.uploadStatusName || "已上传"}</Tag> : <Tag color="warning">{record.uploadStatusName || ""}</Tag> },
{ title: "操作", width: 80, render: () => <Button type="link" size="small">预览</Button> }, { title: "操作", width: 80, render: (_, record) => <PreviewUrlButton url={record.attachmentUrl}>预览</PreviewUrlButton> },
]; ];
const commitment= detail.commitment || {};
const items = [ const items = [
{ {
key: "info", key: "info",
label: "1.备案基本信息", label: "1.备案基本信息",
children: ( children: (
<Descriptions column={2} bordered size="small" style={{ background: "#fff" }}> <Descriptions column={2} bordered size="small" style={{ background: "#fff" }}>
<Descriptions.Item label="申请的业务范围" span={2}>{detail.scopeName}</Descriptions.Item> <Descriptions.Item label="申请的业务范围" span={2}>{detail.businessScope}</Descriptions.Item>
<Descriptions.Item label="备案属地">{detail.filingRegion}</Descriptions.Item> <Descriptions.Item label="备案属地">{detail.filingTerritoryName}</Descriptions.Item>
<Descriptions.Item label="备案单位">{detail.orgName}</Descriptions.Item> <Descriptions.Item label="备案单位">{detail.filingUnitName}</Descriptions.Item>
<Descriptions.Item label="备案单位类型">{detail.orgType}</Descriptions.Item> <Descriptions.Item label="备案单位类型">{detail.filingUnitTypeName}</Descriptions.Item>
<Descriptions.Item label="统一社会信用代码">{detail.creditCode}</Descriptions.Item> <Descriptions.Item label="统一社会信用代码">{detail.creditCode}</Descriptions.Item>
<Descriptions.Item label="办公地址" span={2}>{detail.officeAddress}</Descriptions.Item> <Descriptions.Item label="办公地址" span={2}>{detail.officeAddress}</Descriptions.Item>
<Descriptions.Item label="注册地址" span={2}>{detail.registerAddress}</Descriptions.Item> <Descriptions.Item label="注册地址" span={2}>{detail.registerAddress}</Descriptions.Item>
<Descriptions.Item label="资质证书编号">{detail.certNo}</Descriptions.Item> <Descriptions.Item label="资质证书编号">{detail.qualCertNo}</Descriptions.Item>
<Descriptions.Item label="信息公开网址">{detail.website}</Descriptions.Item> <Descriptions.Item label="信息公开网址">{detail.infoDisclosureUrl}</Descriptions.Item>
<Descriptions.Item label="法人代表及电话">{detail.legalPerson} / {detail.legalPhone}</Descriptions.Item> <Descriptions.Item label="法人代表及电话">{detail.legalPersonPhone}</Descriptions.Item>
<Descriptions.Item label="联系人及电话">{detail.contactPerson} / {detail.contactPhone}</Descriptions.Item> <Descriptions.Item label="联系人及电话">{detail.contactPhone}</Descriptions.Item>
<Descriptions.Item label="固定资产总值(万元)">{detail.fixedAssets}</Descriptions.Item> <Descriptions.Item label="固定资产总值(万元)">{detail.fixedAssetAmount}</Descriptions.Item>
<Descriptions.Item label="工作场所建筑面积">{detail.officeArea}</Descriptions.Item> <Descriptions.Item label="工作场所建筑面积">{detail.workplaceArea}</Descriptions.Item>
<Descriptions.Item label="档案室面积">{detail.archiveArea}</Descriptions.Item> <Descriptions.Item label="档案室面积">{detail.archiveRoomArea}</Descriptions.Item>
<Descriptions.Item label="专职安全评价师数量">{detail.evaluatorCount}</Descriptions.Item> <Descriptions.Item label="专职安全评价师数量">{detail.fulltimeEvaluatorCount}</Descriptions.Item>
<Descriptions.Item label="注册安全工程师数量">{detail.registerEngineerCount}</Descriptions.Item> <Descriptions.Item label="注册安全工程师数量">{detail.registeredEngineerCount}</Descriptions.Item>
<Descriptions.Item label="单位基本情况介绍" span={2}>{detail.description}</Descriptions.Item> <Descriptions.Item label="单位基本情况介绍" span={2}>{detail.unitIntro}</Descriptions.Item>
</Descriptions> </Descriptions>
), ),
}, },
@ -95,7 +100,7 @@ const FilingTabs = ({
key: "materials", key: "materials",
label: "2.备案材料上传", label: "2.备案材料上传",
children: ( children: (
<Table size="small" bordered rowKey="id" pagination={false} dataSource={MOCK_MATERIALS} columns={materialCols} /> <Table size="small" bordered rowKey="id" pagination={false} dataSource={detail?.materials || []} columns={materialCols} />
), ),
}, },
{ {
@ -104,13 +109,17 @@ const FilingTabs = ({
children: ( children: (
<div style={{ border: "1px solid #d9d9d9", borderRadius: 6, padding: "1.25rem 1.5rem", background: "#fafafa", fontSize: "0.85rem", lineHeight: 1.9 }}> <div style={{ border: "1px solid #d9d9d9", borderRadius: 6, 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 style={{ textAlign: "center", fontWeight: 600, fontSize: "0.95rem", marginBottom: "0.75rem" }}>申请单位法定代表人承诺书</p>
<p>本人{detail.legalPerson}{detail.orgName}法定代表人现代表我单位承诺如下</p> <p>本人{commitment.legalRepName}{detail.filingUnitName}法定代表人现代表我单位承诺如下</p>
<p>我单位自愿申请安全评价机构资质本人已经认真学习了解并掌握安全生产法行政许可法行政处罚法安全评价检测检验机构管理办法等法律法规的相关规定知悉开展安全评价工作的法律责任义务权力和风险</p> <p>我单位自愿申请安全评价机构资质本人已经认真学习了解并掌握安全生产法行政许可法行政处罚法安全评价检测检验机构管理办法等法律法规的相关规定知悉开展安全评价工作的法律责任义务权力和风险</p>
<p>本人承诺本单位满足安全评价检测检验机构管理办法所规定的资质条件要求本人及单位三年内无重大违法失信行为申请资质所提交的有关材料真实合法有效并对其真实性合法性承担相应法律责任接受并配合有关部门对本单位开展的专业能力审查</p> <p>本人承诺本单位满足安全评价检测检验机构管理办法所规定的资质条件要求本人及单位三年内无重大违法失信行为申请资质所提交的有关材料真实合法有效并对其真实性合法性承担相应法律责任接受并配合有关部门对本单位开展的专业能力审查</p>
<p>如能获准资质本单位将严格按照法律法规规章标准的要求开展安全评价活动遵守执业准则和职业道德并对作出的安全评价报告结果承担法律责任自觉接受应急管理部门煤矿安全监督管理部门等行政执法部门的监督检查</p> <p>如能获准资质本单位将严格按照法律法规规章标准的要求开展安全评价活动遵守执业准则和职业道德并对作出的安全评价报告结果承担法律责任自觉接受应急管理部门煤矿安全监督管理部门等行政执法部门的监督检查</p>
<p style={{ marginTop: "0.75rem" }}>法定代表人{detail.legalPerson}已签名</p> <p style={{ marginTop: "0.75rem" }}>法定代表人{commitment.legalRepName}已签名</p>
<p>单位盖章</p> <Space>
<p style={{ textAlign: "right" }}>2026年06月25日</p>
<Image src={commitment.legalRepSignatureUrl} style={{ width: 100, height: 100 }} />
<p>单位盖章</p>
</Space>
<p style={{ textAlign: "right" }}>{commitment.signDate}</p>
</div> </div>
), ),
}, },
@ -118,17 +127,16 @@ const FilingTabs = ({
key: "personnel", key: "personnel",
label: "4.备案人员管理", label: "4.备案人员管理",
children: ( children: (
<Table size="small" bordered rowKey="id" pagination={false} dataSource={MOCK_PERSONNEL} <Table size="small" bordered rowKey="id" pagination={false} dataSource={detail?.personnelList || []}
columns={[ columns={[
{ title: "序号", width: 60, render: (_, __, i) => i + 1 }, { title: "序号", width: 60, render: (_, __, i) => i + 1 },
{ title: "人员姓名", dataIndex: "name", width: 100 }, { title: "人员姓名", dataIndex: "personName", width: 100 },
{ title: "类型", dataIndex: "type", width: 100 }, { title: "类型", dataIndex: "personTypeName", width: 100 },
{ title: "职务", dataIndex: "position", width: 120 }, { title: "职务", dataIndex: "positionName", width: 120 },
{ title: "职称", dataIndex: "title", width: 100 }, { title: "职称", dataIndex: "titleName", width: 100 },
{ title: "专业能力", dataIndex: "ability" },
{ title: "操作", width: 80, { title: "操作", width: 80,
render: () => ( render: (_, record) => (
<Button type="link" size="small" onClick={onPersonnelView || (() => setPersonnelVisible(true))}> <Button type="link" size="small" onClick={() => setViewId(record.sourcePersonnelId)}>
查看 查看
</Button> </Button>
), ),
@ -141,23 +149,25 @@ const FilingTabs = ({
key: "equipment", key: "equipment",
label: "5.装备清单", label: "5.装备清单",
children: ( children: (
<Table size="small" bordered rowKey="id" pagination={false} dataSource={MOCK_EQUIPMENT} <Table size="small" bordered rowKey="id" pagination={false} dataSource={detail?.equipmentList || []}
columns={[ columns={[
{ title: "序号", width: 60, render: (_, __, i) => i + 1 }, { title: "序号", width: 60, render: (_, __, i) => i + 1 },
{ title: "装备名称", dataIndex: "name", width: 140 }, { title: "装备名称", dataIndex: "deviceName", width: 140 },
{ title: "规格型号", dataIndex: "model", width: 120 }, { title: "规格型号", dataIndex: "deviceModel", width: 120 },
{ title: "生产厂家", dataIndex: "manufacturer", width: 140 }, { title: "生产厂家", dataIndex: "manufacturer", width: 140 },
{ title: "计量检定情况", dataIndex: "calibration", width: 120, { title: "计量检定情况", width: 120,
render: (s) => { render: (_, record) => {
const cfg = CALIBRATION_MAP[s]; if (record.calibrationReportUrl) {
return cfg ? <Tag color={cfg.color}>{cfg.label}</Tag> : s; return <Tag color="success">已上传</Tag>;
}
return <Tag color="warning">待上传</Tag>;
}, },
}, },
{ title: "操作", width: 80, { title: "操作", width: 80,
render: (_, record) => ( render: (_, record) => (
<Button type="link" size="small" onClick={() => onEquipView ? onEquipView(record) : (setSelectedEquip(record), setEquipVisible(true))}> <PreviewUrlButton url={record.calibrationReportUrl}>
查看 查看
</Button> </PreviewUrlButton>
), ),
}, },
]} ]}
@ -171,54 +181,12 @@ const FilingTabs = ({
<Tabs items={items} /> <Tabs items={items} />
{/* 人员详细信息弹窗 */} {/* 人员详细信息弹窗 */}
<Modal <StaffViewModal
title="人员详细信息" open={!!viewId}
open={personnelVisible} currentId={viewId}
onCancel={() => setPersonnelVisible(false)}
width={720} onCancel={() => setViewId("")}
footer={(originNode, { CancelBtn }) => <CancelBtn />} />
>
<Descriptions column={2} bordered size="small">
<Descriptions.Item label="姓名">{MOCK_PERSONNEL_DETAIL.name}</Descriptions.Item>
<Descriptions.Item label="性别">{MOCK_PERSONNEL_DETAIL.gender}</Descriptions.Item>
<Descriptions.Item label="资质范围">{MOCK_PERSONNEL_DETAIL.qualScope}</Descriptions.Item>
<Descriptions.Item label="出生日期">{MOCK_PERSONNEL_DETAIL.birthDate}</Descriptions.Item>
<Descriptions.Item label="办公地址" span={2}>{MOCK_PERSONNEL_DETAIL.officeAddress}</Descriptions.Item>
<Descriptions.Item label="专业">{MOCK_PERSONNEL_DETAIL.major}</Descriptions.Item>
<Descriptions.Item label="职业等级及证书编号">{MOCK_PERSONNEL_DETAIL.certLevel}</Descriptions.Item>
<Descriptions.Item label="邮箱">{MOCK_PERSONNEL_DETAIL.email}</Descriptions.Item>
<Descriptions.Item label="人员类型">{MOCK_PERSONNEL_DETAIL.personnelType}</Descriptions.Item>
<Descriptions.Item label="身份证号">{MOCK_PERSONNEL_DETAIL.idCard}</Descriptions.Item>
<Descriptions.Item label="现住地址" span={2}>{MOCK_PERSONNEL_DETAIL.currentAddress}</Descriptions.Item>
<Descriptions.Item label="学历">{MOCK_PERSONNEL_DETAIL.education}</Descriptions.Item>
<Descriptions.Item label="毕业院校">{MOCK_PERSONNEL_DETAIL.graduateSchool}</Descriptions.Item>
<Descriptions.Item label="职务">{MOCK_PERSONNEL_DETAIL.position}</Descriptions.Item>
<Descriptions.Item label="是否注册安全工程师">
<Tag color={MOCK_PERSONNEL_DETAIL.isRegisterEngineer === "是" ? "success" : "error"}>
{MOCK_PERSONNEL_DETAIL.isRegisterEngineer}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="出版学术专著、专利、获奖、发表学术论文等" span={2}>{MOCK_PERSONNEL_DETAIL.publications}</Descriptions.Item>
<Descriptions.Item label="自我申报的专业能力及认定方式" span={2}>{MOCK_PERSONNEL_DETAIL.selfDeclaration}</Descriptions.Item>
<Descriptions.Item label="主要学习工作经历" span={2}>{MOCK_PERSONNEL_DETAIL.workExperience}</Descriptions.Item>
<Descriptions.Item label="申报专业能力证明材料" span={2}>
<Space><Button type="link" size="small">查看附件</Button><Tag color="success">.pdf</Tag></Space>
</Descriptions.Item>
</Descriptions>
<h4 style={{ fontSize: "0.85rem", color: "#1890ff", margin: "1.25rem 0 0.6rem", paddingBottom: "0.3rem", borderBottom: "2px solid #e6f7ff" }}>证照信息</h4>
<Table size="small" bordered rowKey="id" pagination={false} dataSource={MOCK_CERTIFICATES} scroll={{ x: 920 }}
columns={[
{ title: "证照名称", dataIndex: "name", ellipsis: true },
{ title: "证书编号", dataIndex: "certNo", width: 120 },
{ title: "证书类别", dataIndex: "category", width: 120 },
{ title: "发证机关", dataIndex: "issuer", width: 150, ellipsis: true },
{ title: "有效开始日期", dataIndex: "startDate", width: 120 },
{ title: "有效结束日期", dataIndex: "endDate", width: 120 },
{ title: "操作", fixed: "right", width: 80, render: () => <Button type="link" size="small">查看证书</Button> },
]}
/>
</Modal>
{/* 设备详细信息弹窗 */} {/* 设备详细信息弹窗 */}
<Modal <Modal
@ -248,8 +216,14 @@ const FilingTabs = ({
</Descriptions> </Descriptions>
)} )}
</Modal> </Modal>
</>
</>
); );
}; };
export default FilingTabs;
export default Connect(
[NS_QUAL_REVIEW],
true,
)(FilingTabs);

View File

@ -52,7 +52,7 @@ const QualReview = (props) => {
fixed: "left", fixed: "left",
render: (text, record, index) => index + 1, render: (text, record, index) => index + 1,
}, },
{ title: "机构名称", dataIndex: "orgName", width: 220, ellipsis: true }, { title: "机构名称", dataIndex: "filingUnitName", width: 220, ellipsis: true },
{ title: "机构类型", dataIndex: "filingUnitTypeName", width: 100 }, { title: "机构类型", dataIndex: "filingUnitTypeName", width: 100 },
{ title: "证书编号", dataIndex: "filingNo", width: 140, ellipsis: true }, { title: "证书编号", dataIndex: "filingNo", width: 140, ellipsis: true },
{ title: "安全评价业务范围", dataIndex: "businessScope", width: 180, ellipsis: true }, { title: "安全评价业务范围", dataIndex: "businessScope", width: 180, ellipsis: true },
@ -83,7 +83,7 @@ const QualReview = (props) => {
<Button type="link" size="small" onClick={() => props.history.push(`FilingDetail?id=${record.id}`)}> <Button type="link" size="small" onClick={() => props.history.push(`FilingDetail?id=${record.id}`)}>
查看 查看
</Button> </Button>
{record.reviewStatus === "pending" && ( {record.filingStatusCode === 2 && (
<Button type="link" size="small" onClick={() => props.history.push(`QualReviewForm?id=${record.id}`)}> <Button type="link" size="small" onClick={() => props.history.push(`QualReviewForm?id=${record.id}`)}>
审核 审核
</Button> </Button>
@ -100,10 +100,10 @@ const QualReview = (props) => {
form={form} form={form}
loading={qualReviewLoading} loading={qualReviewLoading}
formLine={[ formLine={[
<Form.Item key="orgName" name="orgName"> <Form.Item key="filingUnitName" name="filingUnitName">
<ControlWrapper.Input label="机构名称" placeholder="请输入" allowClear /> <ControlWrapper.Input label="机构名称" placeholder="请输入" allowClear />
</Form.Item>, </Form.Item>,
<Form.Item key="orgType" name="orgType"> <Form.Item key="filingUnitTypeName" name="orgType">
<ControlWrapper.Select <ControlWrapper.Select
label="机构类型" label="机构类型"
placeholder="请选择" placeholder="请选择"

View File

@ -1,14 +1,12 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import { Button, Tag, Space, Modal, Select, Input, Form, Spin } from "antd"; import { Button, Tag, Space, Modal, Select, Input, Form, Spin } from "antd";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout"; import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import { REVIEW_STATUS_MAP } from "~/enumerate/constant"; import { REVIEW_STATUS_MAP } from "~/enumerate/constant";
import { tools } from "@cqsjjb/jjb-common-lib"; import { tools } from "@cqsjjb/jjb-common-lib";
import { import {
MOCK_DETAIL,
MOCK_MATERIALS,
LEGAL_CHECK_ITEMS,
LEGAL_STATUS_STYLE, LEGAL_STATUS_STYLE,
} from "../mockData"; } from "~/enumerate/constant";
import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_QUAL_REVIEW } from "~/enumerate/namespace"; import { NS_QUAL_REVIEW } from "~/enumerate/namespace";
import FilingTabs from "../FilingTabs"; import FilingTabs from "../FilingTabs";
@ -18,19 +16,19 @@ const { TextArea } = Input;
const {router}=tools; const {router}=tools;
const QualReviewForm = (props) => { const QualReviewForm = (props) => {
const { queryComplianceCheck, qualReview } = props; const { queryComplianceCheck, qualReview, fetchQualFilingDetail } = props;
const { qualReviewLoading, complianceCheckItems } = qualReview; const { qualReviewLoading, complianceCheckItems, fetchQualFilingDetailLoading, qualFilingDetail } = qualReview;
const [form] = Form.useForm(); const [form] = Form.useForm();
const [compliance, setCompliance] = useState({}); const [compliance, setCompliance] = useState({});
const [reviewVisible, setReviewVisible] = useState(false); const [reviewVisible, setReviewVisible] = useState(false);
const detail = MOCK_DETAIL;
const statusConfig = REVIEW_STATUS_MAP[detail.reviewStatus]; const statusConfig = REVIEW_STATUS_MAP[qualFilingDetail.filingStatusCode];
const legalPassCount = LEGAL_CHECK_ITEMS.filter( const legalPassCount = complianceCheckItems.filter(
(i) => i.status === "pass", (i) => i.status === "PASS",
).length; ).length;
const legalFailCount = LEGAL_CHECK_ITEMS.filter( const legalFailCount = complianceCheckItems.filter(
(i) => i.status === "fail", (i) => i.status === "NOT_PASS",
).length; ).length;
const handleSubmit = (action) => { const handleSubmit = (action) => {
@ -41,6 +39,7 @@ const QualReviewForm = (props) => {
}; };
useEffect(() => { useEffect(() => {
queryComplianceCheck({id: router.query.id}); queryComplianceCheck({id: router.query.id});
fetchQualFilingDetail({id: router.query.id});
}, []); }, []);
return ( return (
@ -61,16 +60,16 @@ const QualReviewForm = (props) => {
</Button> </Button>
} }
> >
<Spin spinning={qualReviewLoading}> <Spin spinning={qualReviewLoading || fetchQualFilingDetailLoading}>
<div className="qual-review-form"> <div className="qual-review-form">
<div className="legal-check-bar"> <div className="legal-check-bar">
<div className="legal-check-title">法律规定内容</div> <div className="legal-check-title">法律规定内容</div>
<div className="legal-check-grid"> <div className="legal-check-grid">
{complianceCheckItems.map((item) => { {complianceCheckItems.map((item) => {
const style = LEGAL_STATUS_STYLE[item.status]; const style = LEGAL_STATUS_STYLE[item.status] || {};
return ( return (
<div <div
key={item.key} key={item.title}
className="legal-check-item" className="legal-check-item"
style={{ style={{
borderLeft: `3px solid ${style.borderColor}`, borderLeft: `3px solid ${style.borderColor}`,
@ -89,7 +88,7 @@ const QualReviewForm = (props) => {
</div> </div>
<FilingTabs <FilingTabs
detail={detail} detail={qualFilingDetail}
isReview isReview
compliance={compliance} compliance={compliance}
onComplianceChange={(id, v) => onComplianceChange={(id, v) =>

View File

@ -1,92 +1,6 @@
// 资质备案审核相关 mock 数据FilingDetail 和 QualReviewForm 共享 // 资质备案审核相关 mock 数据FilingDetail 和 QualReviewForm 共享
export const MOCK_DETAIL = {
id: 1,
scopeName: "化工、石油加工",
filingRegion: "重庆市渝北区",
orgName: "重庆安评技术研究院有限公司",
orgType: "本地单位",
creditCode: "915001072028699512",
officeAddress: "重庆市沙坪坝区大学城东路20号重庆科技学院第39栋西南角实习用房三楼",
registerAddress: "同上",
certNo: "API-2026-001",
website: "www.cqap.com",
legalPerson: "郑远平",
legalPhone: "023-68705577",
contactPerson: "陈芳",
contactPhone: "139****9012",
fixedAssets: "1200",
officeArea: "1200 ㎡",
archiveArea: "80 ㎡",
evaluatorCount: "25 人",
registerEngineerCount: "12 人",
description: "重庆安评技术研究院成立于2015年是一家专业从事安全评价、安全咨询、安全技术开发与转让的综合性安全技术服务机构。",
reviewStatus: "pending",
};
export const MOCK_MATERIALS = [
{ id: 1, name: "申请材料目录(原件)", attachment: "纸质版扫描件", format: "pdf", status: "uploaded" },
{ id: 2, name: "申请书(原件)", attachment: "纸质版扫描件", format: "pdf", status: "uploaded" },
{ id: 3, name: "法人证明(复印件)", attachment: "纸质版扫描件", format: "pdf", status: "uploaded" },
{ id: 4, name: "三年内无重大违法失信记录查询证明", attachment: "纸质版扫描件", format: "pdf", status: "uploaded" },
{ id: 5, name: "法定代表人承诺书(原件)", attachment: "纸质版扫描件", format: "pdf", status: "uploaded" },
{ id: 6, name: "固定资产法定证明材料", attachment: "纸质版扫描件", format: "pdf", status: "uploaded" },
{ id: 7, name: "工作场所及档案室面积证明", attachment: "纸质版扫描件", format: "pdf", status: "uploaded" },
{ id: 8, name: "安全评价师专业能力证明", attachment: "纸质版扫描件", format: "pdf", status: "uploaded" },
{ id: 9, name: "相关负责人证明材料", attachment: "纸质版扫描件", format: "pdf", status: "uploaded" },
{ id: 10, name: "机构内部管理制度", attachment: "纸质版扫描件", format: "pdf", status: "uploaded" },
];
export const MOCK_PERSONNEL = [
{ id: 1, name: "张建国", type: "评价师", position: "高级评价师", title: "高级工程师", ability: "化工工艺安全风险评估" },
{ id: 2, name: "李明华", type: "评价师", position: "评价师", title: "工程师", ability: "矿山安全评价" },
{ id: 3, name: "王丽萍", type: "管理人员", position: "人事主管", title: "—", ability: "人力资源管理" },
{ id: 4, name: "陈志强", type: "评价师", position: "评价师", title: "工程师", ability: "建筑施工安全评估" },
{ id: 5, name: "赵磊", type: "管理人员", position: "综合部主任", title: "—", ability: "安全管理体系" },
];
export const MOCK_EQUIPMENT = [
{ id: 1, name: "气相色谱仪", model: "GC-2018", manufacturer: "岛津", calibration: "done" },
{ id: 2, name: "噪声计", model: "HS-6288", manufacturer: "杭州爱华", calibration: "done" },
{ id: 3, name: "粉尘采样器", model: "FC-5A", manufacturer: "北京劳保所", calibration: "pending" },
];
export const MOCK_EQUIP_DETAIL = {
name: "气相色谱仪",
model: "GC-2018",
category: "分析仪器",
type: "色谱检测类",
manufacturer: "岛津",
calibrationType: "定期检定",
flowDesc: "载气流量 30mL/min",
maxFlow: "100 mL/min",
minFlow: "1 mL/min",
dualChannel: "否",
calibInitValue: "0.0001 mg/m³",
calibUnit: "重庆市计量质量检测研究院",
status: "done",
};
export const MOCK_PERSONNEL_DETAIL = {
name: "张建国",
gender: "男",
qualScope: "化工",
birthDate: "1985-03-15",
officeAddress: "重庆市渝北区龙山街道100号",
major: "安全工程",
certLevel: "安全评价师一级 / SJP-2020-****",
email: "zhangjg@cqap.com",
personnelType: "专职评价师",
idCard: "500***********1234",
currentAddress: "重庆市渝北区新牌坊某路某号",
education: "全日制 · 本科",
graduateSchool: "重庆大学",
position: "高级评价师",
isRegisterEngineer: "是",
publications: "发表安全评价相关论文3篇参与编制行业标准1项",
selfDeclaration: "化工工艺安全风险评估,通过注册安全工程师资格考试认定",
workExperience: "2008-2012 重庆大学 安全工程专业 本科2012-至今 重庆安评技术研究院有限公司 评价师",
};
export const MOCK_CERTIFICATES = [ export const MOCK_CERTIFICATES = [
{ id: 1, name: "危险化学品经营许可证", certNo: "AQSC001", category: "安全生产证书", issuer: "重庆市应急管理局", startDate: "2025-03-29", endDate: "2028-03-28" }, { id: 1, name: "危险化学品经营许可证", certNo: "AQSC001", category: "安全生产证书", issuer: "重庆市应急管理局", startDate: "2025-03-29", endDate: "2028-03-28" },
@ -98,21 +12,6 @@ export const CALIBRATION_MAP = {
pending: { label: "待检定", color: "warning" }, pending: { label: "待检定", color: "warning" },
}; };
export const LEGAL_CHECK_ITEMS = [
{ key: "qualification", title: "主体资格", desc: "独立法人资格,固定资产不少于一千万元", status: "pass" },
{ key: "facility", title: "场所与设备", desc: "工作场所建筑面积不少于1000㎡", status: "pass" },
{ key: "staffCount", title: "人员数量与结构", desc: "专职技术人员≥25人中级注安师≥30%", status: "pass" },
{ key: "staffExp", title: "人员资历", desc: "专职技术人员在本行业领域工作二年以上", status: "pass" },
{ key: "keyRole", title: "关键岗位要求", desc: "负责人具有高级职称,工作八年以上", status: "pass" },
{ key: "management", title: "管理体系", desc: "需核验文件化管理体系是否符合要求", status: "warn" },
{ key: "commitment", title: "责任承诺", desc: "法定代表人已出具承诺书", status: "pass" },
{ key: "disclosure", title: "信息公开", desc: "未检测到公示网站", status: "fail" },
{ key: "credit", title: "信用记录", desc: "三年内无重大违法失信记录", status: "pass" },
{ key: "other", title: "其他", desc: "法律、行政法规规定的其他条件", status: "pass" },
];
export const LEGAL_STATUS_STYLE = {
pass: { icon: "✅", borderColor: "#059669", bg: "#f0fdf4" },
warn: { icon: "⚠️", borderColor: "#d97706", bg: "#fffbeb" },
NOT_PASS: { icon: "❌", borderColor: "#dc2626", bg: "#fef2f2" },
};