dev-tmp1
tangjie 2026-08-27 17:20:58 +08:00
parent a31d7e3115
commit 72b326c6e0
5 changed files with 81 additions and 20 deletions

View File

@ -12,9 +12,9 @@ module.exports = {
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095 // 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
//API_HOST: "http://localhost:80", //API_HOST: "http://localhost:80",
// API_HOST: "http://192.168.0.134", API_HOST: "http://192.168.0.134",
// API_HOST: "http://192.168.0.150", //太浅 // API_HOST: "http://192.168.0.150", //太浅
API_HOST: "https://gbs-gateway.qhdsafety.com", // API_HOST: "https://gbs-gateway.qhdsafety.com",
// API_HOST: "http://192.168.0.103", //huwei // API_HOST: "http://192.168.0.103", //huwei
}, },
production: { production: {

View File

@ -1,11 +1,61 @@
import { Form, Image, Upload, message } from "antd"; import { Form, Image, Upload, message } from "antd";
import { useState } from "react"; import { useEffect, useRef, useState } from "react";
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
/** 批量上传接口:一次请求上传 1~N 个文件,表单字段名为 files */
const BATCH_UPLOAD_ACTION = `${window.process.env.app.API_HOST}/safetyEval/file/upload/batch`;
const isImage = (url) => const isImage = (url) =>
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || ""); /\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
export default function AttachmentUpload({ name, label, disabled, maxCount, accept, extra ,rules, style, keepOriginFile }) { export default function AttachmentUpload({ name, label, disabled, maxCount, accept, extra ,rules, style, keepOriginFile }) {
const [previewImage, setPreviewImage] = useState(""); const [previewImage, setPreviewImage] = useState("");
// 同一次选择的文件先入队,等微任务结束后合并为一个批量请求
const queueRef = useRef([]);
const timerRef = useRef(null);
const flushUpload = () => {
timerRef.current = null;
const batch = queueRef.current.splice(0);
if (!batch.length) return;
const failAll = (msg) => {
message.error(msg);
batch.forEach(({ file, onError }) => onError(new Error(msg)));
};
const formData = new FormData();
batch.forEach(({ file }) => formData.append("files", file));
fetch(BATCH_UPLOAD_ACTION, {
method: "POST",
headers: { token: sessionStorage.getItem("token") || "" },
body: formData,
})
.then((res) => res.json())
.then((json) => {
if (json?.success === false) {
failAll(json.errMessage || json.message || "附件上传失败");
return;
}
const list = json?.data || [];
// 后端保证响应数组与请求文件顺序一致,按下标一一对应
batch.forEach(({ file, onDone, onError }, index) => {
const item = list[index];
if (!item) {
onError(new Error(`${file.name} 上传失败`));
return;
}
// 保留 { data } 结构,兼容表单取值 file.response?.data?.url
onDone({ data: item }, file);
});
})
.catch((err) => failAll(err?.message || "附件上传失败"));
};
useEffect(() => () => clearTimeout(timerRef.current), []);
const customRequest = ({ file, onSuccess: onDone, onError }) => {
queueRef.current.push({ file, onDone, onError });
if (!timerRef.current) timerRef.current = setTimeout(flushUpload, 0);
};
return ( return (
<> <>
@ -55,12 +105,11 @@ export default function AttachmentUpload({ name, label, disabled, maxCount, acce
<Upload <Upload
disabled={disabled} disabled={disabled}
listType="picture-card" listType="picture-card"
headers={{ // maxCount 为 1 时仍单选,其余场景允许一次选择多个文件
token: sessionStorage.getItem('token') multiple={!maxCount || maxCount > 1}
}} customRequest={customRequest}
maxCount={maxCount} maxCount={maxCount}
accept={accept} accept={accept}
action={`${window.process.env.app.API_HOST}/safetyEval/file/upload`}
beforeUpload={(file) => { beforeUpload={(file) => {
if (!accept) return true; if (!accept) return true;
const rules = accept.split(",").map((e) => e.trim().toLowerCase()); const rules = accept.split(",").map((e) => e.trim().toLowerCase());

View File

@ -21,6 +21,7 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime";
import useIndustryOptions from "~/hooks/useIndustryOptions"; import useIndustryOptions from "~/hooks/useIndustryOptions";
import BaiduMapPicker from "~/components/BaiduMapPicker"; import BaiduMapPicker from "~/components/BaiduMapPicker";
import { district } from "~/enumerate/constant"; import { district } from "~/enumerate/constant";
import {REGISTER_ENGINEER_CATEGORY_OPTIONS} from '~/enumerate/enterpriseOptions';
import { phoneRule } from "~/utils/validators"; import { phoneRule } from "~/utils/validators";
import { renderCapabilityList } from "~/utils"; import { renderCapabilityList } from "~/utils";
import StaffPickerModal from "./StaffPickerModal"; import StaffPickerModal from "./StaffPickerModal";
@ -52,6 +53,9 @@ const EvalProjectCreate = (props) => {
} = props.safetyEvalBusiness; } = props.safetyEvalBusiness;
const industryOptions = useIndustryOptions(); const industryOptions = useIndustryOptions();
// 是否法定项目选“否”(仅机构内部管理)时,行业类型默认全选;编辑/查看模式不联动,避免覆盖回填数据
const isStatutory = Form.useWatch("isStatutory", form);
useEffect(() => { useEffect(() => {
props.customerPage({ current: 1, size: 999 }); props.customerPage({ current: 1, size: 999 });
@ -326,11 +330,25 @@ const EvalProjectCreate = (props) => {
</Col> </Col>
</Row> </Row>
<Row gutter={24}> <Row gutter={24}>
<Col span={12}>
<Form.Item
name="isStatutory"
label="是否法定项目"
rules={[{ required: true, message: "请选择是否法定项目" }]}
>
<Radio.Group onChange={()=>{
form.setFieldValue('industryCode', '')
}}>
<Radio value={true}>纳入监管端监管</Radio>
<Radio value={false}>仅机构内部管理</Radio>
</Radio.Group>
</Form.Item>
</Col>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
name="industryCode" name="industryCode"
label="所属行业" label="项目行业类型"
rules={[{ required: true, message: "请选择所属行业" }]} rules={[{ required: true, message: "请选择项目行业类型" }]}
getValueProps={(value) => ({ getValueProps={(value) => ({
value: value ? String(value).split(",") : [], value: value ? String(value).split(",") : [],
})} })}
@ -342,20 +360,13 @@ const EvalProjectCreate = (props) => {
mode="multiple" mode="multiple"
placeholder="请选择" placeholder="请选择"
allowClear allowClear
options={industryOptions} options={isStatutory?industryOptions:REGISTER_ENGINEER_CATEGORY_OPTIONS}
/> />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item name="controlledNumber" label="受控编号" rules={[{ required: true, message: "请选择受控编号" }]}>
name="isStatutory" <Input placeholder="请输入" maxLength={20} />
label="是否法定项目"
rules={[{ required: true, message: "请选择是否法定项目" }]}
>
<Radio.Group>
<Radio value={true}>纳入监管端监管</Radio>
<Radio value={false}>仅机构内部管理</Radio>
</Radio.Group>
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>

View File

@ -32,7 +32,7 @@ const { router } = tools;
const { TextArea } = Input; const { TextArea } = Input;
/** 风险分析记录表 docx 模板 */ /** 风险分析记录表 docx 模板 */
const RISK_TEMPLATE_URL = "https://test-dragon-yf-pub.oss-cn-hangzhou.aliyuncs.com/jjb/6a86d268e4b072649bfb8a84.docx"; const RISK_TEMPLATE_URL = "https://gbs-cqaqpj.oss-cn-beijing.aliyuncs.com/jjb/6a8ffce69f4622759bcd7ba3.docx";
/** 两选项单选回显1 勾选前者2 勾选后者 */ /** 两选项单选回显1 勾选前者2 勾选后者 */
const twoCheck = (val, yes, no) => const twoCheck = (val, yes, no) =>
@ -233,6 +233,7 @@ const RiskAnalysisContent = ({ readOnly, ...props }) => {
RISK_LEVEL_CHECK[values.riskLevelCode] || "□ 低度 □ 中度 □ 高度", RISK_LEVEL_CHECK[values.riskLevelCode] || "□ 低度 □ 中度 □ 高度",
signContractCheck: twoCheck(values.signContractCode, "签合同", "不签合同"), signContractCheck: twoCheck(values.signContractCode, "签合同", "不签合同"),
participantsText: signTokens.join(""), participantsText: signTokens.join(""),
controlledNumber: evalProjectDetailData.controlledNumber,
}; };
try { try {
await fillDocxTemplate( await fillDocxTemplate(