feat
parent
0a0d207409
commit
9c8e386773
|
|
@ -7,6 +7,7 @@
|
|||
/prototype
|
||||
/dist
|
||||
/demo
|
||||
/doc
|
||||
/docs
|
||||
/.trae/
|
||||
npm-debug.log*
|
||||
|
|
|
|||
|
|
@ -62,3 +62,17 @@ export const modifyFilingMaterial = declareRequest(
|
|||
"qualFilingMaterialLoading",
|
||||
"Post > @/safetyEval/qual-filing-material/modify",
|
||||
);
|
||||
|
||||
// ─── 资质保持监控 ───
|
||||
|
||||
export const queryQualMonitorPage = declareRequest(
|
||||
"qualReviewLoading",
|
||||
"Get > /safetyEval/qual-monitor/page",
|
||||
'qualMonitorList: [] | res.data || [] & qualMonitorTotal: 0 | res.total || 0',
|
||||
);
|
||||
|
||||
export const queryQualMonitorCondition = declareRequest(
|
||||
"qualReviewLoading",
|
||||
"Get > /safetyEval/qual-monitor/condition-detail",
|
||||
'qualMonitorCondition: {} | res.data || {}',
|
||||
);
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Modal, Input, message } from 'antd';
|
||||
import './index.less';
|
||||
|
||||
const BaiduMapPicker = ({ visible, onCancel, onConfirm }) => {
|
||||
const mapRef = useRef(null);
|
||||
const mapInstanceRef = useRef(null);
|
||||
const markerRef = useRef(null);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [selectedPoint, setSelectedPoint] = useState(null);
|
||||
|
||||
// 动态加载百度地图脚本
|
||||
useEffect(() => {
|
||||
if (!visible) return;
|
||||
if (window.BMap) {
|
||||
// 如果已有 BMap,等 DOM 渲染后再初始化
|
||||
setTimeout(() => setLoaded(true), 100);
|
||||
return;
|
||||
}
|
||||
|
||||
const scriptId = 'baiduMapScript';
|
||||
if (document.getElementById(scriptId)) return;
|
||||
|
||||
window.__onBMapLoaded = () => {
|
||||
setLoaded(true);
|
||||
};
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.id = scriptId;
|
||||
script.src = `https://api.map.baidu.com/api?v=3.0&ak=6lkWDSnHTCcvUenZhovQPER9nkhFfT9A&callback=__onBMapLoaded`;
|
||||
script.async = true;
|
||||
document.body.appendChild(script);
|
||||
}, [visible]);
|
||||
|
||||
// 初始化地图
|
||||
useEffect(() => {
|
||||
if (!loaded || !visible || !mapRef.current) return;
|
||||
// 避免重复初始化
|
||||
if (mapInstanceRef.current) return;
|
||||
|
||||
const map = new window.BMap.Map(mapRef.current);
|
||||
// 默认以重庆市中心为中心
|
||||
map.centerAndZoom(new window.BMap.Point(106.55, 29.57), 12);
|
||||
map.enableScrollWheelZoom(true);
|
||||
mapInstanceRef.current = map;
|
||||
|
||||
// 点击地图获取坐标
|
||||
map.addEventListener('click', (e) => {
|
||||
const point = e.point;
|
||||
setSelectedPoint(point);
|
||||
if (markerRef.current) {
|
||||
map.removeOverlay(markerRef.current);
|
||||
}
|
||||
const marker = new window.BMap.Marker(point);
|
||||
map.addOverlay(marker);
|
||||
markerRef.current = marker;
|
||||
});
|
||||
|
||||
// 搜索自动完成
|
||||
const ac = new window.BMap.Autocomplete({
|
||||
input: 'baiduSearchInput',
|
||||
location: map,
|
||||
});
|
||||
ac.addEventListener('onconfirm', (e) => {
|
||||
const value = e.item.value;
|
||||
const local = new window.BMap.LocalSearch(map, {
|
||||
onSearchComplete: (results) => {
|
||||
if (results.getCurrentNumPois() > 0) {
|
||||
const poi = results.getPoi(0);
|
||||
const point = poi.point;
|
||||
setSelectedPoint(point);
|
||||
map.centerAndZoom(point, 15);
|
||||
if (markerRef.current) {
|
||||
map.removeOverlay(markerRef.current);
|
||||
}
|
||||
const marker = new window.BMap.Marker(point);
|
||||
map.addOverlay(marker);
|
||||
markerRef.current = marker;
|
||||
}
|
||||
},
|
||||
});
|
||||
local.search(
|
||||
[value.province, value.city, value.district, value.street, value.business]
|
||||
.filter(Boolean)
|
||||
.join(''),
|
||||
);
|
||||
});
|
||||
|
||||
return () => {
|
||||
mapInstanceRef.current = null;
|
||||
};
|
||||
}, [loaded, visible]);
|
||||
|
||||
// 弹窗关闭时重置
|
||||
useEffect(() => {
|
||||
if (!visible) {
|
||||
setLoaded(false);
|
||||
setSelectedPoint(null);
|
||||
markerRef.current = null;
|
||||
mapInstanceRef.current = null;
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!selectedPoint) {
|
||||
message.warning('请在地图上选择位置');
|
||||
return;
|
||||
}
|
||||
onConfirm({
|
||||
lng: selectedPoint.lng,
|
||||
lat: selectedPoint.lat,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="选择坐标"
|
||||
open={visible}
|
||||
onCancel={onCancel}
|
||||
onOk={handleConfirm}
|
||||
width={800}
|
||||
okText="确认"
|
||||
cancelText="取消"
|
||||
destroyOnClose
|
||||
>
|
||||
<input
|
||||
id="baiduSearchInput"
|
||||
className="baidu-map-search"
|
||||
placeholder="输入地名搜索"
|
||||
/>
|
||||
<div ref={mapRef} className="baidu-map-container" />
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default BaiduMapPicker;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
.baidu-map-search {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
padding: 4px 11px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 6px;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: #4096ff;
|
||||
box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #bfbfbf;
|
||||
}
|
||||
}
|
||||
|
||||
.baidu-map-container {
|
||||
width: 100%;
|
||||
height: 480px;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 百度地图 Autocomplete 联想面板默认 z-index 过低,被 Modal 遮挡 */
|
||||
.tangram-suggestion-main {
|
||||
z-index: 10000 !important;
|
||||
}
|
||||
|
|
@ -61,6 +61,14 @@ export const CHANGE_STATUS_MAP = {
|
|||
approved: { label: "已审核", color: "success" },
|
||||
};
|
||||
|
||||
/** 资质保持监控状态 */
|
||||
export const QUAL_MONITOR_STATUS_MAP = {
|
||||
normal: { label: "保持正常", color: "success" },
|
||||
person_warning: { label: "人员比例预警", color: "warning" },
|
||||
focus_warning: { label: "重点预警", color: "error" },
|
||||
area_pending: { label: "属地待核验", color: "warning" },
|
||||
};
|
||||
|
||||
/** 性别 */
|
||||
export const GENDER_OPTIONS = [
|
||||
{ label: "男", value: 1 },
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import {
|
|||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Flex
|
||||
} from "antd";
|
||||
import AttachmentUpload from "~/components/AttachmentUpload";
|
||||
|
||||
import BaiduMapPicker from "~/components/BaiduMapPicker";
|
||||
import dayjs from "dayjs";
|
||||
import { useEffect, useState } from "react";
|
||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||
|
|
@ -44,6 +45,7 @@ function OrgInfoPage(props) {
|
|||
const [editing, setEditing] = useState(true);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [detail, setDetail] = useState({});
|
||||
const [mapPickerVisible, setMapPickerVisible] = useState(false);
|
||||
/** 是否已存在机构数据(有 id 视为已入库,只能修改) */
|
||||
const [hasExistingData, setHasExistingData] = useState(false);
|
||||
|
||||
|
|
@ -128,6 +130,10 @@ function OrgInfoPage(props) {
|
|||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
const handleMapConfirm = ({ lng, lat }) => {
|
||||
form.setFieldsValue({ longitude: lng, latitude: lat });
|
||||
setMapPickerVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
|
|
@ -160,7 +166,11 @@ function OrgInfoPage(props) {
|
|||
label="生产经营单位名称"
|
||||
rules={[{ required: true, message: "请输入生产经营单位名称" }]}
|
||||
>
|
||||
<Input placeholder="请输入生产经营单位名称" allowClear maxLength={200} />
|
||||
<Input
|
||||
placeholder="请输入生产经营单位名称"
|
||||
allowClear
|
||||
maxLength={200}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -169,7 +179,11 @@ function OrgInfoPage(props) {
|
|||
label="统一社会信用代码"
|
||||
rules={[creditCodeRule(true)]}
|
||||
>
|
||||
<Input placeholder="请输入统一社会信用代码" allowClear maxLength={18} />
|
||||
<Input
|
||||
placeholder="请输入统一社会信用代码"
|
||||
allowClear
|
||||
maxLength={18}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -180,7 +194,11 @@ function OrgInfoPage(props) {
|
|||
{ required: true, message: "请输入安全生产监管行业类别" },
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入安全生产监管行业类别" allowClear maxLength={100} />
|
||||
<Input
|
||||
placeholder="请输入安全生产监管行业类别"
|
||||
allowClear
|
||||
maxLength={100}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -206,31 +224,62 @@ function OrgInfoPage(props) {
|
|||
label="所属镇、街道"
|
||||
rules={[{ required: true, message: "请输入所属镇街道" }]}
|
||||
>
|
||||
<Input placeholder="请输入所属镇街道" allowClear maxLength={100} />
|
||||
<Input
|
||||
placeholder="请输入所属镇街道"
|
||||
allowClear
|
||||
maxLength={100}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="villageCommunity" label="属村(社区)">
|
||||
<Input placeholder="请输入属村(社区)" allowClear maxLength={100} />
|
||||
<Input
|
||||
placeholder="请输入属村(社区)"
|
||||
allowClear
|
||||
maxLength={100}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="longitude"
|
||||
label="所在地坐标经度"
|
||||
dependencies={["latitude"]}
|
||||
rules={[
|
||||
longitudeRule(false),
|
||||
coordinatePairRule("latitude", "经度", "纬度"),
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{ width: "100%" }}
|
||||
min={-180}
|
||||
max={180}
|
||||
precision={6}
|
||||
placeholder="请输入经度"
|
||||
/>
|
||||
<Form.Item label="所在地坐标经度">
|
||||
<Flex>
|
||||
<Form.Item
|
||||
name="longitude"
|
||||
noStyle
|
||||
dependencies={["latitude"]}
|
||||
rules={[
|
||||
{
|
||||
validator(_, value) {
|
||||
const latitude = form.getFieldValue("latitude");
|
||||
if (
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
value !== "" &&
|
||||
(latitude === undefined ||
|
||||
latitude === null ||
|
||||
latitude === "")
|
||||
) {
|
||||
return Promise.reject(
|
||||
new Error("填写经度后,纬度必填"),
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{ width: "100%" }}
|
||||
min={-180}
|
||||
max={180}
|
||||
precision={6}
|
||||
placeholder="请输入经度"
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Button onClick={() => setMapPickerVisible(true)}>选择</Button>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -258,7 +307,11 @@ function OrgInfoPage(props) {
|
|||
label="注册地址"
|
||||
rules={[{ required: true, message: "请输入注册地址" }]}
|
||||
>
|
||||
<Input.TextArea rows={3} placeholder="请输入注册地址" maxLength={200} />
|
||||
<Input.TextArea
|
||||
rows={3}
|
||||
placeholder="请输入注册地址"
|
||||
maxLength={200}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -267,7 +320,11 @@ function OrgInfoPage(props) {
|
|||
label="经营地址"
|
||||
rules={[{ required: true, message: "请输入经营地址" }]}
|
||||
>
|
||||
<Input.TextArea rows={3} placeholder="请输入经营地址" maxLength={200} />
|
||||
<Input.TextArea
|
||||
rows={3}
|
||||
placeholder="请输入经营地址"
|
||||
maxLength={200}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -280,7 +337,11 @@ function OrgInfoPage(props) {
|
|||
name="economyIndustryCode"
|
||||
label="国民经济行业分类(GB/T4754-2017)"
|
||||
>
|
||||
<Input placeholder="请输入国民经济行业分类" allowClear maxLength={50} />
|
||||
<Input
|
||||
placeholder="请输入国民经济行业分类"
|
||||
allowClear
|
||||
maxLength={50}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -301,7 +362,11 @@ function OrgInfoPage(props) {
|
|||
typeof value === "string" ? value.trim() : value
|
||||
}
|
||||
>
|
||||
<Input placeholder="请输入法定代表人联系电话" allowClear maxLength={11} />
|
||||
<Input
|
||||
placeholder="请输入法定代表人联系电话"
|
||||
allowClear
|
||||
maxLength={11}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -322,12 +387,20 @@ function OrgInfoPage(props) {
|
|||
typeof value === "string" ? value.trim() : value
|
||||
}
|
||||
>
|
||||
<Input placeholder="请输入主要负责人联系电话" allowClear maxLength={11} />
|
||||
<Input
|
||||
placeholder="请输入主要负责人联系电话"
|
||||
allowClear
|
||||
maxLength={11}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="safetyDeptManager" label="安全管理部门负责人">
|
||||
<Input placeholder="请输入安全管理部门负责人" allowClear maxLength={50} />
|
||||
<Input
|
||||
placeholder="请输入安全管理部门负责人"
|
||||
allowClear
|
||||
maxLength={50}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -356,7 +429,11 @@ function OrgInfoPage(props) {
|
|||
typeof value === "string" ? value.trim() : value
|
||||
}
|
||||
>
|
||||
<Input placeholder="请输入主管安全副总联系电话" allowClear maxLength={11} />
|
||||
<Input
|
||||
placeholder="请输入主管安全副总联系电话"
|
||||
allowClear
|
||||
maxLength={11}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -369,7 +446,11 @@ function OrgInfoPage(props) {
|
|||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="businessStatusName" label="企业经营状态">
|
||||
<Input placeholder="请输入企业经营状态" allowClear maxLength={50} />
|
||||
<Input
|
||||
placeholder="请输入企业经营状态"
|
||||
allowClear
|
||||
maxLength={50}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -381,7 +462,11 @@ function OrgInfoPage(props) {
|
|||
typeof value === "string" ? value.trim() : value
|
||||
}
|
||||
>
|
||||
<Input placeholder="请输入信息公开网址" allowClear maxLength={200} />
|
||||
<Input
|
||||
placeholder="请输入信息公开网址"
|
||||
allowClear
|
||||
maxLength={200}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -393,6 +478,7 @@ function OrgInfoPage(props) {
|
|||
<InputNumber
|
||||
style={{ width: "100%" }}
|
||||
min={0}
|
||||
max={99999}
|
||||
precision={2}
|
||||
placeholder="请输入工作场所建筑面积"
|
||||
/>
|
||||
|
|
@ -407,6 +493,7 @@ function OrgInfoPage(props) {
|
|||
<InputNumber
|
||||
style={{ width: "100%" }}
|
||||
min={0}
|
||||
max={99999}
|
||||
precision={2}
|
||||
placeholder="请输入档案室面积"
|
||||
/>
|
||||
|
|
@ -421,6 +508,7 @@ function OrgInfoPage(props) {
|
|||
<InputNumber
|
||||
style={{ width: "100%" }}
|
||||
min={0}
|
||||
max={99999}
|
||||
precision={0}
|
||||
placeholder="请输入专职安全评价师数量"
|
||||
/>
|
||||
|
|
@ -435,6 +523,7 @@ function OrgInfoPage(props) {
|
|||
<InputNumber
|
||||
style={{ width: "100%" }}
|
||||
min={0}
|
||||
max={99999}
|
||||
precision={0}
|
||||
placeholder="请输入注册安全工程师数量"
|
||||
/>
|
||||
|
|
@ -503,10 +592,21 @@ function OrgInfoPage(props) {
|
|||
<Form.Item name="filingRecordStatusName" noStyle />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<AttachmentUpload name="attachmentUrls" label="上传附件" />
|
||||
<AttachmentUpload
|
||||
name="attachmentUrls"
|
||||
label="上传附件"
|
||||
extra={"最多上传10个PDF、DOC、DOCX格式文件"}
|
||||
maxCount={10}
|
||||
accept=".pdf,.doc,.docx"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
<BaiduMapPicker
|
||||
visible={mapPickerVisible}
|
||||
onCancel={() => setMapPickerVisible(false)}
|
||||
onConfirm={handleMapConfirm}
|
||||
/>
|
||||
{editing && (
|
||||
<div style={{ marginTop: 24, textAlign: "center" }}>
|
||||
<Space>
|
||||
|
|
|
|||
|
|
@ -106,6 +106,11 @@ const menuItems = [
|
|||
label: "备案变更管理",
|
||||
icon: <SwapOutlined />,
|
||||
},
|
||||
{
|
||||
key: "/safetyEval/container/QualificationReview/QualMonitor",
|
||||
label: "资质保持监控",
|
||||
icon: <BarChartOutlined />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,532 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Button, Form, Select, Table, Tag, Modal } 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";
|
||||
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 { QUAL_MONITOR_STATUS_MAP } from "~/enumerate/constant";
|
||||
import "./index.less";
|
||||
|
||||
const { router } = tools;
|
||||
|
||||
/** 资质条件检查项模板 */
|
||||
const CONDITION_ITEMS = [
|
||||
{ key: "legalPerson", label: "独立法人资格" },
|
||||
{ key: "fixedAsset", label: "固定资产" },
|
||||
{ key: "workplace", label: "工作场所" },
|
||||
{ key: "equipment", label: "设施设备软件" },
|
||||
{ key: "staffCount", label: "专职技术人员数量" },
|
||||
{ key: "staffRatio", label: "人员比例" },
|
||||
{ key: "leader", label: "负责人配备" },
|
||||
{ key: "system", label: "制度与过程控制体系" },
|
||||
{ key: "website", label: "承诺书/网站/报告查询" },
|
||||
{ key: "credit", label: "三年重大违法失信记录" },
|
||||
{ key: "area", label: "属地满足情况" },
|
||||
];
|
||||
|
||||
/**
|
||||
* Mock 数据 — 后端接口就绪后删除此数据及下方 MOCK_INSTITUTIONS 引用,
|
||||
* 改用 props.qualReview.qualMonitorList 即可。
|
||||
*/
|
||||
const MOCK_INSTITUTIONS = [
|
||||
{
|
||||
id: 1,
|
||||
orgName: "重庆安评技术研究院有限公司",
|
||||
creditCode: "91500112MA5UQ001",
|
||||
registerArea: "重庆市渝北区",
|
||||
bizScopes: ["石油加工业,化学原料、化学品及医药制造业", "金属冶炼"],
|
||||
bizScopeCount: 2,
|
||||
minStaffRequire: 30,
|
||||
status: "normal",
|
||||
fixedAsset: "1260 万元",
|
||||
workplace: "1280 ㎡",
|
||||
archiveRoom: "160 ㎡",
|
||||
staffCount: 36,
|
||||
midSafeEngineer: 12,
|
||||
midSafeRatio: "33%",
|
||||
seniorTitle: 13,
|
||||
seniorRatio: "36%",
|
||||
totalRatio: "69%",
|
||||
techLeader: "2/2",
|
||||
processLeader: "1/1",
|
||||
systemStatus: "制度体系已备案",
|
||||
promiseStatus: "承诺书已上传",
|
||||
websiteStatus: "网站可查询",
|
||||
creditStatus: "近三年无重大违法失信记录",
|
||||
sourceFields: ["机构备案.固定资产", "场所核验.面积", "人员库.专职人员", "网站备案.报告查询"],
|
||||
assetOk: true,
|
||||
workplaceOk: true,
|
||||
archiveOk: true,
|
||||
staffOk: true,
|
||||
conditionDetails: {
|
||||
legalPerson: { require: "具有独立法人资格", value: "独立法人", result: "通过", source: "机构备案.法人类型、营业执照附件" },
|
||||
fixedAsset: { require: "不少于 800 万元", value: "1260 万元", result: "通过", source: "财务材料.固定资产净值、审计报告" },
|
||||
workplace: { require: "建筑面积不少于 1000 平方米,档案室不少于 100 平方米", value: "工作场所 1280 平方米,档案室 160 平方米", result: "通过", source: "场所核验.建筑面积、档案室核验.面积" },
|
||||
equipment: { require: "具有与业务相适应的技术支撑条件", value: "设备台账、软件授权、检测工具齐全", result: "通过", source: "设备台账.设备清单、软件授权.有效期" },
|
||||
staffCount: { require: "单一业务范围不少于 25 人", value: "2 个业务范围,36 人", result: "通过", source: "人员库.专职人员、业务范围.数量" },
|
||||
staffRatio: { require: "注安≥30%,高工≥30%,合计≥60%", value: "注安 33%,高工 36%,合计 69%", result: "通过", source: "人员库.注册安全工程师、人员库.职称等级" },
|
||||
leader: { require: "每个业务范围配备专职技术负责人", value: "技术负责人 2/2,过程控制负责人 1/1", result: "通过", source: "负责人备案.技术负责人、负责人备案.过程控制负责人" },
|
||||
system: { require: "具有健全内部管理制度和安全评价过程控制体系", value: "制度文件已备案,过程控制节点可追溯", result: "通过", source: "制度文件.版本、过程控制.节点记录" },
|
||||
website: { require: "法定代表人承诺书已出具;网站正常运行", value: "承诺书已上传;网站可查询机构信息和报告", result: "通过", source: "承诺书.上传状态、网站备案.报告查询状态" },
|
||||
credit: { require: "三年内无重大违法失信记录", value: "未命中重大违法失信记录", result: "通过", source: "信用核查.重大违法失信记录" },
|
||||
area: { require: "注册地所在行政区域范围内满足资质条件", value: "注册地与资质条件材料一致", result: "通过", source: "异地备案.注册地、本市社保.缴纳人数" },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
orgName: "重庆恒安安全评价有限公司",
|
||||
creditCode: "91500103MA60H002",
|
||||
registerArea: "重庆市江北区",
|
||||
bizScopes: ["石油加工业,化学原料、化学品及医药制造业"],
|
||||
bizScopeCount: 1,
|
||||
minStaffRequire: 25,
|
||||
status: "person_warning",
|
||||
fixedAsset: "920 万元",
|
||||
workplace: "1040 ㎡",
|
||||
archiveRoom: "110 ㎡",
|
||||
staffCount: 27,
|
||||
midSafeEngineer: 9,
|
||||
midSafeRatio: "33%",
|
||||
seniorTitle: 7,
|
||||
seniorRatio: "26%",
|
||||
totalRatio: "59%",
|
||||
techLeader: "1/1",
|
||||
processLeader: "1/1",
|
||||
systemStatus: "制度体系已备案",
|
||||
promiseStatus: "承诺书已上传",
|
||||
websiteStatus: "网站可查询",
|
||||
creditStatus: "近三年无重大违法失信记录",
|
||||
sourceFields: ["人员库.职称等级", "人员库.注册安全工程师", "社保比对.缴纳单位", "业务范围.数量"],
|
||||
assetOk: true,
|
||||
workplaceOk: true,
|
||||
archiveOk: true,
|
||||
staffOk: true,
|
||||
conditionDetails: {
|
||||
legalPerson: { require: "具有独立法人资格", value: "独立法人", result: "通过", source: "机构备案.法人类型" },
|
||||
fixedAsset: { require: "不少于 800 万元", value: "920 万元", result: "通过", source: "财务材料.固定资产净值" },
|
||||
workplace: { require: "建筑面积不少于 1000 平方米,档案室不少于 100 平方米", value: "工作场所 1040 平方米,档案室 110 平方米", result: "通过", source: "场所核验.建筑面积、档案室核验.面积" },
|
||||
equipment: { require: "具有与业务相适应的技术支撑条件", value: "设备台账、软件授权齐全", result: "通过", source: "设备台账.设备清单" },
|
||||
staffCount: { require: "单一业务范围不少于 25 人", value: "1 个业务范围,27 人", result: "通过", source: "人员库.专职人员" },
|
||||
staffRatio: { require: "注安≥30%,高工≥30%,合计≥60%", value: "注安 33%,高工 26%,合计 59%", result: "预警", source: "人员库.职称等级" },
|
||||
leader: { require: "每个业务范围配备专职技术负责人", value: "技术负责人 1/1,过程控制负责人 1/1", result: "通过", source: "负责人备案" },
|
||||
system: { require: "具有健全内部管理制度和安全评价过程控制体系", value: "制度文件已备案", result: "通过", source: "制度文件" },
|
||||
website: { require: "法定代表人承诺书已出具;网站正常运行", value: "承诺书已上传;网站可查询", result: "通过", source: "网站备案" },
|
||||
credit: { require: "三年内无重大违法失信记录", value: "未命中", result: "通过", source: "信用核查" },
|
||||
area: { require: "注册地所在行政区域范围内满足资质条件", value: "注册地与资质条件材料一致", result: "通过", source: "本市社保.缴纳人数" },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
orgName: "重庆渝安风险评估中心",
|
||||
creditCode: "91500108MA61Y003",
|
||||
registerArea: "重庆市南岸区",
|
||||
bizScopes: ["金属、非金属矿及其他矿采选业"],
|
||||
bizScopeCount: 1,
|
||||
minStaffRequire: 25,
|
||||
status: "focus_warning",
|
||||
fixedAsset: "760 万元",
|
||||
workplace: "980 ㎡",
|
||||
archiveRoom: "90 ㎡",
|
||||
staffCount: 24,
|
||||
midSafeEngineer: 7,
|
||||
midSafeRatio: "29%",
|
||||
seniorTitle: 8,
|
||||
seniorRatio: "33%",
|
||||
totalRatio: "62%",
|
||||
techLeader: "1/1",
|
||||
processLeader: "1/1",
|
||||
systemStatus: "制度体系已备案",
|
||||
promiseStatus: "承诺书已上传",
|
||||
websiteStatus: "网站报告查询接口异常",
|
||||
creditStatus: "近三年无重大违法失信记录",
|
||||
sourceFields: ["财务材料.固定资产", "场所核验.建筑面积", "档案室核验.面积", "人员库.专职人数", "网站监测.查询状态"],
|
||||
assetOk: false,
|
||||
workplaceOk: false,
|
||||
archiveOk: false,
|
||||
staffOk: false,
|
||||
conditionDetails: {
|
||||
legalPerson: { require: "具有独立法人资格", value: "独立法人", result: "通过", source: "机构备案.法人类型" },
|
||||
fixedAsset: { require: "不少于 800 万元", value: "760 万元", result: "不通过", source: "财务材料.固定资产净值" },
|
||||
workplace: { require: "建筑面积不少于 1000 平方米,档案室不少于 100 平方米", value: "工作场所 980 平方米,档案室 90 平方米", result: "不通过", source: "场所核验.建筑面积" },
|
||||
equipment: { require: "具有与业务相适应的技术支撑条件", value: "设备台账齐全,报告查询接口异常", result: "部分异常", source: "网站监测.查询状态" },
|
||||
staffCount: { require: "单一业务范围不少于 25 人", value: "1 个业务范围,24 人", result: "不通过", source: "人员库.专职人数" },
|
||||
staffRatio: { require: "注安≥30%,高工≥30%,合计≥60%", value: "注安 29%,高工 33%,合计 62%", result: "预警", source: "人员库.注册安全工程师" },
|
||||
leader: { require: "每个业务范围配备专职技术负责人", value: "技术负责人 1/1,过程控制负责人 1/1", result: "通过", source: "负责人备案" },
|
||||
system: { require: "具有健全内部管理制度和安全评价过程控制体系", value: "制度文件已备案", result: "通过", source: "制度文件" },
|
||||
website: { require: "法定代表人承诺书已出具;网站正常运行", value: "承诺书已上传;网站报告查询接口异常", result: "预警", source: "网站备案.报告查询状态" },
|
||||
credit: { require: "三年内无重大违法失信记录", value: "未命中", result: "通过", source: "信用核查" },
|
||||
area: { require: "注册地所在行政区域范围内满足资质条件", value: "注册地与资质条件材料一致", result: "通过", source: "本市社保" },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
orgName: "北京中安评价中心重庆分公司",
|
||||
creditCode: "91110108MA62B004",
|
||||
registerArea: "北京市,重庆备案场所:两江新区",
|
||||
bizScopes: ["陆上油气管道运输业"],
|
||||
bizScopeCount: 1,
|
||||
minStaffRequire: 25,
|
||||
status: "area_pending",
|
||||
fixedAsset: "1080 万元",
|
||||
workplace: "1120 ㎡",
|
||||
archiveRoom: "120 ㎡",
|
||||
staffCount: 31,
|
||||
midSafeEngineer: 10,
|
||||
midSafeRatio: "32%",
|
||||
seniorTitle: 9,
|
||||
seniorRatio: "29%",
|
||||
totalRatio: "61%",
|
||||
techLeader: "1/1",
|
||||
processLeader: "1/1",
|
||||
systemStatus: "制度体系已备案",
|
||||
promiseStatus: "承诺书已上传",
|
||||
websiteStatus: "网站可查询",
|
||||
creditStatus: "近三年无重大违法失信记录",
|
||||
sourceFields: ["异地备案.注册地", "本市社保.缴纳人数", "场所核验.租赁合同", "业务范围.油气管道"],
|
||||
assetOk: true,
|
||||
workplaceOk: true,
|
||||
archiveOk: true,
|
||||
staffOk: true,
|
||||
conditionDetails: {
|
||||
legalPerson: { require: "具有独立法人资格", value: "总机构独立法人,重庆为分公司备案", result: "需核验", source: "机构备案.法人类型" },
|
||||
fixedAsset: { require: "不少于 800 万元", value: "1080 万元", result: "通过", source: "财务材料.固定资产净值" },
|
||||
workplace: { require: "建筑面积不少于 1000 平方米,档案室不少于 100 平方米", value: "重庆场所 1120 平方米,档案室 120 平方米", result: "通过", source: "场所核验.租赁合同" },
|
||||
equipment: { require: "具有与业务相适应的技术支撑条件", value: "设备台账齐全", result: "通过", source: "设备台账" },
|
||||
staffCount: { require: "单一业务范围不少于 25 人", value: "备案 31 人,本市社保 21 人", result: "不通过", source: "社保比对.缴纳单位" },
|
||||
staffRatio: { require: "注安≥30%,高工≥30%,合计≥60%", value: "本市专职人员比例待核验", result: "需核验", source: "人员库.本市社保" },
|
||||
leader: { require: "每个业务范围配备专职技术负责人", value: "技术负责人 1/1,过程控制负责人 1/1", result: "通过", source: "负责人备案" },
|
||||
system: { require: "具有健全内部管理制度和安全评价过程控制体系", value: "制度文件已备案", result: "通过", source: "制度文件" },
|
||||
website: { require: "法定代表人承诺书已出具;网站正常运行", value: "承诺书已上传;网站可查询", result: "通过", source: "网站备案" },
|
||||
credit: { require: "三年内无重大违法失信记录", value: "未命中", result: "通过", source: "信用核查" },
|
||||
area: { require: "注册地所在行政区域范围内满足资质条件", value: "重庆备案场所达标,本市社保专职人员不足", result: "需核验", source: "异地备案.注册地" },
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const STATUS_OPTIONS = [
|
||||
{ value: "", label: "全部" },
|
||||
{ value: "normal", label: "保持正常" },
|
||||
{ value: "person_warning", label: "人员比例预警" },
|
||||
{ value: "focus_warning", label: "重点预警" },
|
||||
{ value: "area_pending", label: "属地待核验" },
|
||||
];
|
||||
|
||||
const SCOPE_OPTIONS = [
|
||||
{ value: "", label: "全部" },
|
||||
{ value: "化工", label: "石油加工业,化学原料、化学品及医药制造业" },
|
||||
{ value: "金属冶炼", label: "金属冶炼" },
|
||||
{ value: "矿采选", label: "金属、非金属矿及其他矿采选业" },
|
||||
{ value: "油气管道", label: "陆上油气管道运输业" },
|
||||
];
|
||||
|
||||
/** 统计卡片数据 */
|
||||
function computeStats(list) {
|
||||
return {
|
||||
total: list.length,
|
||||
normal: list.filter((i) => i.status === "normal").length,
|
||||
warning: list.filter((i) => i.status === "person_warning" || i.status === "area_pending").length,
|
||||
focus: list.filter((i) => i.status === "focus_warning").length,
|
||||
};
|
||||
}
|
||||
|
||||
const QualMonitor = (props) => {
|
||||
const [form] = Form.useForm();
|
||||
const [detailVisible, setDetailVisible] = useState(false);
|
||||
const [detailRecord, setDetailRecord] = useState(null);
|
||||
// 待后端接口就绪后,data 改为使用 props.qualReview.qualMonitorList,
|
||||
// 并移除 MOCK_INSTITUTIONS 引用
|
||||
const [data, setData] = useState(MOCK_INSTITUTIONS);
|
||||
|
||||
const { qualReview, queryQualMonitorPage, queryQualMonitorCondition } = props;
|
||||
const { qualMonitorList, qualMonitorTotal, qualReviewLoading } = qualReview || {};
|
||||
|
||||
const stats = computeStats(data);
|
||||
|
||||
/** 搜索 — 当前使用 mock 本地过滤;接接口后改为调用 queryQualMonitorPage */
|
||||
const onFinish = (values) => {
|
||||
const { keyword, scope, status } = values;
|
||||
let filtered = MOCK_INSTITUTIONS;
|
||||
if (keyword) {
|
||||
filtered = filtered.filter(
|
||||
(i) => i.orgName.includes(keyword) || i.creditCode.includes(keyword),
|
||||
);
|
||||
}
|
||||
if (scope) {
|
||||
filtered = filtered.filter((i) =>
|
||||
i.bizScopes.some((s) => s.includes(scope)),
|
||||
);
|
||||
}
|
||||
if (status) {
|
||||
filtered = filtered.filter((i) => i.status === status);
|
||||
}
|
||||
router.query = { ...router.query, ...values, current: 1, size: 10 };
|
||||
setData(filtered);
|
||||
};
|
||||
|
||||
/** 接接口后改为 queryQualMonitorPage 调用 */
|
||||
const handleSearch = () => {
|
||||
// queryQualMonitorPage(router.query);
|
||||
};
|
||||
|
||||
const handleReset = (values) => {
|
||||
router.query = { ...router.query, ...values, current: 1, size: 10 };
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue(router.query);
|
||||
// 接接口后打开下行注释
|
||||
// handleSearch();
|
||||
}, []);
|
||||
|
||||
const openDetail = (record) => {
|
||||
setDetailRecord(record);
|
||||
setDetailVisible(true);
|
||||
};
|
||||
|
||||
const resultTag = (result) => {
|
||||
if (result === "通过") return <Tag color="success">通过</Tag>;
|
||||
if (result === "预警" || result === "需核验") return <Tag color="warning">{result}</Tag>;
|
||||
if (result === "不通过" || result === "部分异常") return <Tag color="error">{result}</Tag>;
|
||||
return <Tag>{result}</Tag>;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "机构信息",
|
||||
dataIndex: "orgName",
|
||||
width: 240,
|
||||
fixed: "left",
|
||||
render: (name, record) => (
|
||||
<div className="org-info">
|
||||
<div className="org-name">{name}</div>
|
||||
<div className="org-meta">
|
||||
统一社会信用代码:{record.creditCode} | 注册地:{record.registerArea}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "具备资质/业务范围",
|
||||
dataIndex: "bizScopes",
|
||||
width: 260,
|
||||
render: (scopes, record) => (
|
||||
<div className="biz-scope-wrap">
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 4 }}>
|
||||
{scopes.map((s, i) => (
|
||||
<Tag key={i} color="blue">{s}</Tag>
|
||||
))}
|
||||
</div>
|
||||
<div className="biz-scope-hint">
|
||||
业务范围数:{record.bizScopeCount};最低专职人员要求:{record.minStaffRequire} 人
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
dataIndex: "status",
|
||||
width: 110,
|
||||
render: (status) => {
|
||||
const config = QUAL_MONITOR_STATUS_MAP[status];
|
||||
return config ? <Tag color={config.color}>{config.label}</Tag> : "--";
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "固定资产/场所",
|
||||
dataIndex: "fixedAsset",
|
||||
width: 260,
|
||||
render: (_, record) => (
|
||||
<div>
|
||||
<div>固定资产 {record.fixedAsset};工作场所 {record.workplace};档案室 {record.archiveRoom}</div>
|
||||
<div className={`place-status ${record.assetOk && record.workplaceOk ? "place-ok" : "place-bad"}`}>
|
||||
{record.assetOk && record.workplaceOk ? "均满足资质条件" : "三项基础条件低于要求"}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "人员与负责人",
|
||||
dataIndex: "staffCount",
|
||||
width: 300,
|
||||
render: (_, record) => (
|
||||
<div>
|
||||
专职技术人员 {record.staffCount} 人;中级及以上注册安全工程师 {record.midSafeEngineer} 人/{record.midSafeRatio};
|
||||
高级职称 {record.seniorTitle} 人/{record.seniorRatio};合计 {record.totalRatio};
|
||||
技术负责人 {record.techLeader},过程控制负责人 {record.processLeader}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "制度/网站/信用",
|
||||
dataIndex: "systemStatus",
|
||||
width: 220,
|
||||
render: (_, record) => (
|
||||
<div>
|
||||
{record.systemStatus};{record.promiseStatus};{record.websiteStatus};{record.creditStatus}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "来源字段",
|
||||
dataIndex: "sourceFields",
|
||||
width: 200,
|
||||
render: (fields) => (
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 4 }}>
|
||||
{fields.map((f, i) => (
|
||||
<span key={i} className="source-pill">{f}</span>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 120,
|
||||
fixed: "right",
|
||||
render: (_, record) => (
|
||||
<TableAction>
|
||||
<Button type="primary" size="small" onClick={() => openDetail(record)}>
|
||||
条件详情
|
||||
</Button>
|
||||
</TableAction>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageLayout title="资质保持监控">
|
||||
<div className="qual-monitor">
|
||||
|
||||
|
||||
{/* 统计卡片 */}
|
||||
<div className="stat-grid">
|
||||
{[
|
||||
{ label: "备案机构总数", value: stats.total, hint: "来源:已备案机构管理.机构状态", color: "#1e293b", iconBg: "#ede9fe", iconColor: "#7c3aed", iconText: "机" },
|
||||
{ label: "条件保持正常", value: stats.normal, hint: "十项资质条件均通过", color: "#059669", iconBg: "#d1fae5", iconColor: "#059669", iconText: "正" },
|
||||
{ label: "条件预警机构", value: stats.warning, hint: "人员比例、属地材料待核验", color: "#d97706", iconBg: "#fef3c7", iconColor: "#d97706", iconText: "预" },
|
||||
|
||||
].map((card, idx) => (
|
||||
<div key={idx} className="stat-card">
|
||||
<div className="stat-info">
|
||||
<div className="stat-label">{card.label}</div>
|
||||
<div className="stat-value" style={{ color: card.color }}>{card.value}</div>
|
||||
<div className="stat-hint">{card.hint}</div>
|
||||
</div>
|
||||
<div className="stat-icon" style={{ background: card.iconBg, color: card.iconColor }}>
|
||||
{card.iconText}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 资质条件来源说明 */}
|
||||
<div className="source-note">
|
||||
<strong>资质条件来源:</strong>
|
||||
依据安全评价机构资质条件,将机构备案信息拆为固定资产不少于 800 万元、工作场所不少于 1000 平方米、档案室不少于 100 平方米、
|
||||
专职技术人员数量及比例、技术负责人/过程控制负责人、内部制度、法定代表人承诺、公开网站、三年重大违法失信记录、注册地属地满足情况等字段。
|
||||
</div>
|
||||
|
||||
{/* 搜索表单 */}
|
||||
<SearchForm
|
||||
style={{ marginBottom: 12 }}
|
||||
form={form}
|
||||
loading={qualReviewLoading}
|
||||
formLine={[
|
||||
<Form.Item key="keyword" name="keyword">
|
||||
<ControlWrapper.Input
|
||||
label="机构名称/信用代码"
|
||||
placeholder="输入机构名称或统一社会信用代码"
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="scope" name="scope">
|
||||
<ControlWrapper.Select
|
||||
label="业务范围"
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
options={SCOPE_OPTIONS.map((o) => ({ label: o.label, value: o.value }))}
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="status" name="status">
|
||||
<ControlWrapper.Select
|
||||
label="资质状态"
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
options={STATUS_OPTIONS.map((o) => ({ label: o.label, value: o.value }))}
|
||||
/>
|
||||
</Form.Item>,
|
||||
]}
|
||||
onReset={handleReset}
|
||||
onFinish={onFinish}
|
||||
/>
|
||||
|
||||
|
||||
{/* 数据表格 */}
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
scroll={{ x: 1600, y: props.scrollY }}
|
||||
loading={qualReviewLoading}
|
||||
pagination={{
|
||||
total: data.length,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
current: router.query.current || 1,
|
||||
pageSize: router.query.size || 10,
|
||||
onChange: (page, size) => {
|
||||
router.query = { ...router.query, current: page, size };
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 条件详情弹窗 */}
|
||||
<Modal
|
||||
title={detailRecord ? `资质保持条件详情 - ${detailRecord.orgName}` : ""}
|
||||
open={detailVisible}
|
||||
onCancel={() => setDetailVisible(false)}
|
||||
width={960}
|
||||
footer={
|
||||
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
|
||||
<Button onClick={() => setDetailVisible(false)}>关闭</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{detailRecord && (
|
||||
<div>
|
||||
<div className="check-note">
|
||||
<strong>机构核查口径:</strong>以机构为主线,将资质条件拆为十项保持条件和属地要求;
|
||||
每项展示监管字段、当前值、结论和来源。
|
||||
</div>
|
||||
<Table
|
||||
rowKey="key"
|
||||
size="small"
|
||||
pagination={false}
|
||||
columns={[
|
||||
{ title: "检查项", dataIndex: "label", width: 120 },
|
||||
{ title: "资质要求", dataIndex: "require", width: 280 },
|
||||
{ title: "当前值", dataIndex: "value", width: 260 },
|
||||
{ title: "结论", dataIndex: "result", width: 80, render: resultTag },
|
||||
{ title: "来源字段", dataIndex: "source", render: (text) => <span className="cell-source">{text}</span> },
|
||||
]}
|
||||
dataSource={CONDITION_ITEMS.map((item) => {
|
||||
const detail = detailRecord.conditionDetails[item.key];
|
||||
return detail ? { key: item.key, label: item.label, ...detail } : null;
|
||||
}).filter(Boolean)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
</div>
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Connect([NS_QUAL_REVIEW], true)(AntdTableFuncControl(QualMonitor));
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
.qual-monitor {
|
||||
// 页面描述条
|
||||
.page-desc {
|
||||
font-size: 0.82rem;
|
||||
color: #64748b;
|
||||
margin-bottom: 16px;
|
||||
padding: 8px 12px;
|
||||
background: #f8fafc;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
// 统计卡片网格
|
||||
.stat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
// 统计卡片
|
||||
.stat-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.stat-info {
|
||||
.stat-label {
|
||||
font-size: 0.82rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.stat-hint {
|
||||
font-size: 0.75rem;
|
||||
color: #64748b;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 资质条件来源说明
|
||||
.source-note {
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 8px 14px;
|
||||
font-size: 0.82rem;
|
||||
color: #64748b;
|
||||
margin-bottom: 10px;
|
||||
|
||||
strong {
|
||||
color: #1e293b;
|
||||
}
|
||||
}
|
||||
|
||||
// 机构信息
|
||||
.org-info {
|
||||
.org-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.org-meta {
|
||||
font-size: 0.72rem;
|
||||
color: #64748b;
|
||||
}
|
||||
}
|
||||
|
||||
// 业务范围标签
|
||||
.biz-scope-wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
|
||||
.biz-scope-hint {
|
||||
font-size: 0.72rem;
|
||||
color: #64748b;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
// 场所状态
|
||||
.place-status {
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.place-ok {
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.place-bad {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
// 来源字段药丸
|
||||
.source-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 2px 7px;
|
||||
border-radius: 999px;
|
||||
background: #f1f5f9;
|
||||
color: #475569;
|
||||
font-size: 0.68rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// 核查口径说明
|
||||
.check-note {
|
||||
margin-bottom: 12px;
|
||||
font-size: 0.86rem;
|
||||
color: #64748b;
|
||||
padding: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
// 条件详情表格
|
||||
.detail-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.82rem;
|
||||
background: #fff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
|
||||
th {
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
text-align: left;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.cell-source {
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.cell-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue