dev_1.0.1
tangjie 2026-07-02 11:00:31 +08:00
parent 9102384a71
commit 65abc7ee66
11 changed files with 120 additions and 186 deletions

View File

@ -10,7 +10,7 @@ module.exports = {
javaGitBranch: "<branch-name>",
// 本地联调 safety-eval-servicecontext-path: /safety-eval默认端口 8095
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
API_HOST: "http://192.168.0.149",
API_HOST: "http://192.168.0.204",
},
production: {
// 应用后端分支名称,部署上线需要
@ -51,7 +51,8 @@ module.exports = {
// 服务地址0.0.0.0 允许局域网 IP 访问)
host: "0.0.0.0",
// 是否自动打开浏览器
open: true,
open: false,
},
// 框架
framework: {

View File

@ -356,3 +356,8 @@ export const qualFilingChangeStart = declareRequest("qualFilingLoading", safeAct
export const qualFilingChangeSubmit = declareRequest("qualFilingLoading", safeAction(async ({ draftFilingId }) => {
return apiPost("/safety-eval/qual-filing-change/submit", { draftFilingId: asId(draftFilingId) });
}));
export const submitQualFiling = declareRequest(
"qualFilingSubmitLoading",
"Post > @/safety-eval/qual-filing/aggregationSaveOrEdit",
);

View File

@ -0,0 +1,7 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const queryReviewList = declareRequest(
"qualReviewLoading",
"Get > /safety-eval/qual-filing/page",
'qualReviewList: [] | res.data || [] & qualReviewTotal: 0 | res.total || 0'
);

View File

@ -1,9 +1,10 @@
import { Form, Image, Upload } from "antd";
import { useState } from "react";
import { PlusOutlined } from "@ant-design/icons";
const isImage = (url) =>
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
const isImage = (url) => /\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
export default function AttachmentUpload({ name, label, disabled }) {
export default function AttachmentUpload({ name, label, disabled = false }) {
const [previewImage, setPreviewImage] = useState("");
return (
@ -50,7 +51,10 @@ export default function AttachmentUpload({ name, label, disabled }) {
}
}}
>
上传附件
<button style={{ border: 0, background: "none" }} type="button">
<PlusOutlined />
<div style={{ marginTop: 8 }}>上传附件</div>
</button>
</Upload>
</Form.Item>
<Image

View File

@ -18,3 +18,4 @@ export const NS_STAFF_CHANGE_LOG = defineNamespace("staffChangeLog");
export const NS_STAFF_RESIGNATION_APPLY = defineNamespace("staffResignationApply");
export const NS_EQUIP_INFO = defineNamespace("equipInfo");
export const NS_QUAL_FILING = defineNamespace("qualFiling");
export const NS_QUAL_REVIEW = defineNamespace("qualReview");

View File

@ -4,16 +4,14 @@ import {
Col,
DatePicker,
Form,
Image,
Input,
InputNumber,
message,
Row,
Select,
Space,
Upload,
} from "antd";
import { PlusOutlined } from "@ant-design/icons";
import AttachmentUpload from "~/components/AttachmentUpload";
import dayjs from "dayjs";
import { useEffect, useState } from "react";
@ -44,7 +42,6 @@ function OrgInfoPage(props) {
const [editing, setEditing] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [detail, setDetail] = useState({});
const [previewImage, setPreviewImage] = useState("");
/** 是否已存在机构数据(有 id 视为已入库,只能修改) */
const [hasExistingData, setHasExistingData] = useState(false);
@ -474,44 +471,7 @@ function OrgInfoPage(props) {
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="attachments"
label="上传附件"
valuePropName="fileList"
getValueFromEvent={({ fileList }) => {
return (
fileList?.map((file) => {
return {
url: file.response?.data.url || file.url,
uid: file.uid,
name: file.name,
};
}) || []
);
}}
>
<Upload
maxCount={5}
onPreview={(file) => {
const isImage =
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(
file.name || file.url || "",
);
if (isImage) {
setPreviewImage(file.url || file.thumbUrl);
} else {
window.open(file.url || file.thumbUrl);
}
}}
listType="picture-card"
action={`${window.process.env.app.API_HOST}/safety-eval/file/upload`}
>
<button style={{ border: 0, background: "none" }} type="button">
<PlusOutlined />
<div style={{ marginTop: 8 }}>上传附件</div>
</button>
</Upload>
</Form.Item>
<AttachmentUpload name="attachments" label="上传附件" />
</Col>
</Row>
</Form>
@ -534,16 +494,6 @@ function OrgInfoPage(props) {
</Space>
</div>
)}
<Image
style={{ display: "none" }}
preview={{
visible: !!previewImage,
src: previewImage,
onVisibleChange: (visible) => {
if (!visible) setPreviewImage("");
},
}}
/>
</PageLayout>
);
}

View File

@ -16,12 +16,12 @@ export default function MaterialStep({
<Table
size="small"
loading={loading}
rowKey="id"
rowKey="materialType"
pagination={false}
scroll={{ x: 900 }}
dataSource={materials}
columns={[
{ title: "序号", dataIndex: "sortOrder", width: 60 },
{ title: "序号", width: 60, render: (_, record, index) => index + 1 },
{ title: "内容", dataIndex: "materialContent", width: 200, ellipsis: true },
{ title: "上传附件", dataIndex: "attachmentDesc", width: 100 },
{

View File

@ -5,16 +5,9 @@ import { useCallback, useEffect, useRef, useState } from "react";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import { fetchQualFilingDetail } from "~/api/qualFiling";
import { NS_QUAL_FILING } from "~/enumerate/namespace";
import {
FILING_FORM_MODE,
isQualFilingEditable,
} from "~/enumerate/qualFilingOptions";
import { createLocalMaterials, FILING_MATERIAL_TEMPLATE } from "../filingMaterialTemplate";
import {
clearLocalDraft,
saveLocalDraft,
} from "../filingLocalDraft";
import { FILING_FORM_MODE } from "~/enumerate/qualFilingOptions";
import { FILING_MATERIAL_TEMPLATE } from "../filingMaterialTemplate";
import { clearLocalDraft, saveLocalDraft } from "../filingLocalDraft";
import {
fetchOrgPersonnelOptions,
mapEquipRowToFilingEquipment,
@ -32,6 +25,8 @@ import MaterialStep from "./components/MaterialStep";
import PersonnelStep from "./components/PersonnelStep";
import PrerequisiteVerifyModal from "./components/PrerequisiteVerifyModal";
const router = tools.router;
const STEP_ITEMS = [
{ key: "basic", label: "1. 备案基本信息" },
{ key: "materials", label: "2. 备案材料上传" },
@ -46,10 +41,8 @@ const MODE_TITLE = {
[FILING_FORM_MODE.CHANGE]: "修改备案信息",
};
function FilingFormPage(props) {
const query = tools.router.query;
const query = router.query;
const mode = query.mode;
const [loading, setLoading] = useState(false);
const [submitting, setSubmitting] = useState(false);
@ -62,8 +55,7 @@ function FilingFormPage(props) {
const [commitmentForm] = Form.useForm();
const detailRef = useRef(null);
const readOnly =
query.readOnly
const readOnly = query.readOnly;
const listMode =
mode === FILING_FORM_MODE.FILED
@ -92,20 +84,17 @@ function FilingFormPage(props) {
useEffect(() => {
loadPersonnelOptions();
if(query.id){
if (query.id) {
fetchQualFilingDetail(query.id).then((res) => {
setDetail((prev) => ({ ...prev, ...res?.data || {} }));
console.log(res?.data, 'res.data');
setDetail((prev) => ({ ...prev, ...(res?.data || {}) }));
console.log(res?.data, "res.data");
basicForm.setFieldsValue(res?.data || {});
});
}
else{
} else {
setDetail((prev) => ({ ...prev, materials: FILING_MATERIAL_TEMPLATE }));
}
}, []);
useEffect(() => {
if (!personnelOptions.length) {
return;
@ -132,7 +121,6 @@ function FilingFormPage(props) {
}
}, [commitmentForm, personnelOptions]);
const collectCurrentDetail = useCallback(() => {
const current = detailRef.current || detail;
return mergeDetailFromForms(current, {
@ -151,7 +139,7 @@ function FilingFormPage(props) {
await persistFilingToBackend(props, currentDetail, mode);
clearLocalDraft(mode);
message.success("已暂存");
goBackList();
props.history.push("qualApplication/filingApplication/list");
} catch (err) {
message.error(err?.message || "暂存失败");
} finally {
@ -159,10 +147,12 @@ function FilingFormPage(props) {
}
};
const handleSubmitRequest = () => {
const handleSubmitRequest = async () => {
if (readOnly) {
return;
}
const currentDetail = collectCurrentDetail();
const result = verifyFilingPrerequisites(currentDetail);
setVerifyResult(result);
@ -174,54 +164,58 @@ function FilingFormPage(props) {
return;
}
setSubmitting(true);
try {
const currentDetail = collectCurrentDetail();
debugger
const saved = await persistFilingToBackend(props, currentDetail, mode);
const currentDetail = collectCurrentDetail();
const body = {
qualFilingAddCmd: { ...currentDetail },
};
const params = body.qualFilingAddCmd;
if (params.materials) {
body.qualFilingMaterialAddCmds = params.materials;
delete params.materials;
}
if (params.personnelList) {
body.qualFilingPersonnelAddCmds = params.personnelList;
delete params.personnelList;
}
if (params.equipmentList) {
body.qualFilingEquipmentAddCmds = params.equipmentList;
delete params.equipmentList;
}
if (params.commitment) {
body.qualFilingCommitmentAddCmd = params.commitment;
delete params.commitment;
}
if (router.query.id) {
body.qualFilingAddCmd.id = router.query.id;
}
debugger;
const result = await props.submitQualFiling(body);
if (mode === FILING_FORM_MODE.CHANGE) {
const res = await props.qualFilingChangeSubmit({
draftFilingId: saved.id,
});
if (res?.success === false) {
message.error(res.message || "提交失败");
return;
}
} else {
const res = await props.qualFilingSubmit({ id: saved.id });
if (res?.success === false) {
message.error(res.message || "提交失败");
return;
}
}
if (result?.success) {
setVerifyOpen(false);
message.success("提交成功");
goBackList();
} catch (err) {
message.error(err?.message || "提交失败");
} finally {
setSubmitting(false);
props.history.push("filingApplication/list");
} else {
message.error(result?.message || "提交失败");
}
setSubmitting(false);
};
const handleMaterialUpload = (record, url) => {
const attachmentUrl = url || resolveUploadFileId([]);
setDetail((prev) => ({
...prev,
materials: (prev.materials || []).map((item) =>
{
return item.id === record.id
materials: (prev.materials || []).map((item) => {
return item.materialType === record.materialType
? {
...item,
attachmentUrl,
uploadStatusCode: 2,
uploadStatusName: "已上传",
}
: item
}
),
: item;
}),
}));
message.success(`材料已选择,${saveActionHint}`);
};
@ -367,17 +361,13 @@ function FilingFormPage(props) {
basicForm.getFieldValue("filingUnitName") ||
detail?.filingUnitName,
});
}
setActiveStep(key);
}}
/>
<div style={{ marginTop: 16 }}>
{activeStep === "basic" && (
<BasicInfoStep
form={basicForm}
disabled={readOnly}
/>
<BasicInfoStep form={basicForm} disabled={readOnly} />
)}
{activeStep === "materials" && (
<MaterialStep
@ -448,8 +438,7 @@ function FilingFormPage(props) {
<Button
type="primary"
onClick={() => {
console.log(basicForm.getFieldsValue());
setActiveStep(STEP_ITEMS[stepIndex + 1].key)
setActiveStep(STEP_ITEMS[stepIndex + 1].key);
}}
>
下一步

View File

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

View File

@ -278,7 +278,7 @@ export function mergeDetailFromForms(detail, { basicValues, commitmentValues })
commitment: {
...detail.commitment,
...commitmentValues,
legalRepSignatureUrl: basicValues?.legalRepSignatureUrl?.map((file) => file.url).join(",") || undefined,
legalRepSignatureUrl: commitmentValues?.legalRepSignatureUrl?.map((file) => file.url).join(",") || undefined,
},
};
}

View File

@ -6,48 +6,23 @@ 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 { REVIEW_STATUS_MAP, EXPERT_STATUS_MAP } from "~/enumerate/constant";
// 静态模拟数据
const MOCK_DATA = [
{
id: 1,
orgName: "重庆安评技术研究院有限公司",
orgType: "本地机构",
certNo: "API-2026-001",
businessScope: "化工, 石油加工",
expertStatus: "pending",
reviewStatus: "pending",
},
{
id: 2,
orgName: "北京中安评价中心(重庆分公司)",
orgType: "异地机构",
certNo: "API-2026-015",
businessScope: "矿山, 建筑施工",
expertStatus: "arranging",
reviewStatus: "reviewing",
},
{
id: 3,
orgName: "重庆渝安风险评估中心",
orgType: "本地机构",
certNo: "API-2026-008",
businessScope: "化工",
expertStatus: "none",
reviewStatus: "rejected",
},
];
const { router } = tools;
const QualReview = (props) => {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const { qualApplication, queryReviewList } = props;
const {qualReviewList, qualReviewTotal,qualReviewLoading}= qualApplication || {};
const handleSearch = () => {
setLoading(true);
setTimeout(() => setLoading(false), 500);
queryReviewList(router.query);
};
const handleReset = (values) => {
@ -55,13 +30,14 @@ const QualReview = (props) => {
...router.query,
...values,
current: 1,
pageSize: 10,
size: 10,
};
handleSearch();
};
useEffect(() => {
form.setFieldsValue(router.query);
handleSearch();
}, []);
const columns = [
@ -72,16 +48,16 @@ const QualReview = (props) => {
fixed: "left",
render: (text, record, index) => index + 1,
},
{ title: "机构名称", dataIndex: "orgName", width: 220, ellipsis: true },
{ title: "机构类型", dataIndex: "orgType", width: 100 },
{ title: "证书编号", dataIndex: "certNo", width: 140, ellipsis: true },
{ title: "机构名称", dataIndex: "filingUnitName", width: 220, ellipsis: true },
{ title: "机构类型", dataIndex: "filingUnitTypeName", width: 100 },
{ title: "证书编号", dataIndex: "filingNo", width: 140, ellipsis: true },
{ title: "安全评价业务范围", dataIndex: "businessScope", width: 180, ellipsis: true },
{
title: "专家核验",
dataIndex: "expertStatus",
width: 100,
render: (status) => {
const config = EXPERT_STATUS_MAP[status];
const config = EXPERT_STATUS_MAP['pending'];
return config ? <Tag color={config.color}>{config.label}</Tag> : '--';
},
},
@ -90,7 +66,7 @@ const QualReview = (props) => {
dataIndex: "reviewStatus",
width: 100,
render: (status) => {
const config = REVIEW_STATUS_MAP[status];
const config = REVIEW_STATUS_MAP[pending];
return config ? <Tag color={config.color}>{config.label}</Tag> : '--';
},
},
@ -118,7 +94,7 @@ const QualReview = (props) => {
<SearchForm
style={{ marginBottom: 24 }}
form={form}
loading={loading}
loading={qualReviewLoading}
formLine={[
<Form.Item key="orgName" name="orgName">
<ControlWrapper.Input label="机构名称" placeholder="请输入" allowClear />
@ -177,7 +153,7 @@ const QualReview = (props) => {
...router.query,
...values,
current: 1,
pageSize: 10,
size: 10,
};
handleSearch();
}}
@ -186,21 +162,21 @@ const QualReview = (props) => {
<Table
rowKey="id"
columns={columns}
dataSource={MOCK_DATA}
dataSource={qualReviewList}
scroll={{ y: props.scrollY }}
loading={loading}
loading={qualReviewLoading}
pagination={{
total: MOCK_DATA.length,
total: qualReviewTotal,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `${total}`,
current: router.query.current,
pageSize: router.query.pageSize,
onChange: (page, pageSize) => {
current: router.query.current || 1,
size: router.query.size || 10,
onChange: (page, size) => {
router.query = {
...router.query,
current: page,
pageSize,
size,
};
handleSearch();
},
@ -210,4 +186,5 @@ const QualReview = (props) => {
);
};
export default AntdTableFuncControl(QualReview);
export default Connect([NS_QUAL_REVIEW], true)(AntdTableFuncControl(QualReview));