增加监管端统计

master
fangjiakai 2025-11-26 08:33:41 +08:00
parent 5be58b5aaa
commit 9e20384ca5
10 changed files with 424 additions and 80 deletions

23
accident/accident.html Normal file
View File

@ -0,0 +1,23 @@
<!doctype html><html lang="zh"><head data-built-info="@cqsjjb/scripts@2.0.0-alpha-1 Env/development (2025/11/18 15:20:02) App/accident"><meta charset="UTF-8"/><meta name="renderer" content="webkit"/><meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1"/><meta name="viewport" content="width=device-width,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover"><title>--</title><script>(function () {
const APP_ENV = {
antd: {
'ant-prefix': 'micro-temp',
fontFamily: 'PingFangSC-Regular',
colorPrimary: '#1677ff',
borderRadius: parseInt('2')
},
appKey: '',
basename: 'accident',
API_HOST: 'http://192.168.20.100:30140'
};
APP_ENV.API_HOST = sessionStorage.API_HOST || APP_ENV.API_HOST || window.location.origin;
window.process = {
env: { app: APP_ENV },
NODE_ENV: 'production'
};
window.__JJB_ENVIRONMENT__ = {
API_HOST: APP_ENV.API_HOST,
redirect: '',
FRAMEWORK: APP_ENV.antd
};
})();</script><script defer="defer" src="/accident/static/js/213.bb9eb53c43b5e0ebd4b2.js"></script><script defer="defer" src="/accident/static/js/617.076e2221828bb825e03d.js"></script><script defer="defer" src="/accident/static/js/main.bebd4111eaadc4e01fab.js"></script><link href="/accident/static/css/main.19d4da69d3868bd682cf.css" rel="stylesheet"></head><body><noscript>此网页需要开启JavaScript功能。</noscript><div id="root" style="width: 100%; height: 100%; position: relative"></div><script type="text/javascript">/* @cqsjjb/script 输出当前应用基本信息、构建时间 */console.log("%c@cqsjjb/scripts@2.0.0-alpha-1 Env/development (2025/11/18 15:20:02) App/accident Version/master Java/<branch-name>", "color: #1890ff; border-radius: 2px; padding: 0 4px; border: 1px solid #1890ff; background: #f9fcff")</script></body></html>

View File

@ -1,10 +1,8 @@
import antfu from "@antfu/eslint-config";
export default antfu({
formatters: {
html: false,
css: true,
},
// Remove formatter configuration that might be causing issues
formatters: false,
test: false,
typescript: true,
react: true,

View File

@ -9,7 +9,7 @@ module.exports = {
// 应用后端分支名称,部署上线需要
javaGitBranch: "<branch-name>",
// 接口服务地址
API_HOST: "http://192.168.10.37:80",
API_HOST: "http://192.168.20.100:30140",
},
production: {
// 应用后端分支名称,部署上线需要
@ -24,7 +24,7 @@ module.exports = {
contextInject: {
// 应用Key
appKey: "",
fileUrl: "附件地址",
fileUrl: "http://192.168.20.240:9787/mnt",
},
// public/index.html注入全局变量
windowInject: {

View File

@ -25,12 +25,12 @@
"@cqsjjb/jjb-dva-runtime": "latest",
"@cqsjjb/jjb-react-admin-component": "latest",
"ahooks": "^3.9.5",
"antd": "latest",
"antd": "5.27.6",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zy-react-library": "^1.0.34"
"zy-react-library": "^1.0.127"
},
"devDependencies": {
"@antfu/eslint-config": "^5.4.1",

View File

@ -49,6 +49,6 @@
<noscript>此网页需要开启JavaScript功能。</noscript>
<!-- MAIN -->
<% const { root } = $element; %>
<div id="<%= root.id %>" style="width: 100%; height: 100%; position: relative"></div>
<div id="<%= root.id %>" style="width: 100%; height: 100%; position: relative;overflow-y: auto;"></div>
</body>
</html>

View File

@ -22,3 +22,17 @@ export const accidentBatchDelete = declareRequest(
);
export const accidentInfo = declareRequest('accidentLoading', 'Get > /accident/accident/{id}');
export const accidentCountByCorpinfoAndType = declareRequest(
'accidentLoading',
'Post > @/accident/accident/countByCorpinfoAndType'
);
export const getCorpInfoList = declareRequest(
'accidentLoading',
'Post > @/basic-info/corpInfo/list'
);
export const accidentExport = declareRequest(
'accidentLoading',
'Post > @/accident/accident/export'
);

View File

@ -1,26 +1,41 @@
import React, { useEffect, useState } from "react";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Descriptions, Form, message, Modal, Space } from "antd";
import { useEffect, useState } from "react";
import FormBuilder from "zy-react-library/components/FormBuilder";
import Search from "zy-react-library/components/Search";
import Table from "zy-react-library/components/Table";
import Upload from "zy-react-library/components/Upload";
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
import { UPLOAD_FILE_TYPE_ENUM } from "zy-react-library/enum/uploadFile/gwj";
import { TWO_DECIMAL_PLACES } from "zy-react-library/regular";
import useTable from "zy-react-library/hooks/useTable";
import useDeleteFile from "zy-react-library/hooks/useDeleteFile";
import useGetFile from "zy-react-library/hooks/useGetFile";
import useUploadFile from "zy-react-library/hooks/useUploadFile";
import { NS_ACCIDENT } from "~/enumerate/namespace";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
import DeleteIcon from "zy-react-library/components/Icon/DeleteIcon";
import ExportIcon from "zy-react-library/components/Icon/ExportIcon";
import HeaderBack from "zy-react-library/components/HeaderBack";
import DictionarySelect from "zy-react-library/components/Select/Dictionary";
import useDownloadBlob from "zy-react-library/hooks/useDownloadBlob";
import useUrlQueryCriteria from "zy-react-library/hooks/useUrlQueryCriteria";
function Accident(props) {
const [addModalVisible, setAddModalVisible] = useState(false);
const [infoModalVisible, setInfoModalVisible] = useState(false);
const [currentId, setCurrentId] = useState("");
const [accidentId, setAccidentId] = useState("");
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const { loading, downloadBlob } = useDownloadBlob();
const {getUrlCriteriaQuery} = useUrlQueryCriteria();
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["accidentList"], {
form,
params: {
eqType: props.type,
eqCorpinfoId: props.corpinfoId,
},
transform: data => ({
geIncidentDate: data.incidentDate?.[0],
@ -30,14 +45,14 @@ function Accident(props) {
const typeName = props.type === 1 ? "事件" : "事故";
return (
<div style={{ padding: 10 }}>
{props.isSupervise && <HeaderBack title={`${typeName}管理`} />}
<Search
form={form}
onFinish={getData}
options={[
{ name: "likeIncidentName", label: `${typeName}名称` },
{ name: "eqIncidentType", label: `${typeName}类型` },
{ name: "eqIncidentLevel", label: `${typeName}级别` },
{ name: "incidentLevelName", label: `${typeName}级别` },
{ name: "eqIncidentType", label: `${typeName}类型`, render: <DictionarySelect dictValue="accidentType" /> },
{ name: "eqIncidentLevel", label: `${typeName}级别`, render: <DictionarySelect dictValue="accidentLevel" /> },
{ name: "likeLocation", label: `${typeName}发生地点` },
{ name: "incidentDate", label: `${typeName}发生时间`, render: FORM_ITEM_RENDER_ENUM.DATE_RANGE },
]}
@ -49,6 +64,13 @@ function Accident(props) {
}}
toolBarRender={() => (
<Space>
<Button icon={<ExportIcon />} onClick={async () => {
const exportParams = getUrlCriteriaQuery("searchFormKeys","searchFormValues");
await downloadBlob("/accident/accident/export",{params:exportParams})
}}>导出</Button>
{!props.isSupervise && (
<React.Fragment>
<Button type="primary" icon={<AddIcon />} onClick={() => setAddModalVisible(true)}>新增</Button>
<Button icon={<DeleteIcon />}
type="primary"
@ -68,6 +90,8 @@ function Accident(props) {
>
批量删除
</Button>
</React.Fragment>
)}
</Space>
)}
columns={[
@ -85,20 +109,25 @@ function Accident(props) {
type="link"
onClick={() => {
setCurrentId(record.id);
setAccidentId(record.accidentId);
setInfoModalVisible(true);
}}
>
查看
</Button>
{!props.isSupervise &&
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setAccidentId(record.accidentId);
setAddModalVisible(true);
}}
>
编辑
</Button>
}
{!props.isSupervise &&
<Button
type="link"
danger
@ -115,34 +144,37 @@ function Accident(props) {
>
删除
</Button>
}
</Space>
),
},
]}
{...tableProps}
/>
<AddModal
visible={addModalVisible}
{addModalVisible && <AddModal
currentId={currentId}
accidentId={accidentId}
onCancel={() => {
setAddModalVisible(false);
setCurrentId("");
setAccidentId("");
}}
type={props.type}
typeName={typeName}
getData={getData}
/>
/>}
<InfoModal
visible={infoModalVisible}
{infoModalVisible && <InfoModal
currentId={currentId}
accidentId={accidentId}
onCancel={() => {
setInfoModalVisible(false);
setCurrentId("");
setAccidentId("");
}}
typeName={typeName}
getData={getData}
/>
/>}
</div>
);
}
@ -150,32 +182,117 @@ function Accident(props) {
function AddModalComponent(props) {
const [form] = Form.useForm();
const typeName = props.typeName;
const [dicNames, setDicNames] = useState({});
// 文件相关状态和hooks
const [deleteImageFiles, setDeleteImageFiles] = useState([]);
const [deleteAttachmentFiles, setDeleteAttachmentFiles] = useState([]);
const { deleteFile } = useDeleteFile();
const { uploadFile } = useUploadFile();
const { getFile } = useGetFile();
useEffect(() => {
if (props.currentId) {
props["accidentInfo"]({ id: props.currentId }).then((res) => {
form.setFieldsValue(res.data);
});
loadData();
}
}, [props.currentId]);
const loadData = async () => {
const { data } = await props["accidentInfo"]({ id: props.currentId });
// 获取已上传的文件
const imageFiles = await getFile({ eqType: UPLOAD_FILE_TYPE_ENUM["136"], eqForeignKey: props.currentId });
const attachmentFiles = await getFile({ eqType: UPLOAD_FILE_TYPE_ENUM["137"], eqForeignKey: props.currentId });
const values = {
...data,
imageFiles,
attachmentFiles
};
form.setFieldsValue(values);
setDicNames({
incidentTypeName: data.incidentTypeName,
incidentLevelName: data.incidentLevelName,
});
};
const onCancel = () => {
form.resetFields();
setDeleteImageFiles([]);
setDeleteAttachmentFiles([]);
props.onCancel();
};
const submit = async (values) => {
if (props.currentId) {
await props["accidentEdit"]({ ...values, id: props.currentId })
} else {
await props["accidentAdd"]({ ...values, type: props.type, id: 485738919865 });
try {
// 保存基本信息
const accidentId = props.accidentId;
// 编辑模式
if (accidentId) {
// 删除标记为删除的文件
if (deleteImageFiles.length > 0) {
await deleteFile({ single: false, files: deleteImageFiles });
}
if (deleteAttachmentFiles.length > 0) {
await deleteFile({ single: false, files: deleteAttachmentFiles });
}
// 上传新文件
if (values.imageFiles && values.imageFiles.length > 0) {
await uploadFile({
single: false,
files: values.imageFiles,
params: { type: UPLOAD_FILE_TYPE_ENUM["136"], foreignKey: accidentId }
});
}
if (values.attachmentFiles && values.attachmentFiles.length > 0) {
await uploadFile({
single: false,
files: values.attachmentFiles,
params: { type: UPLOAD_FILE_TYPE_ENUM["137"], foreignKey: accidentId }
});
}
// 保存基本信息
await props["accidentEdit"]({ ...values, ...dicNames, id: props.currentId });
} else {
// 上传文件
const { id } = await uploadFile({
single: false,
files: values.imageFiles,
params: { type: UPLOAD_FILE_TYPE_ENUM["136"], foreignKey: "" }
});
if (values.attachmentFiles && values.attachmentFiles.length > 0) {
await uploadFile({
single: false,
files: values.attachmentFiles,
params: { type: UPLOAD_FILE_TYPE_ENUM["137"], foreignKey: id }
});
}
const result = await props["accidentAdd"]({
...values,
...dicNames,
accidentId: id,
type: props.type,
});
}
onCancel();
props.getData();
message.success("操作成功");
} catch (error) {
message.error("操作失败,请重试");
console.error("提交失败:", error);
}
};
return (
<Modal
open={props.visible}
open
onCancel={onCancel}
onOk={form.submit}
title="新增"
title={props.currentId ? "编辑" : "新增"}
loading={props.accident.accidentLoading}
width={800}
>
@ -188,8 +305,8 @@ function AddModalComponent(props) {
options={[
{ name: "incidentNumber", label: `${typeName}案号` },
{ name: "incidentName", label: `${typeName}名称` },
{ name: "incidentType", label: `${typeName}类型` },
{ name: "incidentLevel", label: `${typeName}级别` },
{ name: "incidentType", label: `${typeName}类型`, render: <DictionarySelect dictValue="accidentType" onGetLabel={(label) => setDicNames({ ...dicNames, incidentTypeName: label })} /> },
{ name: "incidentLevel", label: `${typeName}级别`, render: <DictionarySelect dictValue="accidentLevel" onGetLabel={(label) => setDicNames({ ...dicNames, incidentLevelName: label })} /> },
{ name: "incidentNature", label: `${typeName}性质` },
{ name: "location", label: `${typeName}发生地点` },
{ name: "incidentDate", label: `${typeName}发生时间`, render: FORM_ITEM_RENDER_ENUM.DATETIME },
@ -203,6 +320,33 @@ function AddModalComponent(props) {
{ name: "suggestions", label: "考核建议", render: FORM_ITEM_RENDER_ENUM.TEXTAREA },
{ name: "measures", label: "整改措施", render: FORM_ITEM_RENDER_ENUM.TEXTAREA },
{ name: "reportDate", label: "报出日期", render: FORM_ITEM_RENDER_ENUM.DATETIME },
// 添加图片上传
{
name: "imageFiles",
label: "事故图片",
render: (
<Upload
onGetRemoveFile={(file) => {
setDeleteImageFiles([...deleteImageFiles, file]);
}}
/>
),
span: 24
},
// 添加附件上传
{
name: "attachmentFiles",
label: "事故附件",
render: (
<Upload
fileType="file"
onGetRemoveFile={(file) => {
setDeleteAttachmentFiles([...deleteAttachmentFiles, file]);
}}
/>
),
span: 24
},
]}
/>
</Modal>
@ -211,18 +355,34 @@ function AddModalComponent(props) {
function InfoModalComponent(props) {
const [info, setInfo] = useState({});
const [imageFiles, setImageFiles] = useState([]);
const [attachmentFiles, setAttachmentFiles] = useState([]);
const typeName = props.typeName;
const { getFile } = useGetFile();
useEffect(() => {
if (props.currentId) {
props["accidentInfo"]({ id: props.currentId }).then((res) => {
setInfo(res.data);
});
loadData();
}
}, [props.currentId]);
const loadData = async () => {
// 获取基本信息
const { data } = await props["accidentInfo"]({ id: props.currentId });
setInfo(data);
// 获取图片和附件
const images = await getFile({ eqType: UPLOAD_FILE_TYPE_ENUM["136"], eqForeignKey: props.currentId });
const attachments = await getFile({ eqType: UPLOAD_FILE_TYPE_ENUM["137"], eqForeignKey: props.currentId });
setImageFiles(images);
setAttachmentFiles(attachments);
};
return (
<Modal
open={props.visible}
open
onCancel={props.onCancel}
footer={<Button onClick={props.onCancel}>关闭</Button>}
title={`查看${typeName}`}
@ -251,6 +411,38 @@ function InfoModalComponent(props) {
{ children: info.suggestions, label: "考核建议" },
{ children: info.measures, label: "整改措施" },
{ children: info.reportDate, label: "报出日期" },
// 显示图片
{
label: "事故图片",
children: (
<div>
{imageFiles.map((file, index) => (
<div key={index} style={{ marginBottom: 8 }}>
<a href={file.url} target="_blank" rel="noopener noreferrer">
{file.originalName}
</a>
</div>
))}
{imageFiles.length === 0 && <span></span>}
</div>
)
},
// 显示附件
{
label: "事故附件",
children: (
<div>
{attachmentFiles.map((file, index) => (
<div key={index} style={{ marginBottom: 8 }}>
<a href={file.url} target="_blank" rel="noopener noreferrer">
{file.originalName}
</a>
</div>
))}
{attachmentFiles.length === 0 && <span></span>}
</div>
)
},
]}
/>
</Modal>

View File

@ -0,0 +1,15 @@
import Accident from "../../Accident/components/Accident";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
function AccidentContainer(props) {
const {corpinfoId,eqAccidentType} = useGetUrlQuery();
return (
<Accident type={Number(eqAccidentType)}
isSupervise={true}
corpinfoId={corpinfoId}
{...props}
/>
);
}
export default AccidentContainer;

View File

@ -0,0 +1,98 @@
import {Connect} from "@cqsjjb/jjb-dva-runtime";
import {Button, Form, Space} from "antd";
import {useEffect, useState} from "react";
import Search from "zy-react-library/components/Search";
import Table from "zy-react-library/components/Table";
import useTable from "zy-react-library/hooks/useTable";
import {NS_ACCIDENT} from "~/enumerate/namespace";
import useDictionary from "zy-react-library/hooks/useDictionary";
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
import useUrlQueryCriteria from "zy-react-library/hooks/useUrlQueryCriteria";
const TYPE = [
{name: "事件", bianma: "1"},
{name: "事故", bianma: "2"},
]
function SuperviseAccident(props) {
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [form] = Form.useForm();
const {getDictionary} = useDictionary();
const [accidentType, setAccidentType] = useState([]);
const {getUrlCriteriaQuery} = useUrlQueryCriteria();
const [accidentCountByCorpinfoAndType, setAccidentCountByCorpinfoAndType] = useState([]);
useEffect(() => {
getDictionary({dictValue: "accidentType"}).then(res => {
setAccidentType(res);
});
}, []);
const getAccidentCountByCorpinfoAndType=async (corpinfoIds,type)=>{
const {data} = await props["accidentCountByCorpinfoAndType"]({corpinfoIds,eqAccidentType:type})
setAccidentCountByCorpinfoAndType(data)
}
const {tableProps, getData} = useTable(props["getCorpInfoList"], {
form,
onSuccess: ({data}) => {
getAccidentCountByCorpinfoAndType(data.map(item => item.id),form.getFieldValue("eqAccidentType"))
},
});
const getAccidentCount=(id,type)=>{
return accidentCountByCorpinfoAndType.find(item => item.corpinfoId === id && item.incidentType === type)?.[`count`] || 0;
}
const searchType = getUrlCriteriaQuery("searchFormKeys","searchFormValues").eqAccidentType;
return (
<div style={{padding: 10}}>
<Search
form={form}
values={{
eqAccidentType: "1",
}}
onFinish={getData}
options={[
{name: "likecorpName", label: `公司名称`},
{name: "eqAccidentType", label: `类型`, render: FORM_ITEM_RENDER_ENUM.SELECT, items: TYPE,componentProps: {allowClear: false }},
]}
/>
<Table
rowSelection={{
selectedRowKeys,
onChange: selectedRowKeys => setSelectedRowKeys(selectedRowKeys),
}}
columns={[
{dataIndex: "corpName", title: `所属公司`},
...accidentType.map(item => ({
dataIndex: `countByCorpinfoAndType_${item.dictValue}`,
title: `${item.dictLabel}`,
render: (_, record) => (
getAccidentCount(record.id,item.dictValue)
),
})),
{
title: "操作",
width: 160,
fixed: "right",
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => {
props.history.push(`./info?corpinfoId=${record.id}&eqAccidentType=${searchType}`)
}}
>
查看
</Button>
</Space>
),
},
]}
{...tableProps}
/>
</div>
);
}
export default Connect([NS_ACCIDENT], true)(SuperviseAccident);

View File

@ -0,0 +1,4 @@
function SuperviseAccidentContainer(props) {
return props.children;
}
export default SuperviseAccidentContainer;