修改bug
parent
a1ce3edda0
commit
7989d62a1c
1913
dev-stdout.log
1913
dev-stdout.log
File diff suppressed because it is too large
Load Diff
|
|
@ -14,8 +14,8 @@ module.exports = {
|
|||
|
||||
// API_HOST: "http://192.168.0.134",
|
||||
// API_HOST: "http://192.168.0.150", //太浅
|
||||
API_HOST: "https://gbs-gateway.qhdsafety.com",
|
||||
//API_HOST: "http://192.168.0.103", //huwei
|
||||
// API_HOST: "https://gbs-gateway.qhdsafety.com",
|
||||
API_HOST: "http://192.168.0.103", //huwei
|
||||
},
|
||||
production: {
|
||||
// 应用后端分支名称,部署上线需要
|
||||
|
|
|
|||
1928
pnpm-lock.yaml
1928
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -30,6 +30,13 @@ export const reviewSubmit = declareRequest(
|
|||
"Post > @/safetyEval/qual-filing/review",
|
||||
);
|
||||
|
||||
// 2026-08-14 新增:专家核验(监管端资质备案初审)
|
||||
// 仅更新专家核验结果(expert_result)与专家审核意见(expert_opinion),不修改备案状态
|
||||
export const expertReviewSubmit = declareRequest(
|
||||
"qualReviewSubmitLoading",
|
||||
"Post > @/safetyEval/qual-filing/expert-review",
|
||||
);
|
||||
|
||||
// ─── 变更模式 ───
|
||||
|
||||
export const fetchQualChangeDetail = declareRequest(
|
||||
|
|
|
|||
|
|
@ -49,13 +49,13 @@ export const FILING_STATUS_MAP = {
|
|||
export const FILING_STATUS_MAP_CHANGE = {
|
||||
1: { label: "审核通过", color: "success" },
|
||||
3: { label: "打回", color: "error" },
|
||||
4: { label: "待审核", color: "processing" },
|
||||
// 2026-08-14 变更:移除「待审核(4)」展示映射(监管端备案变更管理不再展示待审核)
|
||||
};
|
||||
|
||||
// 2026-08-14 变更:移除「待审核(4)」选项(监管端备案变更管理不再提供待审核筛选)
|
||||
export const CHANGE_STATUS_OPTIONS = [
|
||||
{ label: "审核通过", value: 1 },
|
||||
{ label: "打回", value: 3 },
|
||||
{ label: "待审核", value: 4 },
|
||||
];
|
||||
|
||||
/** 资质保持监控状态 */
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
Tag,
|
||||
Button,
|
||||
Form,
|
||||
message,
|
||||
Modal,
|
||||
Space,
|
||||
Typography,
|
||||
|
|
@ -15,6 +16,7 @@ import {
|
|||
Image,
|
||||
} from "antd";
|
||||
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
|
||||
import { DownloadOutlined } from "@ant-design/icons";
|
||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||
import SearchForm from "~/components/SearchForm";
|
||||
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
|
||||
|
|
@ -22,13 +24,123 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
|||
import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
|
||||
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||
import {
|
||||
CLOCK_IN_STATUS_OPTIONS,
|
||||
ROLE_DEFINITIONS_MAP,
|
||||
} from "~/enumerate/constant";
|
||||
import "./index.less";
|
||||
|
||||
// ============ 变更记录(2026-08-14 监管端需求 V2.1 原型):踏勘监管页面改造 ============
|
||||
// 变更原因:原页面查询项/弹窗与 V2.1 监管平台原型不符,按原型补查询项、改造告知书/打卡弹窗。
|
||||
// 变更说明:
|
||||
// 1) 查询项新增"评价机构(orgName 模糊搜索)"、"告知书状态(notificationStatus)"、
|
||||
// "踏勘日期"(复用后端 plan_end_date,入参 planEndDateStart/End)。
|
||||
// 2) 告知书按钮由直接打开文件改为 Modal 展示核验信息(告知书状态、踏勘日期、归档文件+下载)。
|
||||
// 3) 打卡弹窗新增"导出打卡记录"按钮(前端 CSV 导出,无需后端接口)。
|
||||
// ---- 2026-08-14 第二轮(prototype-to-code 增量回归,按原型精确对齐)----
|
||||
// 4) 告知书状态下拉改为原型三态:盖章件完整(COMPLETE)/待上传(PENDING)/超期告知(OVERDUE)。
|
||||
// 5) 打卡状态下拉改为原型四态:正常(NORMAL)/人员未到齐(NOT_ALL_PRESENT)/缺签到(MISSING_SIGN)/异常(ABNORMAL)。
|
||||
// 6) 查询项"计划踏勘时间"文案改为原型"踏勘日期起/止"。
|
||||
// 7) 告知书弹窗标题改为"从业告知详情-{项目名}"(已排除项目组成员/机构盖章/版本一致性及"告知书归档"卡片项)。
|
||||
// 8) 打卡弹窗补充原型"允许打卡范围"说明;列表表头"实到/应到"改为原型"应到/实到"。
|
||||
// 影响范围:仅监管端 /MonitorManage/SurveyMonitor 页面。
|
||||
// 后端同步修改(2026-08-14 第二轮已落地):
|
||||
// - RegulatorEvalProjectSurveyPageQuery:orgName、notificationStatus(三态)、clockInStatus(四态);
|
||||
// - RegulatorEvalProjectSurveyPageCO:新增 planEndDate;notificationStatus 枚举为三态;
|
||||
// - EvalProjectMapper.xml selectRegulatorSurveyPage:
|
||||
// orgName LIKE o.unit_name;notificationStatus 三态(OVERDUE 需 plan_end_date<CURDATE);
|
||||
// clockInStatus 四态(MISSING_SIGN 映射 NOT_CLOCKED_IN,ABNORMAL 为兜底);
|
||||
// 主查询返回 plan_end_date;
|
||||
// - RegulatorEvalProjectMonitorExecutor:告知书状态统一在 Java 层按三态计算并回填 planEndDate。
|
||||
// ======================================================================================
|
||||
|
||||
const { router } = tools;
|
||||
|
||||
// ============ 2026-08-14 第二轮:按原型对齐可选项与弹窗 ============
|
||||
// 变更原因:V2.1 原型查询可选项为【告知状态:盖章件完整/待上传/超期告知】【打卡状态:正常/人员未到齐/缺签到/异常】,
|
||||
// 与上一轮"已归档/未归档"两态不一致,按原型与用户确认改为三态/四态,并同步修改后端对齐。
|
||||
// 告知书状态(后端 notificationStatus):
|
||||
// COMPLETE = 盖章件完整(存在 PRACTICE_NOTIFICATION 附件)
|
||||
// PENDING = 待上传(无附件且未超期)
|
||||
// OVERDUE = 超期告知(无附件且计划踏勘时间已过,由后端 plan_end_date < 当前日期判定)
|
||||
// 打卡状态(后端 clockInStatus,由 clock_in_status 三态映射为四态):
|
||||
// NORMAL=正常 NOT_ALL_PRESENT=人员未到齐 MISSING_SIGN=缺签到 ABNORMAL=异常
|
||||
// ======================================================================
|
||||
const NOTIFICATION_STATUS_OPTIONS = [
|
||||
{ label: "盖章件完整", value: "COMPLETE" },
|
||||
{ label: "待上传", value: "PENDING" },
|
||||
{ label: "超期告知", value: "OVERDUE" },
|
||||
];
|
||||
|
||||
// 告知书状态前端展示映射(文案/颜色与原型一致)
|
||||
const NOTIFICATION_STATUS_MAP = {
|
||||
COMPLETE: { text: "盖章件完整", color: "success" },
|
||||
PENDING: { text: "待上传", color: "warning" },
|
||||
OVERDUE: { text: "超期告知", color: "warning" },
|
||||
};
|
||||
|
||||
// 打卡状态下拉选项(对应后端 clockInStatus 四态映射)
|
||||
const CLOCK_IN_STATUS_FILTER_OPTIONS = [
|
||||
{ label: "正常", value: "NORMAL" },
|
||||
{ label: "人员未到齐", value: "NOT_ALL_PRESENT" },
|
||||
{ label: "缺签到", value: "MISSING_SIGN" },
|
||||
{ label: "异常", value: "ABNORMAL" },
|
||||
];
|
||||
|
||||
/** 获取告知书归档状态:优先使用后端返回的 notificationStatus,兜底按附件与计划时间推断 */
|
||||
const getNotificationStatus = (record) => {
|
||||
if (record?.notificationStatus) return record.notificationStatus;
|
||||
const hasFile = parseFileList(record?.notificationFileUrl).length > 0;
|
||||
if (hasFile) return "COMPLETE";
|
||||
// 无附件且计划踏勘时间早于今天 → 超期告知;否则待上传
|
||||
const planEnd = record?.planEndDate ? new Date(record.planEndDate) : null;
|
||||
const today = new Date();
|
||||
if (planEnd && planEnd < today) return "OVERDUE";
|
||||
return "PENDING";
|
||||
};
|
||||
|
||||
/**
|
||||
* 2026-08-14 加固:归一化告知书状态为原型三态,兼容后端可能返回的旧枚举值
|
||||
* ARCHIVED→COMPLETE(盖章件完整)、NOT_ARCHIVED→PENDING(待上传),未知值兜底 PENDING。
|
||||
*/
|
||||
const normalizeNotificationStatus = (status) => {
|
||||
const map = {
|
||||
ARCHIVED: "COMPLETE",
|
||||
NOT_ARCHIVED: "PENDING",
|
||||
COMPLETE: "COMPLETE",
|
||||
PENDING: "PENDING",
|
||||
OVERDUE: "OVERDUE",
|
||||
};
|
||||
return map[status] || "PENDING";
|
||||
};
|
||||
|
||||
/**
|
||||
* 2026-08-14 修复:解析项目角色字段为数组。
|
||||
* 后端返回的 role 是 JSON 数组字符串(如 '["PROCESS_CONTROL_LEAD"]')而非数组,
|
||||
* 导致原 Array.isArray 判断失效、英文编码原样展示。此处统一兼容"数组/JSON字符串/普通字符串"三种形态。
|
||||
*/
|
||||
const parseRoleList = (v) => {
|
||||
if (Array.isArray(v)) return v;
|
||||
if (typeof v === "string") {
|
||||
const trimmed = v.trim();
|
||||
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed);
|
||||
return Array.isArray(parsed) ? parsed : [parsed];
|
||||
} catch (e) {
|
||||
// 解析失败按逗号拆分兜底
|
||||
return trimmed.slice(1, -1).split(",").map((s) => s.trim()).filter(Boolean);
|
||||
}
|
||||
}
|
||||
return trimmed ? [trimmed] : [];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
/** 项目角色编码 → 中文展示(多角色用"、"连接),未匹配到则原样展示 */
|
||||
const roleText = (v) => {
|
||||
const list = parseRoleList(v);
|
||||
return list.map((r) => ROLE_DEFINITIONS_MAP[r] || r).join("、") || "-";
|
||||
};
|
||||
|
||||
const STAT_ITEMS = [
|
||||
{ key: "notificationArchivedCount", title: "告知书已归档", color: "#1677ff" },
|
||||
{ key: "clockInNormalCount", title: "人员打卡正常", color: "#52c41a" },
|
||||
|
|
@ -47,15 +159,18 @@ const parseFileList = (raw) => {
|
|||
}
|
||||
};
|
||||
|
||||
/** 根据打卡数据计算状态 */
|
||||
/** 2026-08-14 第二轮:根据打卡数据按原型四态计算展示状态(正常/人员未到齐/缺签到/异常) */
|
||||
const getClockInStatus = (record) => {
|
||||
// 实到少于应到 → 人员未到齐
|
||||
if (record.actualCount < record.expectedCount) {
|
||||
return { color: "error", text: "人员未到齐" };
|
||||
}
|
||||
if (
|
||||
record.signInCount < record.actualCount ||
|
||||
record.signOutCount < record.actualCount
|
||||
) {
|
||||
// 到齐但存在缺签到或缺签退 → 缺签到
|
||||
if (record.signInCount < record.actualCount || record.signOutCount < record.actualCount) {
|
||||
return { color: "warning", text: "缺签到" };
|
||||
}
|
||||
// 其余异常情况(人脸等未通过)→ 异常
|
||||
if (record.signInCount === 0 && record.signOutCount === 0) {
|
||||
return { color: "warning", text: "异常" };
|
||||
}
|
||||
return { color: "success", text: "正常" };
|
||||
|
|
@ -68,6 +183,9 @@ const SurveyMonitor = (props) => {
|
|||
const [clockInModal, setClockInModal] = useState(false);
|
||||
const [clockInData, setClockInData] = useState([]);
|
||||
const [clockInProjectName, setClockInProjectName] = useState("");
|
||||
// 2026-08-14 新增:告知书核验弹窗状态(由直接打开文件改为弹窗展示)
|
||||
const [noticeModal, setNoticeModal] = useState(false);
|
||||
const [noticeRecord, setNoticeRecord] = useState(null);
|
||||
|
||||
const { safetyEvalBusiness, evalSurveyMonitorStat, evalSurveyMonitorPage,
|
||||
evalSurveyClockIn } = props;
|
||||
|
|
@ -103,6 +221,14 @@ const SurveyMonitor = (props) => {
|
|||
const handleSearch = (values) => {
|
||||
router.query = {
|
||||
...values,
|
||||
// 2026-08-14 修复:DatePicker 返回 dayjs 对象,需格式化为 yyyy-MM-dd 字符串再传给后端,
|
||||
// 否则后端 @DateTimeFormat(pattern="yyyy-MM-dd") 无法解析导致踏勘日期筛选失效
|
||||
planEndDateStart: values.planEndDateStart
|
||||
? values.planEndDateStart.format("YYYY-MM-DD")
|
||||
: undefined,
|
||||
planEndDateEnd: values.planEndDateEnd
|
||||
? values.planEndDateEnd.format("YYYY-MM-DD")
|
||||
: undefined,
|
||||
current: 1,
|
||||
size: 10,
|
||||
};
|
||||
|
|
@ -133,13 +259,51 @@ const SurveyMonitor = (props) => {
|
|||
}
|
||||
};
|
||||
|
||||
// 2026-08-14 改造:告知书由直接打开文件改为弹窗展示核验信息(含归档文件下载)
|
||||
const handleViewNotice = (record) => {
|
||||
const files = parseFileList(record.notificationFileUrl);
|
||||
setNoticeRecord(record);
|
||||
setNoticeModal(true);
|
||||
};
|
||||
|
||||
// 下载告知书归档文件
|
||||
const handleDownloadNotice = (record) => {
|
||||
const files = parseFileList(record?.notificationFileUrl);
|
||||
if (files.length > 0 && files[0].url) {
|
||||
window.open(files[0].url, "_blank");
|
||||
}
|
||||
};
|
||||
|
||||
// 2026-08-14 新增:导出打卡记录(前端 CSV 导出,无需后端接口)
|
||||
// 字段对应原型打卡弹窗表格:人员/项目角色/人脸识别/ GPS 定位/签到时间/签退时间/现场照片/本人签字
|
||||
const handleExportClockIn = () => {
|
||||
if (!clockInData.length) {
|
||||
message.warning("暂无打卡数据可导出");
|
||||
return;
|
||||
}
|
||||
const header = [
|
||||
"人员", "项目角色", "人脸识别", "GPS定位", "签到时间", "签退时间", "现场照片", "本人签字",
|
||||
];
|
||||
const rows = clockInData.map((row) => [
|
||||
row.userName || "-",
|
||||
// 2026-08-14 修复:复用全局 roleText,兼容后端 JSON 数组字符串
|
||||
roleText(row.role),
|
||||
row.faceState === 1 ? "通过" : "未通过",
|
||||
row.gpsState === 1 ? "正常" : "异常",
|
||||
row.singInTime || "-",
|
||||
row.signOutTime || "-",
|
||||
row.sceneUrl ? row.sceneUrl.split(",").filter(Boolean).length + "张" : "-",
|
||||
row.signatureUrl ? "已签字" : "未签字",
|
||||
]);
|
||||
// 构造 CSV(含 BOM 头,保证 Excel 正确识别中文)
|
||||
const csv = "" + [header, ...rows].map((r) => r.join(",")).join("\r\n");
|
||||
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = `${clockInProjectName || "项目"}_打卡记录.csv`;
|
||||
link.click();
|
||||
URL.revokeObjectURL(link.href);
|
||||
};
|
||||
|
||||
const clockInColumns = [
|
||||
{ title: "人员", dataIndex: "userName", width: 100 },
|
||||
{
|
||||
|
|
@ -147,10 +311,8 @@ const SurveyMonitor = (props) => {
|
|||
dataIndex: "role",
|
||||
width: 160,
|
||||
ellipsis: true,
|
||||
render: (v) =>
|
||||
Array.isArray(v)
|
||||
? v.map((r) => ROLE_DEFINITIONS_MAP[r] || r).join("、")
|
||||
: v || "-",
|
||||
// 2026-08-14 修复:兼容后端 role 为 JSON 数组字符串,映射为中文角色名
|
||||
render: (v) => roleText(v),
|
||||
},
|
||||
{
|
||||
title: "人脸识别",
|
||||
|
|
@ -213,21 +375,21 @@ const SurveyMonitor = (props) => {
|
|||
{ title: "评价机构", dataIndex: "orgName", ellipsis: true, width: 200 },
|
||||
{
|
||||
title: "告知书状态",
|
||||
dataIndex: "notificationFileUrl",
|
||||
dataIndex: "notificationStatus",
|
||||
width: 120,
|
||||
render: (v) => {
|
||||
const files = parseFileList(v);
|
||||
return files.length > 0 ? (
|
||||
<Tag color="success">已归档</Tag>
|
||||
) : (
|
||||
<Tag color="warning">待上传</Tag>
|
||||
);
|
||||
render: (notificationStatus, record) => {
|
||||
// 2026-08-14 加固:兼容后端旧枚举,统一归一化为原型三态
|
||||
const raw = notificationStatus || getNotificationStatus(record);
|
||||
const status = normalizeNotificationStatus(raw);
|
||||
const map = NOTIFICATION_STATUS_MAP[status] || NOTIFICATION_STATUS_MAP.PENDING;
|
||||
return <Tag color={map.color}>{map.text}</Tag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "实到/应到",
|
||||
// 2026-08-14 第二轮:与原型表头"应到/实到"对齐
|
||||
title: "应到/实到",
|
||||
width: 100,
|
||||
render: (_, r) => `${r.actualCount || 0} / ${r.expectedCount || 0}`,
|
||||
render: (_, r) => `${r.expectedCount || 0} / ${r.actualCount || 0}`,
|
||||
},
|
||||
{
|
||||
title: "签到/签退",
|
||||
|
|
@ -291,6 +453,7 @@ const SurveyMonitor = (props) => {
|
|||
loading={false}
|
||||
style={{ marginBottom: 16 }}
|
||||
formLine={[
|
||||
// 项目名称/编号(原型查询项,后端 projectNameOrNo 模糊匹配)
|
||||
<Form.Item key="projectNameOrNo" name="projectNameOrNo">
|
||||
<ControlWrapper.Input
|
||||
label="项目名称/编号"
|
||||
|
|
@ -298,12 +461,37 @@ const SurveyMonitor = (props) => {
|
|||
allowClear
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="clockInStatus" name="clockInStatus">
|
||||
// 2026-08-14 新增:评价机构名称模糊查询(后端 orgName,对应 org_info.unit_name)
|
||||
<Form.Item key="orgName" name="orgName">
|
||||
<ControlWrapper.Input
|
||||
label="评价机构"
|
||||
placeholder="请输入机构名称"
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>,
|
||||
// 2026-08-14 第二轮:告知书状态(后端 notificationStatus:COMPLETE盖章件完整/PENDING待上传/OVERDUE超期告知)
|
||||
<Form.Item key="notificationStatus" name="notificationStatus">
|
||||
<ControlWrapper.Select
|
||||
label="打卡状态"
|
||||
label="告知状态"
|
||||
placeholder="全部"
|
||||
allowClear
|
||||
options={CLOCK_IN_STATUS_OPTIONS}
|
||||
options={NOTIFICATION_STATUS_OPTIONS}
|
||||
/>
|
||||
</Form.Item>,
|
||||
// 2026-08-14 新增:踏勘日期(复用后端 plan_end_date,入参 planEndDateStart/End,原型为"踏勘日期起/止")
|
||||
<Form.Item key="planEndDateStart" name="planEndDateStart">
|
||||
<ControlWrapper.DatePicker label="踏勘日期起" style={{ width: "100%" }} />
|
||||
</Form.Item>,
|
||||
<Form.Item key="planEndDateEnd" name="planEndDateEnd">
|
||||
<ControlWrapper.DatePicker label="踏勘日期止" style={{ width: "100%" }} />
|
||||
</Form.Item>,
|
||||
// 2026-08-14 第二轮:打卡状态四态(NORMAL正常/NOT_ALL_PRESENT人员未到齐/MISSING_SIGN缺签到/ABNORMAL异常)
|
||||
<Form.Item key="clockInStatus" name="clockInStatus">
|
||||
<ControlWrapper.Select
|
||||
label="人员打卡状态"
|
||||
placeholder="全部"
|
||||
allowClear
|
||||
options={CLOCK_IN_STATUS_FILTER_OPTIONS}
|
||||
/>
|
||||
</Form.Item>,
|
||||
]}
|
||||
|
|
@ -332,7 +520,20 @@ const SurveyMonitor = (props) => {
|
|||
title={`人员打卡详情 - ${clockInProjectName}`}
|
||||
open={clockInModal}
|
||||
onCancel={() => setClockInModal(false)}
|
||||
footer={null}
|
||||
// 2026-08-14 新增:导出打卡记录按钮(原型要求)
|
||||
footer={[
|
||||
<Button
|
||||
key="export"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={handleExportClockIn}
|
||||
disabled={!clockInData.length}
|
||||
>
|
||||
导出打卡记录
|
||||
</Button>,
|
||||
<Button key="close" onClick={() => setClockInModal(false)}>
|
||||
关闭
|
||||
</Button>,
|
||||
]}
|
||||
width={1000}
|
||||
>
|
||||
<Table
|
||||
|
|
@ -344,6 +545,71 @@ const SurveyMonitor = (props) => {
|
|||
loading={evalSurveyClockInLoading}
|
||||
scroll={{ x: 900 }}
|
||||
/>
|
||||
{/* 2026-08-14 第二轮:按原型补充打卡范围说明 */}
|
||||
<div style={{ marginTop: 12, color: "#888", fontSize: 13, lineHeight: 1.8 }}>
|
||||
<strong>允许打卡范围:</strong>
|
||||
项目地址周边500米;异常定位、缺签到或缺签退将自动进入项目预警。
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
{/* 2026-08-14 第二轮:告知书弹窗按原型 surveyNotice 还原(标题"从业告知详情";已排除项目组成员/机构盖章/版本一致性及"告知书归档"卡片项) */}
|
||||
<Modal
|
||||
title={`从业告知详情 - ${noticeRecord?.projectName || ""}`}
|
||||
open={noticeModal}
|
||||
onCancel={() => setNoticeModal(false)}
|
||||
footer={[
|
||||
<Button
|
||||
key="download"
|
||||
type="primary"
|
||||
icon={<DownloadOutlined />}
|
||||
disabled={!parseFileList(noticeRecord?.notificationFileUrl).length}
|
||||
onClick={() => handleDownloadNotice(noticeRecord)}
|
||||
>
|
||||
下载告知书
|
||||
</Button>,
|
||||
<Button key="close" onClick={() => setNoticeModal(false)}>
|
||||
关闭
|
||||
</Button>,
|
||||
]}
|
||||
width={640}
|
||||
>
|
||||
{noticeRecord && (() => {
|
||||
// 2026-08-14 修复:兼容后端可能返回的旧枚举值(ARCHIVED/NOT_ARCHIVED),统一归一化到原型三态,避免 undefined 取色报错
|
||||
const status = normalizeNotificationStatus(getNotificationStatus(noticeRecord));
|
||||
const statusMap = NOTIFICATION_STATUS_MAP[status] || NOTIFICATION_STATUS_MAP.PENDING;
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
dataSource={[
|
||||
{
|
||||
key: "status",
|
||||
item: "告知书状态",
|
||||
value: <Tag color={statusMap.color}>{statusMap.text}</Tag>,
|
||||
},
|
||||
// 踏勘日期:复用后端 plan_end_date(原型为"计划踏勘时间")
|
||||
{
|
||||
key: "planTime",
|
||||
item: "计划踏勘时间",
|
||||
value: noticeRecord.planEndDate || "-",
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{ title: "核验项", dataIndex: "item", width: 140 },
|
||||
{ title: "内容", dataIndex: "value" },
|
||||
]}
|
||||
rowKey="key"
|
||||
pagination={false}
|
||||
size="small"
|
||||
/>
|
||||
<div style={{ marginTop: 16, color: "#888", fontSize: 13, lineHeight: 1.8 }}>
|
||||
归档文件:
|
||||
{parseFileList(noticeRecord.notificationFileUrl).length > 0
|
||||
? "已上传从业告知书,可点击右上角「下载告知书」查看归档文件。"
|
||||
: "暂未上传从业告知书归档文件。"}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</Modal>
|
||||
</PageLayout>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Button, Form, Select, Table, Tag } from "antd";
|
||||
import { Button, Form, Select, Table, Tag, Tooltip } from "antd";
|
||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||
import SearchForm from "~/components/SearchForm";
|
||||
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||
|
|
@ -8,11 +8,10 @@ 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 { VERIFY_STATUS_MAP } from "~/enumerate/constant";
|
||||
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP } from "~/enumerate/enterpriseOptions";
|
||||
import {
|
||||
FILING_CODE_TO_CONFIRM,
|
||||
REVIEW_BUSINESS_SCOPE_OPTIONS,
|
||||
REVIEW_RESULT_OPTIONS,
|
||||
REVIEW_STATUS_CLASS,
|
||||
REVIEW_UNIT_TYPE_OPTIONS,
|
||||
} from "../reviewPageQuery";
|
||||
|
|
@ -58,20 +57,15 @@ const ExpertVerification = (props) => {
|
|||
loadList();
|
||||
}, []);
|
||||
|
||||
// 2026-08-14 修复:确认状态列与搜索列对齐(基于 filingStatusCode:2待确认/1确认通过/3打回)
|
||||
const renderConfirmStatus = (record) => {
|
||||
const name = record.filingStatusName || record.statusName;
|
||||
if (name) {
|
||||
const color = /待确认|待核验/.test(name)
|
||||
? "warning"
|
||||
: /确认通过|已审核|通过/.test(name)
|
||||
? "success"
|
||||
: /打回|退回/.test(name)
|
||||
? "error"
|
||||
: "default";
|
||||
return <Tag className={REVIEW_STATUS_CLASS[color] || "tag_default"}>{name}</Tag>;
|
||||
}
|
||||
const status = FILING_CODE_TO_CONFIRM[Number(record.filingStatusCode)];
|
||||
const config = VERIFY_STATUS_MAP[status];
|
||||
const status = Number(record.filingStatusCode);
|
||||
const CONF = {
|
||||
1: { label: "确认通过", color: "success" },
|
||||
2: { label: "待确认", color: "warning" },
|
||||
3: { label: "打回", color: "error" },
|
||||
};
|
||||
const config = CONF[status];
|
||||
return config ? (
|
||||
<Tag className={REVIEW_STATUS_CLASS[config.color] || "tag_default"}>
|
||||
{config.label}
|
||||
|
|
@ -81,6 +75,28 @@ const ExpertVerification = (props) => {
|
|||
);
|
||||
};
|
||||
|
||||
// 2026-08-14 调整:专家核验列——优先展示 expert_result(结果),鼠标悬浮 Tooltip 展示 expert_opinion(意见)
|
||||
// 若结果为空但有意见,则展示意见内容。
|
||||
const renderExpertCheck = (record) => {
|
||||
const item = REVIEW_RESULT_OPTIONS.find((r) => r.value === Number(record.expertResult));
|
||||
const opinion = record.expertOpinion;
|
||||
// 结果与意见均无 → 显示 "-"
|
||||
if (!item && !opinion) return "-";
|
||||
const content = item ? (
|
||||
<Tag className={REVIEW_STATUS_CLASS[item.value === 1 ? "success" : item.value === 3 ? "error" : "warning"] || "tag_default"}>
|
||||
{item.label}
|
||||
</Tag>
|
||||
) : (
|
||||
<span className="text-muted">待核验</span>
|
||||
);
|
||||
// 有意见时用 Tooltip 悬浮展示完整意见
|
||||
return opinion ? (
|
||||
<Tooltip title={opinion} overlayStyle={{ maxWidth: 320 }}>
|
||||
<span style={{ cursor: "help" }}>{content}</span>
|
||||
</Tooltip>
|
||||
) : content;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "序号",
|
||||
|
|
@ -105,6 +121,13 @@ const ExpertVerification = (props) => {
|
|||
return record.businessScope || "-";
|
||||
},
|
||||
},
|
||||
// 2026-08-14 调整:专家核验(优先展示 expertResult,悬浮 Tooltip 展示 expertOpinion)
|
||||
{
|
||||
title: "专家核验",
|
||||
dataIndex: "expertResult",
|
||||
width: 120,
|
||||
render: (_, record) => renderExpertCheck(record),
|
||||
},
|
||||
{
|
||||
title: "确认状态",
|
||||
dataIndex: "filingStatusCode",
|
||||
|
|
@ -210,7 +233,7 @@ const ExpertVerification = (props) => {
|
|||
columns={columns}
|
||||
dataSource={Array.isArray(qualReviewList) ? qualReviewList : []}
|
||||
rowSelection={{}}
|
||||
scroll={{ x: 1000, y: props.scrollY }}
|
||||
scroll={{ x: 1400, y: props.scrollY }}
|
||||
loading={qualReviewLoading}
|
||||
pagination={{
|
||||
total: qualReviewTotal,
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ const QualChange = (props) => {
|
|||
dataIndex: "changeItemNames",
|
||||
width: 160,
|
||||
ellipsis: true,
|
||||
// 后端列表 CO 未聚合变更项(明细在 qual_filing_change_detail.change_item_name),待后端补充;mock 有值
|
||||
// 2026-08-14 说明:后端已聚合变更项名称(qual_filing_change_detail.change_item_name 逗号连接)到 changeItemNames 字段
|
||||
render: (v) => v || "-",
|
||||
},
|
||||
{
|
||||
|
|
@ -102,8 +102,8 @@ const QualChange = (props) => {
|
|||
>
|
||||
查看
|
||||
</Button>
|
||||
{/* 仅审核中/变更审核中可审核 */}
|
||||
{[2, 4].includes(Number(record.filingStatusCode)) && (
|
||||
{/* 2026-08-14 变更:移除「待审核(4)」的审核操作,仅保留审核中(2)可审核 */}
|
||||
{[2].includes(Number(record.filingStatusCode)) && (
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { Button, Form, Select, Table, Tag } from "antd";
|
||||
import { Button, Form, Select, Table, Tag, Tooltip } from "antd";
|
||||
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||
import SearchForm from "~/components/SearchForm";
|
||||
|
|
@ -9,9 +9,10 @@ import { tools } from "@cqsjjb/jjb-common-lib";
|
|||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import { NS_QUAL_REVIEW } from "~/enumerate/namespace";
|
||||
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP } from "~/enumerate/enterpriseOptions";
|
||||
import { EXPERT_STATUS_MAP, REVIEW_STATUS_MAP } from "~/enumerate/constant";
|
||||
import { REVIEW_STATUS_MAP } from "~/enumerate/constant";
|
||||
import {
|
||||
REVIEW_BUSINESS_SCOPE_OPTIONS,
|
||||
REVIEW_RESULT_OPTIONS,
|
||||
REVIEW_STATUS_CLASS,
|
||||
REVIEW_UNIT_TYPE_OPTIONS,
|
||||
} from "../reviewPageQuery";
|
||||
|
|
@ -57,20 +58,28 @@ const QualReview = (props) => {
|
|||
|
||||
|
||||
|
||||
const renderExpertStatus = (record) => {
|
||||
if (!record.needExpertCheck) {
|
||||
return "-";
|
||||
}
|
||||
const config = EXPERT_STATUS_MAP[record.expertStatus];
|
||||
return config ? (
|
||||
<Tag className={REVIEW_STATUS_CLASS[config.color] || "tag_default"}>
|
||||
{config.label}
|
||||
// 2026-08-14 调整:专家核验列——优先展示 expert_result(结果),鼠标悬浮 Tooltip 展示 expert_opinion(意见)
|
||||
const renderExpertCheck = (record) => {
|
||||
const item = REVIEW_RESULT_OPTIONS.find((r) => r.value === Number(record.expertResult));
|
||||
const opinion = record.expertOpinion;
|
||||
if (!item && !opinion) return "-";
|
||||
const content = item ? (
|
||||
<Tag className={REVIEW_STATUS_CLASS[item.value === 1 ? "success" : item.value === 3 ? "error" : "warning"] || "tag_default"}>
|
||||
{item.label}
|
||||
</Tag>
|
||||
) : (
|
||||
"-"
|
||||
<span className="text-muted">待核验</span>
|
||||
);
|
||||
return opinion ? (
|
||||
<Tooltip title={opinion} overlayStyle={{ maxWidth: 320 }}>
|
||||
<span style={{ cursor: "help" }}>{content}</span>
|
||||
</Tooltip>
|
||||
) : content;
|
||||
};
|
||||
|
||||
// 2026-08-14 新增:综合审核意见展示
|
||||
const renderOpinion = (text) => (text ? <span title={text}>{text}</span> : "-");
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "序号",
|
||||
|
|
@ -95,11 +104,12 @@ const QualReview = (props) => {
|
|||
return record.businessScope || "-";
|
||||
},
|
||||
},
|
||||
// 2026-08-14 调整:专家核验(优先展示 expertResult,悬浮 Tooltip 展示 expertOpinion),保留一列
|
||||
{
|
||||
title: "专家核验",
|
||||
dataIndex: "expertStatus",
|
||||
width: 100,
|
||||
render: (_, record) => renderExpertStatus(record),
|
||||
dataIndex: "expertResult",
|
||||
width: 120,
|
||||
render: (_, record) => renderExpertCheck(record),
|
||||
},
|
||||
{
|
||||
title: "审核状态",
|
||||
|
|
@ -110,6 +120,14 @@ const QualReview = (props) => {
|
|||
return config ? <Tag color={config.color}>{config.label}</Tag> : '--';
|
||||
},
|
||||
},
|
||||
// 2026-08-14 新增:综合审核意见(qual_filing_review.review_opinion,列表聚合回填)
|
||||
{
|
||||
title: "综合审核意见",
|
||||
dataIndex: "reviewOpinion",
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
render: (text) => renderOpinion(text),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 140,
|
||||
|
|
@ -217,7 +235,7 @@ const QualReview = (props) => {
|
|||
columns={columns}
|
||||
dataSource={Array.isArray(qualReviewList) ? qualReviewList : []}
|
||||
rowSelection={{}}
|
||||
scroll={{ x: 1000, y: props.scrollY }}
|
||||
scroll={{ x: 1500, y: props.scrollY }}
|
||||
loading={qualReviewLoading}
|
||||
pagination={{
|
||||
total: qualReviewTotal,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useMemo } from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { Button, Form, Input, message, Modal, Select } from "antd";
|
||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import { NS_QUAL_EXPERT, NS_QUAL_REVIEW } from "~/enumerate/namespace";
|
||||
|
|
@ -7,14 +7,22 @@ import { REVIEW_RESULT_OPTIONS } from "../reviewPageQuery";
|
|||
const { TextArea } = Input;
|
||||
|
||||
/**
|
||||
* 审核操作弹窗(原型 modal-review-action)
|
||||
* - 评价分析:已核验材料 / 符合 / 不符合 / 条件未满足 四卡
|
||||
* - 表单:审核结果(通过/待定/不通过)、指派专家、综合审核意见
|
||||
* - 底部:取消 / 📤 提交 / ✅ 通过
|
||||
* 审核操作弹窗(原型 modal-review-action / modal-expert-review-act)
|
||||
*
|
||||
* 按 mode 差异化三种审核场景(2026-08-14 按原型与需求重构):
|
||||
* 1) 资质备案初审(mode 为空):审核结果(通过/待审核/不通过) + 指派专家 + 综合审核意见,走原 reviewSubmit;
|
||||
* 2) 备案变更管理(mode=change):审核结果(通过/不通过) + 综合审核意见(无指派专家),走原 changeReviewSubmit;
|
||||
* 3) 专家资料核验(mode=expert):核验分析 + 核验意见(无审核结果下拉/无指派专家),走独立 expertReviewSubmit。
|
||||
*
|
||||
* 通用头部:📊 评价分析(已核验材料/符合/不符合/条件未满足 四卡)
|
||||
* 底部:取消 / 📤 提交 / ✅ 通过
|
||||
*
|
||||
* @param {boolean} open
|
||||
* @param {string|number} filingId - 备案申请ID
|
||||
* @param {boolean} isChange - 是否变更审核(兼容旧调用)
|
||||
* @param {string} mode - 路由 mode:expert=专家核验,change=备案变更,空/其它=资质备案初审
|
||||
* @param {function} onCancel
|
||||
* @param {function} onSuccess - 审核成功后回调(刷新列表等)
|
||||
* @param {function} onSuccess
|
||||
*/
|
||||
const statStyle = {
|
||||
background: "#fff",
|
||||
|
|
@ -25,11 +33,18 @@ const statStyle = {
|
|||
};
|
||||
const statLabelStyle = { fontSize: "0.7rem", color: "#64748b" };
|
||||
|
||||
// 2026-08-14 各场景审核结果选项:
|
||||
// - 资质备案初审:通过/待审核/不通过(后端 1/2/3)
|
||||
// - 备案变更管理:通过/不通过(后端 1/3)
|
||||
const QUAL_RESULT_OPTIONS = REVIEW_RESULT_OPTIONS; // 通过(1)/待审核(2)/不通过(3)
|
||||
const CHANGE_RESULT_OPTIONS = REVIEW_RESULT_OPTIONS.filter((r) => r.value !== 2); // 通过(1)/不通过(3)
|
||||
|
||||
const ReviewModal = (props) => {
|
||||
const {
|
||||
open,
|
||||
filingId,
|
||||
isChange = false,
|
||||
mode,
|
||||
legalPassCount = 0,
|
||||
legalFailCount = 0,
|
||||
legalWarnCount = 0,
|
||||
|
|
@ -37,63 +52,111 @@ const ReviewModal = (props) => {
|
|||
onSuccess,
|
||||
} = props;
|
||||
const {
|
||||
qualExpert,
|
||||
qualReview,
|
||||
queryExpertPage,
|
||||
reviewSubmit,
|
||||
changeReviewSubmit,
|
||||
expertReviewSubmit,
|
||||
} = props;
|
||||
const { qualExpertList } = qualExpert || {};
|
||||
// 2026-08-14 专家列表(指派专家下拉数据源,仅资质备案初审场景使用)
|
||||
const { qualExpertList, queryExpertPage } = props;
|
||||
const { qualReviewSubmitLoading } = qualReview || {};
|
||||
|
||||
// 场景判定:优先按 mode,mode 为空时按 isChange 兜底
|
||||
const isExpert = mode === "expert";
|
||||
const isChangeScene = isChange || mode === "change";
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const reviewResult = Form.useWatch("reviewResult", form);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
form.resetFields();
|
||||
queryExpertPage({ current: 1, size: 100 });
|
||||
// 2026-08-14 资质备案初审(非专家/非变更)需要指派专家下拉:加载专家列表
|
||||
if (!isExpert && !isChangeScene) {
|
||||
queryExpertPage({ current: 1, size: 100 });
|
||||
}
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const expertOptions = useMemo(
|
||||
() =>
|
||||
(qualExpertList || []).map((item) => ({
|
||||
label: item.userName,
|
||||
value: item.id,
|
||||
expert: item,
|
||||
})),
|
||||
[qualExpertList],
|
||||
);
|
||||
// 提取后端错误信息,避免未捕获异常导致 [object Object] 崩溃
|
||||
const getErrMsg = (err) => {
|
||||
if (typeof err === "string") return err;
|
||||
if (err && typeof err === "object") {
|
||||
return (
|
||||
err.message ||
|
||||
err.errMessage ||
|
||||
err.msg ||
|
||||
(Array.isArray(err.errors) ? err.errors.map((e) => e.message).join(";") : null) ||
|
||||
"操作失败,请稍后重试"
|
||||
);
|
||||
}
|
||||
return "操作失败,请稍后重试";
|
||||
};
|
||||
|
||||
const doSubmit = async (values, passQuick) => {
|
||||
const selected = expertOptions.find(
|
||||
(o) => o.value === values.filingExpertId,
|
||||
);
|
||||
// 底部「通过」快捷操作:资质备案初审/备案变更传通过(1),专家核验不区分(直接核验通过)
|
||||
const result = passQuick ? 1 : values.reviewResult;
|
||||
const resultLabel =
|
||||
REVIEW_RESULT_OPTIONS.find((r) => r.value === result)?.label || "通过";
|
||||
const submitAction = isChange ? changeReviewSubmit : reviewSubmit;
|
||||
const params = {
|
||||
filingId,
|
||||
reviewResult: result,
|
||||
reviewResultRemark: passQuick ? "审核通过" : resultLabel,
|
||||
filingExpertId: values.filingExpertId,
|
||||
filingExpertName: selected?.expert?.userName,
|
||||
reviewOpinion: values.reviewOpinion || (passQuick ? "审核通过" : ""),
|
||||
};
|
||||
if (isChange) {
|
||||
params.id = params.filingId;
|
||||
delete params.filingId;
|
||||
|
||||
try {
|
||||
if (isExpert) {
|
||||
// 专家资料核验:走独立专家核验接口,仅更新专家核验结果(expertResult)与专家审核意见(expertOpinion),不修改备案状态
|
||||
const expertParams = {
|
||||
filingId,
|
||||
expertResult: passQuick ? 1 : result,
|
||||
expertOpinion: values.expertOpinion || (passQuick ? "核验通过" : ""),
|
||||
};
|
||||
const expertRes = await expertReviewSubmit(expertParams);
|
||||
if (expertRes?.success !== false) {
|
||||
message.success(passQuick ? "核验通过!" : "专家核验提交成功");
|
||||
onCancel();
|
||||
onSuccess?.();
|
||||
return true;
|
||||
}
|
||||
message.error(getErrMsg(expertRes));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 备案变更管理:走原 change 审核接口(参数 id)
|
||||
if (isChangeScene) {
|
||||
const params = {
|
||||
id: filingId,
|
||||
reviewResult: result,
|
||||
reviewResultRemark: passQuick ? "审核通过" : (values.reviewResultRemark || ""),
|
||||
reviewOpinion: values.reviewOpinion || (passQuick ? "审核通过" : ""),
|
||||
};
|
||||
const res = await changeReviewSubmit(params);
|
||||
if (res?.success !== false) {
|
||||
message.success(passQuick ? "审核通过!" : "审核提交成功");
|
||||
onCancel();
|
||||
onSuccess?.();
|
||||
return true;
|
||||
}
|
||||
message.error(getErrMsg(res));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 资质备案初审:走原 review 审核接口(含指派专家 filingExpertId/filingExpertName)
|
||||
const params = {
|
||||
filingId,
|
||||
reviewResult: result,
|
||||
reviewResultRemark: passQuick ? "审核通过" : (values.reviewResultRemark || ""),
|
||||
reviewOpinion: values.reviewOpinion || (passQuick ? "审核通过" : ""),
|
||||
filingExpertId: values.filingExpertId,
|
||||
filingExpertName: values.filingExpertName,
|
||||
};
|
||||
const res = await reviewSubmit(params);
|
||||
if (res?.success !== false) {
|
||||
message.success(passQuick ? "审核通过!" : "审核提交成功");
|
||||
onCancel();
|
||||
onSuccess?.();
|
||||
return true;
|
||||
}
|
||||
message.error(getErrMsg(res));
|
||||
return false;
|
||||
} catch (err) {
|
||||
message.error(getErrMsg(err));
|
||||
return false;
|
||||
}
|
||||
const res = await submitAction(params);
|
||||
if (res?.success !== false) {
|
||||
message.success(passQuick ? "审核通过!" : "审核提交成功");
|
||||
onCancel();
|
||||
onSuccess?.();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
|
|
@ -110,7 +173,7 @@ const ReviewModal = (props) => {
|
|||
|
||||
return (
|
||||
<Modal
|
||||
title="审核操作"
|
||||
title={isExpert ? "专家核验" : "审核操作"}
|
||||
open={open}
|
||||
onCancel={onCancel}
|
||||
width={620}
|
||||
|
|
@ -118,7 +181,7 @@ const ReviewModal = (props) => {
|
|||
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
|
||||
<Button onClick={onCancel}>取消</Button>
|
||||
{reviewResult !== 3 && (
|
||||
<Button loading={qualReviewSubmitLoading} onClick={handlePass} color="green" variant="solid" >
|
||||
<Button loading={qualReviewSubmitLoading} onClick={handlePass} color="green" variant="solid">
|
||||
通过
|
||||
</Button>
|
||||
)}
|
||||
|
|
@ -132,6 +195,7 @@ const ReviewModal = (props) => {
|
|||
</div>
|
||||
}
|
||||
>
|
||||
{/* 📊 评价分析(四卡),专家核验场景标题为「核验分析」 */}
|
||||
<div
|
||||
style={{
|
||||
background: "#f8fafc",
|
||||
|
|
@ -148,7 +212,7 @@ const ReviewModal = (props) => {
|
|||
marginBottom: "0.5rem",
|
||||
}}
|
||||
>
|
||||
📊 评价分析
|
||||
{isExpert ? "📊 核验分析" : "📊 评价分析"}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
|
|
@ -159,33 +223,25 @@ const ReviewModal = (props) => {
|
|||
>
|
||||
<div style={statStyle}>
|
||||
<div style={statLabelStyle}>已核验材料</div>
|
||||
<div
|
||||
style={{ fontSize: "1.1rem", fontWeight: 700, color: "#059669" }}
|
||||
>
|
||||
<div style={{ fontSize: "1.1rem", fontWeight: 700, color: "#059669" }}>
|
||||
{totalChecked}
|
||||
</div>
|
||||
</div>
|
||||
<div style={statStyle}>
|
||||
<div style={statLabelStyle}>符合</div>
|
||||
<div
|
||||
style={{ fontSize: "1.1rem", fontWeight: 700, color: "#059669" }}
|
||||
>
|
||||
<div style={{ fontSize: "1.1rem", fontWeight: 700, color: "#059669" }}>
|
||||
{legalPassCount}
|
||||
</div>
|
||||
</div>
|
||||
<div style={statStyle}>
|
||||
<div style={statLabelStyle}>不符合</div>
|
||||
<div
|
||||
style={{ fontSize: "1.1rem", fontWeight: 700, color: "#dc2626" }}
|
||||
>
|
||||
<div style={{ fontSize: "1.1rem", fontWeight: 700, color: "#dc2626" }}>
|
||||
{legalFailCount}
|
||||
</div>
|
||||
</div>
|
||||
<div style={statStyle}>
|
||||
<div style={statLabelStyle}>风险提醒</div>
|
||||
<div
|
||||
style={{ fontSize: "1.1rem", fontWeight: 700, color: "#d97706" }}
|
||||
>
|
||||
<div style={statLabelStyle}>条件未满足</div>
|
||||
<div style={{ fontSize: "1.1rem", fontWeight: 700, color: "#d97706" }}>
|
||||
{legalWarnCount}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -198,45 +254,83 @@ const ReviewModal = (props) => {
|
|||
style={{ marginTop: "0.5rem" }}
|
||||
scrollToFirstError
|
||||
>
|
||||
<Form.Item
|
||||
label="审核结果"
|
||||
name="reviewResult"
|
||||
rules={[{ required: true, message: "请选择审核结果" }]}
|
||||
>
|
||||
<Select placeholder="请选择">
|
||||
{REVIEW_RESULT_OPTIONS.map((item) => (
|
||||
<Select.Option key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
{/* 专家核验场景:审核结果 + 核验意见(2026-08-14 补充审核结果选项) */}
|
||||
{isExpert ? (
|
||||
<>
|
||||
<Form.Item
|
||||
label="审核结果"
|
||||
name="reviewResult"
|
||||
rules={[{ required: true, message: "请选择审核结果" }]}
|
||||
>
|
||||
<Select placeholder="请选择">
|
||||
{QUAL_RESULT_OPTIONS.map((item) => (
|
||||
<Select.Option key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="核验意见"
|
||||
name="expertOpinion"
|
||||
rules={[{ required: true, message: "请输入核验意见" }]}
|
||||
>
|
||||
<TextArea rows={3} placeholder="请输入核验意见..." maxLength={200} />
|
||||
</Form.Item>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Form.Item
|
||||
label="审核结果"
|
||||
name="reviewResult"
|
||||
rules={[{ required: true, message: "请选择审核结果" }]}
|
||||
>
|
||||
<Select placeholder="请选择">
|
||||
{(isChangeScene ? CHANGE_RESULT_OPTIONS : QUAL_RESULT_OPTIONS).map((item) => (
|
||||
<Select.Option key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{!isChange && (
|
||||
<Form.Item label="指派专家" name="filingExpertId">
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
options={expertOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
{/* 2026-08-14 指派专家:仅资质备案初审显示(原型 modal-review-action),备案变更/专家核验不显示 */}
|
||||
{!isChangeScene && (
|
||||
<Form.Item
|
||||
label="指派专家"
|
||||
name="filingExpertId"
|
||||
rules={[{ required: true, message: "请选择指派专家" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
onChange={(_, option) => {
|
||||
// 同步设置专家姓名,随审核提交给后端
|
||||
form.setFieldValue("filingExpertName", option?.userName);
|
||||
}}
|
||||
>
|
||||
{(qualExpertList || []).map((exp) => (
|
||||
<Select.Option key={exp.id} value={exp.id} label={exp.userName}>
|
||||
{exp.userName}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
label="综合审核意见"
|
||||
name="reviewOpinion"
|
||||
rules={[{ required: true, message: "请输入综合审核意见" }]}
|
||||
>
|
||||
<TextArea rows={3} placeholder="请输入综合审核意见..." maxLength={200} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
label="综合审核意见"
|
||||
name="reviewOpinion"
|
||||
rules={[{ required: true, message: "请输入综合审核意见" }]}
|
||||
>
|
||||
<TextArea
|
||||
rows={3}
|
||||
placeholder="请输入综合审核意见..."
|
||||
maxLength={200}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default Connect([NS_QUAL_EXPERT, NS_QUAL_REVIEW], true)(ReviewModal);
|
||||
export default Connect([NS_QUAL_REVIEW, NS_QUAL_EXPERT], true)(ReviewModal);
|
||||
|
|
|
|||
|
|
@ -136,6 +136,9 @@ const QualReviewForm = (props) => {
|
|||
open={reviewVisible}
|
||||
filingId={qualFilingDetail?.id}
|
||||
isChange={isChange}
|
||||
// 2026-08-14 新增:mode=expert 表示专家资料核验入口(ExpertVerification 进入),
|
||||
// 此时审核操作走独立专家核验接口,仅更新专家核验结果/意见,不修改备案状态
|
||||
mode={router.query.mode}
|
||||
legalPassCount={legalPassCount}
|
||||
legalFailCount={legalFailCount}
|
||||
legalWarnCount={legalWarnCount}
|
||||
|
|
|
|||
Loading…
Reference in New Issue