消防基础路由

main
wangyunfei 1 month ago
parent f5bc63501c
commit 373ccbc654

@ -24,6 +24,12 @@ export default [
name: 'business', name: 'business',
component: './nav_system_content/SystemContentList', component: './nav_system_content/SystemContentList',
routes: [ routes: [
// 基础信息管理
{
path: '/topnavbar00/business/basic',
name: 'basic',
component: './business_basic/basic',
},
// 安全管理基础信息 // 安全管理基础信息
{ {
path: '/topnavbar00/business/basicinformation', path: '/topnavbar00/business/basicinformation',

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

@ -0,0 +1,66 @@
import React, { useState } from 'react';
import { Card, Row, Col, Statistic, Progress, Button, Space } from 'antd';
import styles from './basic.less';
import ResponsibilityImplementation from './module/ResponsibilityImplementation'; //责任落实
import OnlineMonitoring from './module/OnlineMonitoring'; //在线监测预警
import RiskAssessment from './module/RiskAssessment'; //风险管控
import EvaluationReport from './module/EvaluationReport'; //评估报告
const SafeMajorHazardList = () => {
const [activeModule, setActiveModule] = useState('organization');
const handleModuleClick = (module) => {
setActiveModule(module)
}
const renderModule = () => {
switch (activeModule) {
case 'organization':
return <ResponsibilityImplementation />;
case 'equipment':
return <OnlineMonitoring />;
case 'firefighting':
return <RiskAssessment />;
case 'other':
return <EvaluationReport />;
default:
return <ResponsibilityImplementation />;
}
};
return (
<div className={styles.container}>
<div className={styles.TopButton}>
<Button
className={`${styles.TopButtonItem} ${activeModule === "organization" ? styles.active : ""}`}
onClick={() => handleModuleClick("organization")}
>组织机构管理
</Button>
<Button
className={`${styles.TopButtonItem} ${activeModule === "equipment" ? styles.active : ""}`}
onClick={() => handleModuleClick("equipment")}
>设备设施管理
</Button>
<Button
className={`${styles.TopButtonItem} ${activeModule === "firefighting" ? styles.active : ""}`}
onClick={() => handleModuleClick("firefighting")}
>建筑消防与器材管理
</Button>
<Button
className={`${styles.TopButtonItem} ${activeModule === "other" ? styles.active : ""}`}
onClick={() => handleModuleClick("other")}
>其他管理
</Button>
</div>
<div className={styles.content}>
{renderModule()}
</div>
</div>
);
};
export default SafeMajorHazardList;

@ -0,0 +1,66 @@
.container {
background-color: transparent;
width: 100%;
height: 89vh;
overflow: hidden;
display: flex;
flex-direction: column;
.TopButton {
background-color: white;
width: 100%;
padding: 10px 30px;
display: flex;
gap: 24px;
margin-left: 6px;
.TopButtonItem {
background-color: transparent !important;
color: #333333 !important;
font-family: 'PingFang SC', sans-serif !important;
font-weight: 500 !important;
font-size: 14px !important;
line-height: 100% !important;
border-radius: 8px !important;
padding: 6px 10px !important;
height: auto !important;
border: none !important;
box-shadow: none !important;
position: relative !important;
&:hover {
color: #333333 !important;
border: none !important;
}
&:focus {
color: #2E4CD4 !important;
border: none !important;
}
&.active {
color: #2E4CD4 !important;
&::after {
content: '';
position: absolute;
bottom: -10px;
left: 0;
right: 0;
width: 100%;
height: 4px;
background-color: #2E4CD4;
border-radius: 0;
opacity: 1;
}
}
}
}
.content {
// ======== 内容区域样式 ========
flex: 1; // ======== 占据剩余空间 ========
overflow-y: auto; // ======== 允许垂直滚动 ========
padding: 0; // ======== 无内边距 ========
}
}

@ -0,0 +1,271 @@
import { useState, useEffect } from 'react'
import { Col, DatePicker, Form, Input, Modal, Row, Select } from 'antd'
import SelectDeptTree from '@/components/SelectDeptTree'
import SelectOrganTree from '@/components/SelectOrganTree'
import datadictionary from '@/utils/dataDictionary'
import { formatDictOptions, verifyPhone } from '@/utils/globalCommon'
import { NumberInput } from '@/components/NumberInput'
import styles from '../StaffSheetList.less'
import style from '@/global.less'
import dayjs from 'dayjs'
import { formatDate } from '@/utils/formatUtils'
const { Item: FormItem } = Form
const { TextArea } = Input
const dictData = datadictionary
//新增表单
let getDeptTreeBySelectTree
let getOrganTreeBySelectTree
const StaffSheetCreateForm = (props => {
const [form] = Form.useForm()
const [jobStatus, setJobStatus] = useState('1')
const {
modalVisible,
handleAdd,
handleModalVisible,
loading,
dispatch,
selectDeptTree,
selectOrganTree
} = props
useEffect(() => {
form.setFieldsValue({
user_type: 'employee',
job_status: '1',
mgr_type: '0'
})
}, [])
const selectedDeptTreeValue = (deptRecord) => {
getDeptTreeBySelectTree = deptRecord
}
const selectedOrganTreeValue = (orgRecord) => {
getOrganTreeBySelectTree = orgRecord
}
const parentDeptTreeMethod = {
dispatch: dispatch,
selectDeptTree: selectDeptTree,
selectedDeptTreeValue: selectedDeptTreeValue,
}
const parentOrganTreeMethod = {
dispatch: dispatch,
selectOrganTree: selectOrganTree,
selectedOrganTreeValue: selectedOrganTreeValue
}
const okHandle = () => {
form.validateFields()
.then(fieldsValue => {
form.resetFields()
fieldsValue.birthday = formatDate(fieldsValue.birthday, 'YYYY-MM-DD')
fieldsValue.hiredate = formatDate(fieldsValue.hiredate, 'YYYY-MM-DD')
fieldsValue.departure_time = formatDate(fieldsValue.departure_time, 'YYYY-MM-DD')
fieldsValue.posts = fieldsValue.posts ? JSON.stringify(fieldsValue.posts) : null
// if (getDeptTreeBySelectTree) {
// fieldsValue.dept_code = getDeptTreeBySelectTree.dept_code
// fieldsValue.dept_name = getDeptTreeBySelectTree.title
// }
if (getOrganTreeBySelectTree) {
fieldsValue.org_code = getOrganTreeBySelectTree.org_code
fieldsValue.org_name = getOrganTreeBySelectTree.title
}
handleAdd(fieldsValue)
})
.catch(errInfo => {})
}
const afterClose = () =>{
form.resetFields();
}
const handleJobStatusChange = (value) => {
setJobStatus(value)
}
return (
<Modal
width={800}
height={550}
bodyStyle={{ height: '500px', overflowY: 'auto' }}
className={style.createForm}
centered
destroyOnClose
title='新增'
open={modalVisible}
onOk={okHandle}
onCancel={() => handleModalVisible()}
afterClose={() => afterClose()}
confirmLoading={loading}
>
<Form form={form} layout='vertical' requiredMark={false}>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='登录账号' name='user_name' rules={[{ required: true, message: '请输入至少2个字符的用户名', min: 2 }]}>
<Input placeholder='请输入' />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='用户名称' name='user_name_cn' rules={[{ required: true, message: '请输入至少2个字符的用户名称', min: 2 }]}>
<Input placeholder='请输入' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='用户性别' name='sex'>
<Select options={formatDictOptions(dictData.sys_user_sex, 'dict_label', 'dict_value')} placeholder='请选择' />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='用户生日' name='birthday'>
<DatePicker format='YYYY-MM-DD' placeholder='请选择' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='手机号码' name='phone' rules={[{ required: false, min: 1, validator: verifyPhone }]}>
<NumberInput placeholder='请输入' style={{width: '100%'}} maxLength={11} />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='电子邮箱' name='email' rules={[{ type: 'email' }]}>
<Input placeholder='请输入' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='机构名称' name='org_code'>
<SelectOrganTree {...parentOrganTreeMethod} />
</FormItem>
</Col>
{/*<Col md={12} sm={24}>*/}
{/* <FormItem label='部门名称' name='dept_code'>*/}
{/* <SelectDeptTree {...parentDeptTreeMethod} placeholder={'请选择部门'} />*/}
{/* </FormItem>*/}
{/*</Col>*/}
<Col md={12} sm={24}>
<FormItem label='所属岗位' name='posts'>
<Select
mode='multiple'
allowClear
placeholder='请选择'
options={formatDictOptions(dictData.sys_user_post, 'dict_label', 'dict_value')}
/>
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
{/*<Col md={12} sm={24}>
<FormItem
label={
<span>
密码
<em className={styles.optional}>
<Tooltip title='默认密码123456'>
<InfoCircleOutlined style={{ marginLeft: 4 }} />
</Tooltip>
</em>
</span>
}
name='password'
initialValue={'123456'}
rules={[{required: true, message: '请输入至少6个字符的密码', min: 6}]}>
<Input placeholder='请输入' type='password'/>
</FormItem>
</Col>*/}
<Col md={12} sm={24}>
<FormItem label='在职状态' name='job_status'>
<Select
placeholder='请选择'
options={formatDictOptions(dictData.sys_job_status, 'dict_label', 'dict_value')}
onChange={handleJobStatusChange}
/>
</FormItem>
</Col>
{jobStatus === '1' ?
<Col md={12} sm={24}>
<FormItem label='入职时间' name='hiredate' initialValue={dayjs().endOf('day')}>
<DatePicker format='YYYY-MM-DD' placeholder='请选择' />
</FormItem>
</Col>
:
<Col md={12} sm={24}>
<FormItem label='离职时间' name='departure_time'>
<DatePicker format='YYYY-MM-DD' placeholder='请选择' />
</FormItem>
</Col>
}
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='员工类型' name='user_type'>
<Select
placeholder='请选择'
options={formatDictOptions(dictData.sys_user_type, 'dict_label', 'dict_value')}
/>
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='员工工号' name='emp_no'>
<Input placeholder='请输入' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='门禁卡号' name='access_card_no'>
<Input placeholder='请输入' />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='是否是管理员' name='mgr_type'>
<Select
style={{width: '100%'}}
placeholder='请选择'
options={formatDictOptions(dictData.sys_mgr_type, 'dict_label', 'dict_value')}
/>
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={24} sm={24}>
<FormItem label='备注' name='remarks'>
<TextArea rows={4} />
</FormItem>
</Col>
</Row>
</Form>
</Modal>
)
})
export default StaffSheetCreateForm

@ -0,0 +1,113 @@
import { useEffect } from 'react'
import { Button, Col, Form, Input, Row } from 'antd'
import { UpOutlined, SearchOutlined, RedoOutlined } from '@ant-design/icons'
import SelectDeptTree from '@/components/SelectDeptTree'
import SelectOrganTree from '@/components/SelectOrganTree'
import style from '@/global.less'
const { Item: FormItem } = Form
let getDeptTreeBySelectTree
let getOrganTreeBySelectTree
const StaffSheetRenderAdvancedForm = (props) => {
const [form] = Form.useForm()
const { dispatch, handleSearch, handleFormReset, toggleForm, selectDeptTree, selectOrganTree, params } = props
useEffect(() => {
form.setFieldsValue({
user_name: params?.user_name,
user_name_cn: params?.user_name_cn,
deptname: params?.deptname,
orgname: params?.orgname,
})
}, [params])
const onFinish = values => {
// if (getDeptTreeBySelectTree) {
// values.dept_code = getDeptTreeBySelectTree.dept_code
// values.deptname = getDeptTreeBySelectTree.title
// }
if (getOrganTreeBySelectTree) {
values.org_code = getOrganTreeBySelectTree.org_code
values.orgname = getOrganTreeBySelectTree.title
}
handleSearch(values)
}
const myHandleFormReset = () => {
form.resetFields()
handleFormReset()
}
const selectedDeptTreeValue = (deptRecord) => {
getDeptTreeBySelectTree = deptRecord
}
const selectedOrganTreeValue = (orgRecord) => {
getOrganTreeBySelectTree = orgRecord
}
const parentDeptTreeMethod = {
dispatch: dispatch,
selectDeptTree: selectDeptTree,
selectedDeptTreeValue: selectedDeptTreeValue
}
const parentOrganTreeMethod = {
dispatch: dispatch,
selectOrganTree: selectOrganTree,
selectedOrganTreeValue: selectedOrganTreeValue
}
return (
<Form form={form} onFinish={onFinish} layout='inline'>
<Row gutter={{ md: 8, lg: 24, xl: 48 }} className={style.searchInput}>
<Col md={8} sm={24}>
<FormItem label='用户名' name='user_name'>
<Input placeholder='请输入' />
</FormItem>
</Col>
<Col md={8} sm={24}>
<FormItem label='用户名称' name='user_name_cn'>
<Input placeholder='请输入' />
</FormItem>
</Col>
<Col md={8} sm={24}>
<FormItem label='机构代码' name='orgname'>
<SelectOrganTree {...parentOrganTreeMethod} />
</FormItem>
</Col>
</Row>
<Row gutter={{md: 8, lg: 24, xl: 48}} className={style.searchBox}>
{/*<Col md={8} sm={24}>*/}
{/* <FormItem label='部门名称' name='deptname'>*/}
{/* <SelectDeptTree placeholder={'请选择部门'} {...parentDeptTreeMethod} />*/}
{/* </FormItem>*/}
{/*</Col>*/}
<Col md={24} sm={24}>
<div className={style.searchBtn}>
<Button type='primary' htmlType='submit'>
查询
</Button>
<Button onClick={myHandleFormReset}>
重置
</Button>
<a onClick={() => toggleForm(form)}>
收起 <UpOutlined />
</a>
</div>
</Col>
</Row>
</Form>
)
}
export default StaffSheetRenderAdvancedForm

@ -0,0 +1,81 @@
import { useEffect } from 'react'
import {Button, Col, Form, Input, Row, DatePicker, Select} from 'antd'
import {DownOutlined, RedoOutlined, SearchOutlined} from '@ant-design/icons'
import style from '@/global.less'
import dayjs from 'dayjs'
const { Item: FormItem } = Form
const StaffSheetRenderSimpleForm = (props) => {
const [form] = Form.useForm()
const { handleSearch, handleFormReset, toggleForm, params } = props
useEffect(() => {
form.setFieldsValue({
user_name: params?.user_name,
user_name_cn: params?.user_name_cn,
})
}, [params])
const onFinish = values => {
handleSearch(values)
}
const myHandleFormReset = () => {
form.resetFields()
handleFormReset()
}
return (
<Form form={form} onFinish={onFinish} layout='inline'>
<Row gutter={{ md: 8, lg: 24, xl: 48 }} className={style.searchInput}>
<Col md={4} sm={24}>
<FormItem label='我的查询条件' name='wdcxtj'>
<Select
placeholder='请选择'
options={[]}
/>
</FormItem>
</Col>
<Col md={4} sm={24}>
<FormItem label='日期' name='rq' rules={[{ required: true, message: '请选择日期!' }]}>
<DatePicker defaultValue={dayjs('2025-04-10', 'YYYY-MM-DD')} format='YYYY-MM-DD' />
</FormItem>
</Col>
<Col md={4} sm={24}>
<FormItem label='工作地点' name='gzdd'>
<Input placeholder='请输入' />
</FormItem>
</Col>
<Col md={4} sm={24}>
<FormItem label='工号' name='gh'>
<Input placeholder='请输入' defaultValue="123456"/>
</FormItem>
</Col>
<Col md={4} sm={24}>
<FormItem label='名称' name='gh'>
<Input placeholder='请输入'/>
</FormItem>
</Col>
<Col md={4} sm={24}>
<div className={style.searchBtn}>
<Button type='primary' htmlType='submit'>
查询
</Button>
<Button onClick={myHandleFormReset}>
重置
</Button>
</div>
</Col>
</Row>
</Form>
)
}
export default StaffSheetRenderSimpleForm

@ -0,0 +1,362 @@
import { useState, useEffect } from 'react'
import { Col, DatePicker, Form, Input, Modal, Row, Select } from 'antd'
import SelectOrganTree from '@/components/SelectOrganTree'
import datadictionary from '@/utils/dataDictionary'
import { formatDictOptions, verifyPhone } from '@/utils/globalCommon'
import { NumberInput } from '@/components/NumberInput'
import styles from '../StaffSheetList.less'
import style from '@/global.less'
import dayjs from 'dayjs'
import { formatDate, formatDateObject } from '@/utils/formatUtils'
const { Item: FormItem } = Form
const { TextArea } = Input
const dictData = datadictionary
//新增表单
let getDeptTreeBySelectTree
let getOrganTreeBySelectTree
const StaffSheetUpdateForm = (props) => {
const [form] = Form.useForm()
const [jobStatus, setJobStatus] = useState('1')
const [userStatus, setUserStatus] = useState('0')
const {
handleUpdate,
updateModalVisible,
handleUpdateModalVisible,
values,
loading,
dispatch,
selectDeptTree,
selectOrganTree
} = props
const selectedDeptTreeValue = (deptRecord) => {
getDeptTreeBySelectTree = deptRecord
}
const selectedOrganTreeValue = (orgRecord) => {
getOrganTreeBySelectTree = orgRecord
}
const parentDeptTreeMethod = {
dispatch: dispatch,
selectDeptTree: selectDeptTree,
selectedDeptTreeValue: selectedDeptTreeValue,
}
const parentOrganTreeMethod = {
dispatch: dispatch,
selectOrganTree: selectOrganTree,
selectedOrganTreeValue: selectedOrganTreeValue
}
useEffect(() => {
setJobStatus(values.job_status)
setUserStatus(values.status)
form.setFieldsValue({
user_id: values.user_id,
user_name: values.user_name,
user_name_cn: values.user_name_cn,
user_name_en: values.user_name_en,
password: values.password,
email: values.email,
phone: values.phone,
landline: values.landline,
sex: values.sex,
avatar: values.avatar,
sign: values.sign,
tags: values.tags,
id_card: values.id_card,
birthday: formatDateObject(values.birthday, 'YYYY-MM-DD'),
job_status: values.job_status,
hiredate: formatDateObject(values.hiredate, 'YYYY-MM-DD'),
departure_time: formatDateObject(values.departure_time, 'YYYY-MM-DD'),
user_type: values.user_type,
emp_no: values.emp_no,
access_card_no: values.access_card_no,
country: values.country,
province: values.province,
city: values.city,
address: values.address,
work_addr: values.work_addr,
floor: values.floor,
inprovince: values.inprovince,
// dept_code: values.dept_code,
// dept_name: values.dept_name,
inner_dept_code: values.inner_dept_code,
org_code: values.org_code,
org_name: values.org_name,
inner_org_code: values.inner_org_code,
posts: values.posts ? JSON.parse(values.posts) : [],
wx_openid: values.wx_openid,
wx_mpopenid: values.wx_mpopenid,
wx_miniopenid: values.wx_miniopenid,
wx_unionid: values.wx_unionid,
mobile_imei: values.mobile_imei,
device_num: values.device_num,
al_taobao: values.al_taobao,
al_alipay: values.al_alipay,
al_dingding: values.al_dingding,
is_system_user: values.is_system_user,
mgr_type: values.mgr_type,
pwd_security_level: values.pwd_security_level,
pwd_update_date: values.pwd_update_date,
last_login_ip: values.last_login_ip,
last_login_date: values.last_login_date,
freeze_date: values.freeze_date,
freeze_cause: values.freeze_cause,
zindex: values.zindex,
wx_msg: values.wx_msg,
email_msg: values.email_msg,
system_msg: values.system_msg,
remarks: values.remarks,
status: values.status,
creator: values.creator,
create_date: values.create_date,
updater: values.updater,
update_date: values.update_date
})
}, [])
const handleLocalUpdate = () => {
form
.validateFields()
.then(fieldsValue => {
const formVals = {...values, ...fieldsValue}
formVals.birthday = formatDate(formVals.birthday, 'YYYY-MM-DD')
formVals.hiredate = formatDate(formVals.hiredate, 'YYYY-MM-DD')
formVals.departure_time = formatDate(formVals.departure_time, 'YYYY-MM-DD')
formVals.posts = formVals.posts ? JSON.stringify(formVals.posts) : null
formVals.freeze_date = '3' === formVals.status ? formatDate(dayjs().endOf('day'), 'YYYY-MM-DD') : null
formVals.freeze_cause = '3' === formVals.status ? formVals.freeze_cause : null
// if (getDeptTreeBySelectTree) {
// formVals.dept_code = getDeptTreeBySelectTree.dept_code
// formVals.dept_name = getDeptTreeBySelectTree.title
// }
if (getOrganTreeBySelectTree) {
formVals.org_code = getOrganTreeBySelectTree.org_code
formVals.org_name = getOrganTreeBySelectTree.title
}
handleUpdate(formVals)
})
.catch(errInfo => {})
}
const afterClose = () =>{
form.resetFields();
}
const handleJobStatusChange = (value) => {
setJobStatus(value)
}
const handleUserStatusChange = (value) => {
setUserStatus(value)
}
return (
<Modal
width={800}
height={550}
bodyStyle={{ height: '500px', overflowY: 'auto' }}
className={style.updateForm}
centered
destroyOnClose
title='修改'
open={updateModalVisible}
onOk={() => handleLocalUpdate()}
onCancel={() => handleUpdateModalVisible()}
afterClose={() => afterClose()}
confirmLoading={loading}
>
<Form form={form} layout='vertical' requiredMark={false}>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='登录账号' name='user_name' rules={[{ required: true, message: '请输入至少2个字符的用户名', min: 2 }]}>
<Input placeholder='请输入' />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='用户名称' name='user_name_cn' rules={[{ required: true, message: '请输入至少2个字符的用户名称', min: 2 }]}>
<Input placeholder='请输入' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='用户性别' name='sex'>
<Select options={formatDictOptions(dictData.sys_user_sex, 'dict_label', 'dict_value')} placeholder='请选择' />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='用户生日' name='birthday'>
<DatePicker format='YYYY-MM-DD' placeholder='请选择' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='手机号码' name='phone' rules={[{ required: false, min: 1, validator: verifyPhone }]}>
<NumberInput placeholder='请输入' style={{width: '100%'}} maxLength={11} />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='电子邮箱' name='email' rules={[{ type: 'email' }]}>
<Input placeholder='请输入' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='机构名称' name='org_code'>
<SelectOrganTree {...parentOrganTreeMethod} />
</FormItem>
</Col>
{/*<Col md={12} sm={24}>*/}
{/* <FormItem label='部门名称' name='dept_code'>*/}
{/* <SelectDeptTree {...parentDeptTreeMethod} placeholder={'请选择部门'} />*/}
{/* </FormItem>*/}
{/*</Col>*/}
<Col md={12} sm={24}>
<FormItem label='所属岗位' name='posts'>
<Select
mode='multiple'
allowClear
placeholder='请选择'
options={formatDictOptions(dictData.sys_user_post, 'dict_label', 'dict_value')}
/>
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
{/*<Col md={12} sm={24}>
<FormItem
label={
<span>
密码
<em className={styles.optional}>
<Tooltip title='默认密码123456'>
<InfoCircleOutlined style={{ marginLeft: 4 }} />
</Tooltip>
</em>
</span>
}
name='password'
initialValue={'123456'}
rules={[{required: true, message: '请输入至少6个字符的密码', min: 6}]}>
<Input placeholder='请输入' type='password'/>
</FormItem>
</Col>*/}
<Col md={12} sm={24}>
<FormItem label='在职状态' name='job_status'>
<Select
placeholder='请选择'
options={formatDictOptions(dictData.sys_job_status, 'dict_label', 'dict_value')}
onChange={handleJobStatusChange}
/>
</FormItem>
</Col>
{jobStatus === '1' ?
<Col md={12} sm={24}>
<FormItem label='入职时间' name='hiredate' initialValue={dayjs().endOf('day')}>
<DatePicker format='YYYY-MM-DD' placeholder='请选择' />
</FormItem>
</Col>
:
<Col md={12} sm={24}>
<FormItem label='离职时间' name='departure_time'>
<DatePicker format='YYYY-MM-DD' placeholder='请选择' />
</FormItem>
</Col>
}
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='员工类型' name='user_type'>
<Select
placeholder='请选择'
options={formatDictOptions(dictData.sys_user_type, 'dict_label', 'dict_value')}
/>
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='员工工号' name='emp_no'>
<Input placeholder='请输入' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='门禁卡号' name='access_card_no'>
<Input placeholder='请输入' />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='是否是管理员' name='mgr_type'>
<Select
style={{width: '100%'}}
placeholder='请选择'
options={formatDictOptions(dictData.sys_mgr_type, 'dict_label', 'dict_value')}
/>
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='状态' name='status'>
<Select
placeholder='请选择'
options={formatDictOptions(dictData.user_status, 'dict_label', 'dict_value')}
onChange={handleUserStatusChange}
/>
</FormItem>
</Col>
{ userStatus === '3' &&
<Col md={12} sm={24}>
<FormItem label='冻结原因' name='freeze_cause'>
<Input placeholder='请输入' />
</FormItem>
</Col>
}
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={24} sm={24}>
<FormItem label='备注' name='remarks'>
<TextArea rows={4} />
</FormItem>
</Col>
</Row>
</Form>
</Modal>
)
}
export default StaffSheetUpdateForm

@ -0,0 +1,299 @@
import { useState, useEffect } from 'react'
import {Col, DatePicker, Form, Input, Modal, Row, Select} from 'antd'
import datadictionary from '@/utils/dataDictionary'
import style from "@/global.less";
import {formatDictOptions, verifyPhone} from "@/utils/globalCommon";
import {NumberInput} from "@/components/NumberInput";
import dayjs from "dayjs";
import SelectOrganTree from "@/components/SelectOrganTree";
import {formatDateObject} from "@/utils/formatUtils";
const { Item: FormItem } = Form
const { TextArea } = Input
const dictData = datadictionary
const StaffSheetViewForm = (props) => {
const [form] = Form.useForm()
const [jobStatus, setJobStatus] = useState('1')
const [userStatus, setUserStatus] = useState('0')
const { viewModalVisible, handleViewModalVisible, values } = props
useEffect(() => {
setJobStatus(values.job_status)
setUserStatus(values.status)
form.setFieldsValue({
user_id: values.user_id,
user_name: values.user_name,
user_name_cn: values.user_name_cn,
user_name_en: values.user_name_en,
password: values.password,
email: values.email,
phone: values.phone,
landline: values.landline,
sex: values.sex,
avatar: values.avatar,
sign: values.sign,
tags: values.tags,
id_card: values.id_card,
birthday: formatDateObject(values.birthday, 'YYYY-MM-DD'),
job_status: values.job_status,
hiredate: formatDateObject(values.hiredate, 'YYYY-MM-DD'),
departure_time: formatDateObject(values.departure_time, 'YYYY-MM-DD'),
user_type: values.user_type,
emp_no: values.emp_no,
access_card_no: values.access_card_no,
country: values.country,
province: values.province,
city: values.city,
address: values.address,
work_addr: values.work_addr,
floor: values.floor,
inprovince: values.inprovince,
// dept_code: values.dept_code,
// dept_name: values.dept_name,
inner_dept_code: values.inner_dept_code,
org_code: values.org_code,
org_name: values.org_name,
inner_org_code: values.inner_org_code,
posts: values.posts ? JSON.parse(values.posts) : [],
wx_openid: values.wx_openid,
wx_mpopenid: values.wx_mpopenid,
wx_miniopenid: values.wx_miniopenid,
wx_unionid: values.wx_unionid,
mobile_imei: values.mobile_imei,
device_num: values.device_num,
al_taobao: values.al_taobao,
al_alipay: values.al_alipay,
al_dingding: values.al_dingding,
is_system_user: values.is_system_user,
mgr_type: values.mgr_type,
pwd_security_level: values.pwd_security_level,
pwd_update_date: values.pwd_update_date,
last_login_ip: values.last_login_ip,
last_login_date: values.last_login_date,
freeze_date: values.freeze_date,
freeze_cause: values.freeze_cause,
zindex: values.zindex,
wx_msg: values.wx_msg,
email_msg: values.email_msg,
system_msg: values.system_msg,
remarks: values.remarks,
status: values.status,
creator: values.creator,
create_date: values.create_date,
updater: values.updater,
update_date: values.update_date
})
}, [])
const afterClose = () =>{
form.resetFields();
}
const handleJobStatusChange = (value) => {
setJobStatus(value)
}
const handleUserStatusChange = (value) => {
setUserStatus(value)
}
return (
<Modal
width={800}
height={550}
bodyStyle={{ height: '500px', overflowY: 'auto' }}
className={style.viewForm}
centered
destroyOnClose
title='查看'
open={viewModalVisible}
onOk={() => handleViewModalVisible()}
onCancel={() => handleViewModalVisible()}
afterClose={() => afterClose()}
>
<Form form={form} layout='vertical' requiredMark={false} style={{pointerEvents: 'none'}}>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='登录账号' name='user_name' rules={[{ required: true, message: '请输入至少2个字符的用户名', min: 2 }]}>
<Input placeholder='请输入' />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='用户名称' name='user_name_cn' rules={[{ required: true, message: '请输入至少2个字符的用户名称', min: 2 }]}>
<Input placeholder='请输入' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='用户性别' name='sex'>
<Select options={formatDictOptions(dictData.sys_user_sex, 'dict_label', 'dict_value')} placeholder='请选择' />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='用户生日' name='birthday'>
<DatePicker format='YYYY-MM-DD' placeholder='请选择' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='手机号码' name='phone' rules={[{ required: false, min: 1, validator: verifyPhone }]}>
<NumberInput placeholder='请输入' style={{width: '100%'}} maxLength={11} />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='电子邮箱' name='email' rules={[{ type: 'email' }]}>
<Input placeholder='请输入' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='机构名称' name='org_name'>
<Input placeholder='请输入' />
</FormItem>
</Col>
{/*<Col md={12} sm={24}>*/}
{/* <FormItem label='部门名称' name='dept_code'>*/}
{/* <SelectDeptTree {...parentDeptTreeMethod} placeholder={'请选择部门'} />*/}
{/* </FormItem>*/}
{/*</Col>*/}
<Col md={12} sm={24}>
<FormItem label='所属岗位' name='posts'>
<Select
mode='multiple'
allowClear
placeholder='请选择'
options={formatDictOptions(dictData.sys_user_post, 'dict_label', 'dict_value')}
/>
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
{/*<Col md={12} sm={24}>
<FormItem
label={
<span>
密码
<em className={styles.optional}>
<Tooltip title='默认密码123456'>
<InfoCircleOutlined style={{ marginLeft: 4 }} />
</Tooltip>
</em>
</span>
}
name='password'
initialValue={'123456'}
rules={[{required: true, message: '请输入至少6个字符的密码', min: 6}]}>
<Input placeholder='请输入' type='password'/>
</FormItem>
</Col>*/}
<Col md={12} sm={24}>
<FormItem label='在职状态' name='job_status'>
<Select
placeholder='请选择'
options={formatDictOptions(dictData.sys_job_status, 'dict_label', 'dict_value')}
onChange={handleJobStatusChange}
/>
</FormItem>
</Col>
{jobStatus === '1' ?
<Col md={12} sm={24}>
<FormItem label='入职时间' name='hiredate' initialValue={dayjs().endOf('day')}>
<DatePicker format='YYYY-MM-DD' placeholder='请选择' />
</FormItem>
</Col>
:
<Col md={12} sm={24}>
<FormItem label='离职时间' name='departure_time'>
<DatePicker format='YYYY-MM-DD' placeholder='请选择' />
</FormItem>
</Col>
}
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='员工类型' name='user_type'>
<Select
placeholder='请选择'
options={formatDictOptions(dictData.sys_user_type, 'dict_label', 'dict_value')}
/>
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='员工工号' name='emp_no'>
<Input placeholder='请输入' />
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='门禁卡号' name='access_card_no'>
<Input placeholder='请输入' />
</FormItem>
</Col>
<Col md={12} sm={24}>
<FormItem label='是否是管理员' name='mgr_type'>
<Select
style={{width: '100%'}}
placeholder='请选择'
options={formatDictOptions(dictData.sys_mgr_type, 'dict_label', 'dict_value')}
/>
</FormItem>
</Col>
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={12} sm={24}>
<FormItem label='状态' name='status'>
<Select
placeholder='请选择'
options={formatDictOptions(dictData.user_status, 'dict_label', 'dict_value')}
onChange={handleUserStatusChange}
/>
</FormItem>
</Col>
{ userStatus === '3' &&
<Col md={12} sm={24}>
<FormItem label='冻结原因' name='freeze_cause'>
<Input placeholder='请输入' />
</FormItem>
</Col>
}
</Row>
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={24} sm={24}>
<FormItem label='备注' name='remarks'>
<TextArea rows={4} />
</FormItem>
</Col>
</Row>
</Form>
</Modal>
)
}
export default StaffSheetViewForm

@ -0,0 +1,319 @@
import { deleteByPrimaryKeyForProUser, selectByPrimaryKeyForProUser, insertForProUser, updateForProUser, deleteByMapForProUser,updateByMapForProUser, getOneForProUser,getAllForProUser,queryPageForProUser, countForProUser, insertBatchForProUser, deleteBatchForProUser,updateBatchForProUser, resetPwdForProUser } from '@/services/system/api_prouser';
export default {
namespace: 'safemajorhazard',
state: {
params: {},
data: {
list: [],
pagination: {},
},
},
effects: {
*delete_by_primarykey_for_prouser({ payload, callback }, { select, call, put }) {
const response = yield call(deleteByPrimaryKeyForProUser, payload)
yield put({
type: 'deleteByPrimaryKeyForProUser',
payload: response
})
if (!response.success) {
callback && callback(response)
return
}
const params = yield select(state => state.prouser.params)
const responseData = yield call(queryPageForProUser, params)
yield put({
type: 'queryPageForProUser',
payload: responseData
})
if (callback) callback(response)
},
*select_by_primarykey_for_prouser({ payload, callback }, { call, put }) {
const response = yield call(selectByPrimaryKeyForProUser, payload)
yield put({
type: 'selectByPrimaryKeyForProUser',
payload: response
})
if (callback) callback(response)
},
*insert_for_prouser({ payload, callback }, { select, call, put }) {
const response = yield call(insertForProUser, payload)
yield put({
type: 'insertForProUser',
payload: response
})
if (!response.success) {
callback && callback(response)
return
}
const params = yield select(state => state.prouser.params)
const responseData = yield call(queryPageForProUser, params)
yield put({
type: 'queryPageForProUser',
payload: responseData
})
if (callback) callback(response)
},
*update_for_prouser({ payload, callback }, { select, call, put }) {
const response = yield call(updateForProUser, payload)
yield put({
type: 'updateForProUser',
payload: response
})
if (!response.success) {
callback && callback(response)
return
}
const params = yield select(state => state.prouser.params)
const responseData = yield call(queryPageForProUser, params)
yield put({
type: 'queryPageForProUser',
payload: responseData
})
if (callback) callback(response)
},
*delete_by_map_for_prouser({ payload, callback }, { select, call, put }) {
const response = yield call(deleteByMapForProUser, payload);
yield put({
type: 'deleteByMapForProUser',
payload: response,
});
const params = yield select(state => state.prouser.params);
const responsedata = yield call(queryPageForProUser, params);
yield put({
type: 'queryPageForProUser',
payload: responsedata,
});
if (callback) callback(response);
},
*update_by_map_for_prouser({ payload, callback }, { select, call, put }) {
const response = yield call(updateByMapForProUser, payload);
yield put({
type: 'updateByMapForProUser',
payload: response,
});
const params = yield select(state => state.prouser.params);
const responsedata = yield call(queryPageForProUser, params);
yield put({
type: 'queryPageForProUser',
payload: responsedata,
});
if (callback) callback(response);
},
*get_one_for_prouser({ payload, callback }, { call, put }) {
const response = yield call(getOneForProUser, payload);
yield put({
type: 'getOneForProUser',
payload: response,
});
if (callback) callback(response);
},
*get_all_for_prouser({ payload, callback }, { call, put }) {
const response = yield call(getAllForProUser, payload);
yield put({
type: 'getAllForProUser',
payload: response,
});
if (callback) callback(response);
},
*query_page_for_prouser({ payload, callback }, { select, call, put }) {
const params = yield select(state => state.prouser.params);
const newParams = payload?.resetFlag ? payload : {...params, ...payload};
yield put({
type: 'setQueryPageByParams',
payload: newParams,
});
const response = yield call(queryPageForProUser, newParams);
yield put({
type: 'queryPageForProUser',
payload: response,
});
if (callback) callback(response);
},
*count_for_prouser({ payload, callback }, { call, put }) {
const response = yield call(countForProUser, payload);
yield put({
type: 'countForProUser',
payload: response,
});
if (callback) callback(response);
},
*insert_batch_for_prouser({ payload, callback }, { select, call, put }) {
const response = yield call(insertBatchForProUser, payload);
yield put({
type: 'insertBatchForProUser',
payload: response,
});
const params = yield select(state => state.prouser.params);
const responsedata = yield call(queryPageForProUser, params);
yield put({
type: 'queryPageForProUser',
payload: responsedata,
});
if (callback) callback(response);
},
*delete_batch_for_prouser({ payload, callback }, { select, call, put }) {
const response = yield call(deleteBatchForProUser, payload);
yield put({
type: 'deleteBatchForProUser',
payload: response,
});
const params = yield select(state => state.prouser.params);
const responsedata = yield call(queryPageForProUser, params);
yield put({
type: 'queryPageForProUser',
payload: responsedata,
});
if (callback) callback(response);
},
*update_batch_for_prouser({ payload, callback }, { select, call, put }) {
const response = yield call(updateBatchForProUser, payload);
yield put({
type: 'updateBatchForProUser',
payload: response,
});
const params = yield select(state => state.prouser.params);
const responsedata = yield call(queryPageForProUser, params);
yield put({
type: 'queryPageForProUser',
payload: responsedata,
});
if (callback) callback(response);
},
*resetpwd_for_prouser({ payload, callback }, { select, call, put }) {
const response = yield call(resetPwdForProUser, payload);
yield put({
type: 'resetPwdForProUser',
payload: response,
});
const params = yield select(state => state.prouser.params);
const responsedata = yield call(queryPageForProUser, params);
yield put({
type: 'queryPageForProUser',
payload: responsedata,
});
if (callback) callback(response);
},
},
reducers: {
setQueryPageByParams(state, { payload }) {
return {
...state,
params: {...payload},
};
},
deleteByPrimaryKeyForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
selectByPrimaryKeyForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
insertForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
updateForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
deleteByMapForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
updateByMapForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
getOneForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
getAllForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
queryPageForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
countForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
insertBatchForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
deleteBatchForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
updateBatchForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
resetPwdForProUser(state, action) {
return {
...state,
data: action.payload,
};
},
},
};

@ -0,0 +1,300 @@
import React, { useState, useEffect } from 'react';
import { Card, Table, Button, Modal, Form, Input, Select, message, Space, Tag } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';
import './EvaluationReport.less';
const { Option } = Select;
const EvaluationReport = () => {
const [form] = Form.useForm();
const [dataSource, setDataSource] = useState([]);
const [loading, setLoading] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const [editingRecord, setEditingRecord] = useState(null);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 0,
});
// 模拟数据
const mockData = [
{
id: 1,
reportName: '2024年第一季度安全评估报告',
reportType: '季度评估',
assessmentPeriod: '2024-01-01 至 2024-03-31',
assessor: '张三',
status: '已完成',
createTime: '2024-04-01 10:00:00',
description: '对第一季度安全生产情况进行全面评估',
},
{
id: 2,
reportName: '2024年年度安全评估报告',
reportType: '年度评估',
assessmentPeriod: '2024-01-01 至 2024-12-31',
assessor: '李四',
status: '进行中',
createTime: '2024-01-15 14:30:00',
description: '年度安全生产综合评估',
},
];
useEffect(() => {
fetchData();
}, [pagination.current, pagination.pageSize]);
const fetchData = async () => {
setLoading(true);
try {
// 模拟API调用
setTimeout(() => {
setDataSource(mockData);
setPagination(prev => ({ ...prev, total: mockData.length }));
setLoading(false);
}, 500);
} catch (error) {
message.error('获取数据失败');
setLoading(false);
}
};
const handleAdd = () => {
setEditingRecord(null);
form.resetFields();
setModalVisible(true);
};
const handleEdit = (record) => {
setEditingRecord(record);
form.setFieldsValue(record);
setModalVisible(true);
};
const handleDelete = (record) => {
Modal.confirm({
title: '确认删除',
content: `确定要删除评估报告"${record.reportName}"吗?`,
onOk: () => {
setDataSource(dataSource.filter(item => item.id !== record.id));
message.success('删除成功');
},
});
};
const handleView = (record) => {
Modal.info({
title: '查看评估报告',
content: (
<div>
<p><strong>报告名称</strong>{record.reportName}</p>
<p><strong>报告类型</strong>{record.reportType}</p>
<p><strong>评估周期</strong>{record.assessmentPeriod}</p>
<p><strong>评估人</strong>{record.assessor}</p>
<p><strong>状态</strong>{record.status}</p>
<p><strong>创建时间</strong>{record.createTime}</p>
<p><strong>描述</strong>{record.description}</p>
</div>
),
width: 600,
});
};
const handleSubmit = async (values) => {
try {
if (editingRecord) {
// 编辑
setDataSource(dataSource.map(item =>
item.id === editingRecord.id ? { ...item, ...values } : item
));
message.success('编辑成功');
} else {
// 新增
const newRecord = {
id: Date.now(),
...values,
status: '进行中',
createTime: new Date().toLocaleString(),
};
setDataSource([...dataSource, newRecord]);
message.success('添加成功');
}
setModalVisible(false);
form.resetFields();
} catch (error) {
message.error('操作失败');
}
};
const columns = [
{
title: '报告名称',
dataIndex: 'reportName',
key: 'reportName',
width: 200,
},
{
title: '报告类型',
dataIndex: 'reportType',
key: 'reportType',
width: 120,
},
{
title: '评估周期',
dataIndex: 'assessmentPeriod',
key: 'assessmentPeriod',
width: 200,
},
{
title: '评估人',
dataIndex: 'assessor',
key: 'assessor',
width: 100,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 100,
render: (status) => (
<Tag color={status === '已完成' ? 'green' : 'orange'}>
{status}
</Tag>
),
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
width: 150,
},
{
title: '操作',
key: 'action',
width: 200,
render: (_, record) => (
<Space size="small">
<Button
type="link"
icon={<EyeOutlined />}
onClick={() => handleView(record)}
>
查看
</Button>
<Button
type="link"
icon={<EditOutlined />}
onClick={() => handleEdit(record)}
>
编辑
</Button>
<Button
type="link"
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(record)}
>
删除
</Button>
</Space>
),
},
];
return (
<div className="evaluation-report">
<Card
title="评估报告管理"
extra={
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
新增报告
</Button>
}
>
<Table
columns={columns}
dataSource={dataSource}
loading={loading}
rowKey="id"
pagination={{
...pagination,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total, range) =>
`${range[0]}-${range[1]} 条/共 ${total}`,
onChange: (page, pageSize) => {
setPagination(prev => ({
...prev,
current: page,
pageSize: pageSize || prev.pageSize,
}));
},
}}
/>
</Card>
<Modal
title={editingRecord ? '编辑评估报告' : '新增评估报告'}
open={modalVisible}
onCancel={() => {
setModalVisible(false);
form.resetFields();
}}
onOk={() => form.submit()}
width={600}
>
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
>
<Form.Item
name="reportName"
label="报告名称"
rules={[{ required: true, message: '请输入报告名称' }]}
>
<Input placeholder="请输入报告名称" />
</Form.Item>
<Form.Item
name="reportType"
label="报告类型"
rules={[{ required: true, message: '请选择报告类型' }]}
>
<Select placeholder="请选择报告类型">
<Option value="季度评估">季度评估</Option>
<Option value="年度评估">年度评估</Option>
<Option value="专项评估">专项评估</Option>
</Select>
</Form.Item>
<Form.Item
name="assessmentPeriod"
label="评估周期"
rules={[{ required: true, message: '请输入评估周期' }]}
>
<Input placeholder="请输入评估周期" />
</Form.Item>
<Form.Item
name="assessor"
label="评估人"
rules={[{ required: true, message: '请输入评估人' }]}
>
<Input placeholder="请输入评估人" />
</Form.Item>
<Form.Item
name="description"
label="描述"
>
<Input.TextArea rows={4} placeholder="请输入描述" />
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default EvaluationReport;

@ -0,0 +1,115 @@
.evaluation-report {
padding: 20px;
.ant-card {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-radius: 6px;
.ant-card-head {
border-bottom: 1px solid #f0f0f0;
.ant-card-head-title {
font-size: 16px;
font-weight: 600;
color: #262626;
}
}
.ant-card-body {
padding: 20px;
}
}
.ant-table {
.ant-table-thead > tr > th {
background-color: #fafafa;
font-weight: 600;
color: #262626;
border-bottom: 1px solid #f0f0f0;
}
.ant-table-tbody > tr > td {
border-bottom: 1px solid #f0f0f0;
}
.ant-table-tbody > tr:hover > td {
background-color: #f5f5f5;
}
}
.ant-btn {
border-radius: 4px;
&.ant-btn-primary {
background-color: #1890ff;
border-color: #1890ff;
&:hover {
background-color: #40a9ff;
border-color: #40a9ff;
}
}
&.ant-btn-link {
padding: 4px 8px;
height: auto;
&:hover {
background-color: #f5f5f5;
}
}
}
.ant-tag {
border-radius: 4px;
font-size: 12px;
padding: 2px 8px;
}
.ant-modal {
.ant-modal-header {
border-bottom: 1px solid #f0f0f0;
.ant-modal-title {
font-size: 16px;
font-weight: 600;
color: #262626;
}
}
.ant-modal-body {
padding: 24px;
}
.ant-form-item-label > label {
font-weight: 500;
color: #262626;
}
.ant-input,
.ant-select-selector {
border-radius: 4px;
border: 1px solid #d9d9d9;
&:hover {
border-color: #40a9ff;
}
&:focus,
&.ant-select-focused .ant-select-selector {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
}
}
.ant-pagination {
margin-top: 16px;
text-align: right;
.ant-pagination-total-text {
color: #8c8c8c;
margin-right: 16px;
}
}
}

@ -0,0 +1,344 @@
import React, { useState, useEffect } from 'react';
import { Card, Table, Button, Modal, Form, Input, Select, message, Space, Tag, Badge } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined, PlayCircleOutlined, PauseCircleOutlined } from '@ant-design/icons';
import './OnlineMonitoring.less';
const { Option } = Select;
const OnlineMonitoring = () => {
const [form] = Form.useForm();
const [dataSource, setDataSource] = useState([]);
const [loading, setLoading] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const [editingRecord, setEditingRecord] = useState(null);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 0,
});
// 模拟数据
const mockData = [
{
id: 1,
deviceName: '温度传感器-001',
deviceType: '温度传感器',
location: '生产车间A区',
status: '在线',
lastUpdate: '2024-01-15 14:30:25',
value: '25.6°C',
threshold: '30°C',
description: '监测生产车间温度变化',
},
{
id: 2,
deviceName: '压力传感器-002',
deviceType: '压力传感器',
location: '储罐区B区',
status: '离线',
lastUpdate: '2024-01-15 12:15:30',
value: '--',
threshold: '2.5MPa',
description: '监测储罐压力状态',
},
{
id: 3,
deviceName: '气体检测器-003',
deviceType: '气体检测器',
location: '危险品仓库',
status: '在线',
lastUpdate: '2024-01-15 14:32:10',
value: '正常',
threshold: '50ppm',
description: '监测有害气体浓度',
},
];
useEffect(() => {
fetchData();
}, [pagination.current, pagination.pageSize]);
const fetchData = async () => {
setLoading(true);
try {
// 模拟API调用
setTimeout(() => {
setDataSource(mockData);
setPagination(prev => ({ ...prev, total: mockData.length }));
setLoading(false);
}, 500);
} catch (error) {
message.error('获取数据失败');
setLoading(false);
}
};
const handleAdd = () => {
setEditingRecord(null);
form.resetFields();
setModalVisible(true);
};
const handleEdit = (record) => {
setEditingRecord(record);
form.setFieldsValue(record);
setModalVisible(true);
};
const handleDelete = (record) => {
Modal.confirm({
title: '确认删除',
content: `确定要删除监控设备"${record.deviceName}"吗?`,
onOk: () => {
setDataSource(dataSource.filter(item => item.id !== record.id));
message.success('删除成功');
},
});
};
const handleView = (record) => {
Modal.info({
title: '查看监控设备详情',
content: (
<div>
<p><strong>设备名称</strong>{record.deviceName}</p>
<p><strong>设备类型</strong>{record.deviceType}</p>
<p><strong>安装位置</strong>{record.location}</p>
<p><strong>运行状态</strong>
<Badge
status={record.status === '在线' ? 'success' : 'error'}
text={record.status}
/>
</p>
<p><strong>当前数值</strong>{record.value}</p>
<p><strong>阈值设置</strong>{record.threshold}</p>
<p><strong>最后更新</strong>{record.lastUpdate}</p>
<p><strong>设备描述</strong>{record.description}</p>
</div>
),
width: 600,
});
};
const handleStartStop = (record) => {
const newStatus = record.status === '在线' ? '离线' : '在线';
setDataSource(dataSource.map(item =>
item.id === record.id ? { ...item, status: newStatus } : item
));
message.success(`设备已${newStatus === '在线' ? '启动' : '停止'}`);
};
const handleSubmit = async (values) => {
try {
if (editingRecord) {
// 编辑
setDataSource(dataSource.map(item =>
item.id === editingRecord.id ? { ...item, ...values } : item
));
message.success('编辑成功');
} else {
// 新增
const newRecord = {
id: Date.now(),
...values,
status: '离线',
lastUpdate: new Date().toLocaleString(),
value: '--',
};
setDataSource([...dataSource, newRecord]);
message.success('添加成功');
}
setModalVisible(false);
form.resetFields();
} catch (error) {
message.error('操作失败');
}
};
const columns = [
{
title: '设备名称',
dataIndex: 'deviceName',
key: 'deviceName',
width: 150,
},
{
title: '设备类型',
dataIndex: 'deviceType',
key: 'deviceType',
width: 120,
},
{
title: '安装位置',
dataIndex: 'location',
key: 'location',
width: 150,
},
{
title: '运行状态',
dataIndex: 'status',
key: 'status',
width: 100,
render: (status) => (
<Badge
status={status === '在线' ? 'success' : 'error'}
text={status}
/>
),
},
{
title: '当前数值',
dataIndex: 'value',
key: 'value',
width: 100,
},
{
title: '阈值设置',
dataIndex: 'threshold',
key: 'threshold',
width: 100,
},
{
title: '最后更新',
dataIndex: 'lastUpdate',
key: 'lastUpdate',
width: 150,
},
{
title: '操作',
key: 'action',
width: 250,
render: (_, record) => (
<Space size="small">
<Button
type="link"
icon={<EyeOutlined />}
onClick={() => handleView(record)}
>
查看
</Button>
<Button
type="link"
icon={<EditOutlined />}
onClick={() => handleEdit(record)}
>
编辑
</Button>
<Button
type="link"
icon={record.status === '在线' ? <PauseCircleOutlined /> : <PlayCircleOutlined />}
onClick={() => handleStartStop(record)}
>
{record.status === '在线' ? '停止' : '启动'}
</Button>
<Button
type="link"
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(record)}
>
删除
</Button>
</Space>
),
},
];
return (
<div className="online-monitoring">
<Card
title="在线监控管理"
extra={
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
新增设备
</Button>
}
>
<Table
columns={columns}
dataSource={dataSource}
loading={loading}
rowKey="id"
pagination={{
...pagination,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total, range) =>
`${range[0]}-${range[1]} 条/共 ${total}`,
onChange: (page, pageSize) => {
setPagination(prev => ({
...prev,
current: page,
pageSize: pageSize || prev.pageSize,
}));
},
}}
/>
</Card>
<Modal
title={editingRecord ? '编辑监控设备' : '新增监控设备'}
open={modalVisible}
onCancel={() => {
setModalVisible(false);
form.resetFields();
}}
onOk={() => form.submit()}
width={600}
>
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
>
<Form.Item
name="deviceName"
label="设备名称"
rules={[{ required: true, message: '请输入设备名称' }]}
>
<Input placeholder="请输入设备名称" />
</Form.Item>
<Form.Item
name="deviceType"
label="设备类型"
rules={[{ required: true, message: '请选择设备类型' }]}
>
<Select placeholder="请选择设备类型">
<Option value="温度传感器">温度传感器</Option>
<Option value="压力传感器">压力传感器</Option>
<Option value="气体检测器">气体检测器</Option>
<Option value="液位传感器">液位传感器</Option>
<Option value="振动传感器">振动传感器</Option>
</Select>
</Form.Item>
<Form.Item
name="location"
label="安装位置"
rules={[{ required: true, message: '请输入安装位置' }]}
>
<Input placeholder="请输入安装位置" />
</Form.Item>
<Form.Item
name="threshold"
label="阈值设置"
rules={[{ required: true, message: '请输入阈值设置' }]}
>
<Input placeholder="请输入阈值设置" />
</Form.Item>
<Form.Item
name="description"
label="设备描述"
>
<Input.TextArea rows={4} placeholder="请输入设备描述" />
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default OnlineMonitoring;

@ -0,0 +1,157 @@
.online-monitoring {
padding: 20px;
.ant-card {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-radius: 6px;
.ant-card-head {
border-bottom: 1px solid #f0f0f0;
.ant-card-head-title {
font-size: 16px;
font-weight: 600;
color: #262626;
}
}
.ant-card-body {
padding: 20px;
}
}
.ant-table {
.ant-table-thead > tr > th {
background-color: #fafafa;
font-weight: 600;
color: #262626;
border-bottom: 1px solid #f0f0f0;
}
.ant-table-tbody > tr > td {
border-bottom: 1px solid #f0f0f0;
}
.ant-table-tbody > tr:hover > td {
background-color: #f5f5f5;
}
}
.ant-btn {
border-radius: 4px;
&.ant-btn-primary {
background-color: #1890ff;
border-color: #1890ff;
&:hover {
background-color: #40a9ff;
border-color: #40a9ff;
}
}
&.ant-btn-link {
padding: 4px 8px;
height: auto;
&:hover {
background-color: #f5f5f5;
}
}
}
.ant-badge {
.ant-badge-status-dot {
width: 8px;
height: 8px;
}
.ant-badge-status-text {
margin-left: 8px;
font-size: 12px;
}
}
.ant-modal {
.ant-modal-header {
border-bottom: 1px solid #f0f0f0;
.ant-modal-title {
font-size: 16px;
font-weight: 600;
color: #262626;
}
}
.ant-modal-body {
padding: 24px;
}
.ant-form-item-label > label {
font-weight: 500;
color: #262626;
}
.ant-input,
.ant-select-selector {
border-radius: 4px;
border: 1px solid #d9d9d9;
&:hover {
border-color: #40a9ff;
}
&:focus,
&.ant-select-focused .ant-select-selector {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
}
}
.ant-pagination {
margin-top: 16px;
text-align: right;
.ant-pagination-total-text {
color: #8c8c8c;
margin-right: 16px;
}
}
// 状态指示器样式
.status-indicator {
display: inline-flex;
align-items: center;
gap: 4px;
&.online {
color: #52c41a;
}
&.offline {
color: #ff4d4f;
}
}
// 数值显示样式
.value-display {
font-weight: 500;
&.normal {
color: #52c41a;
}
&.warning {
color: #faad14;
}
&.danger {
color: #ff4d4f;
}
&.unknown {
color: #8c8c8c;
}
}
}

@ -0,0 +1,233 @@
import React from 'react';
import { Card, Result, Timeline,Statistic, Table,Row, Input,Button,Col, Select} from 'antd';
import StandardTable from '@/components/StandardTable';
import styles from './ResponsibilityImplementation.less';
const ResponsibilityImplementation = () => {
const columns = [
{
title:"编号",
dataIndex:"id",
key:"id",
width:60,
},
{
title:"责任区域",
dataIndex:"zrqy",
key:"zrqy",
width:120,
},
{
title:"设备型号",
dataIndex:"sbxh",
key:"sbxh",
width:120,
},
{
title:"危险源类型",
dataIndex:"wxlylx",
key:"wxlylx",
width:120,
},
{
title:"责任人",
dataIndex:"zrr",
key:"zrr",
width:120,
},
{
title:"联系方式",
dataIndex:"lxfs",
key:"lxfs",
width:120,
},
{
title:"巡检频率",
dataIndex:"xjpl",
key:"xjpl",
width:120,
},
{
title:"最近巡检",
dataIndex:"zjxj",
key:"zjxj",
width:120,
},
{
title:"状态",
dataIndex:"zt",
key:"zt",
width:120,
},
{
title:"操作",
dataIndex:"cz",
key:"cz",
width:120,
render: (text, record) => { // ======== 渲染操作列 ========
const handleView = (record) => { // ======== 定义查看函数 ========
console.log('查看记录:', record); // ======== 打印记录信息到控制台 ========
// 这里可以添加查看详情的逻辑 // ======== 注释:可以添加查看详情的逻辑 ========
}; // ======== 函数结束 ========
return ( // ======== 返回按钮组件 ========
<Button type="link" onClick={() => handleView(record)}>查看</Button> // ======== 查看按钮点击时调用handleView函数 ========
); // ======== return语句结束 ========
} // ======== render函数结束 ========
}
];
// 生成20条假数据
const generateMockData = () => { // ======== 定义生成假数据的函数 ========
const data = []; // ======== 创建空数组存储数据 ========
const areas = ['生产车间A区', '生产车间B区', '储罐区', '配电室', '锅炉房', '化学品仓库', '实验室', '办公区']; // ======== 责任区域选项 ========
const devices = ['压力容器', '储罐', '反应釜', '压缩机', '泵类设备', '电气设备', '管道系统', '安全阀']; // ======== 设备型号选项 ========
const statuses = ['正常', '维护中', '故障', '停用', '检修']; // ======== 状态选项 ========
const names = ['张三', '李四', '王五', '赵六', '孙七', '周八', '吴九', '郑十', '钱十一', '陈十二']; // ======== 责任人姓名选项 ========
for (let i = 1; i <= 20; i++) { // ======== 循环生成20条数据 ========
data.push({ // ======== 添加一条数据到数组 ========
key: i.toString(), // ======== 唯一标识 ========
id: `WX${String(i).padStart(3, '0')}`, // ======== 编号WX001, WX002... ========
zrqy: areas[Math.floor(Math.random() * areas.length)], // ======== 随机选择责任区域 ========
sbxh: devices[Math.floor(Math.random() * devices.length)], // ======== 随机选择设备型号 ========
zrr: names[Math.floor(Math.random() * names.length)], // ======== 随机选择责任人 ========
zt: statuses[Math.floor(Math.random() * statuses.length)], // ======== 随机选择状态 ========
}); // ======== 数据对象结束 ========
} // ======== 循环结束 ========
return data; // ======== 返回生成的数据数组 ========
}; // ======== 函数结束 ========
const tableData = generateMockData(); // ======== 调用函数生成假数据 ========
return (
<div className={styles.containerR}>
{/* 警告提示框 */}
<div className={styles.warningBox}>
<img src={require('@/assets/business_basic/Frame.png')} alt="警告" className={styles.warningIcon} />
<span className={styles.warningText}>
有5个消防设备需要维护3个资质证书即将到期请及时处理
</span>
</div>
<div className={styles.containerOne}>
<div className={styles.containerOneLeft}>
{/* 第一行:标题和按钮 */}
<div className={styles.leftTopSection}>
<div className={styles.titleLeft}>
<div className={styles.titleIcon}></div>
<div>组织架构图预览</div>
</div>
<div className={styles.buttonGroup}>
<Button className={styles.actionBtn}>
<span className={styles.btnIcon}>📤</span>
上传
</Button>
<Button className={styles.actionBtn}>
<span className={styles.btnIcon}>📥</span>
下载
</Button>
</div>
</div>
{/* 第二行:图片占位 */}
<div className={styles.leftBottomSection}>
<div className={styles.imagePlaceholder}>
<div className={styles.imageIcon}>🏢</div>
<div className={styles.imageText}>组织架构图</div>
</div>
</div>
</div>
<div className={styles.containerOneRight}>
{/* 第一块:标题 + 下拉选择框 + 导出按钮 */}
<div className={styles.rightTopSection}>
<div className={styles.rightTopLeft}>
<div className={styles.titleLeft}>
<div className={styles.titleIcon}></div>
<div>履职时间轴</div>
</div>
<Select
style={{ width: 100, marginLeft: 15 }}
defaultValue="每月"
className={styles.rightTopSelect}
options={[
{ value: '每月', label: '每月' },
{ value: '每年', label: '每年' },
]}
optionRender={(option) => (
<div className={styles.customOption}>
<span className={styles.optionIcon}>📅</span>
<span className={styles.optionText}>{option.label}</span>
</div>
)}
/>
</div>
<div className={styles.rightTopRight}>
<Button
type="primary"
className={styles.exportBtn}
icon="📄"
>
导出PDF考核报告
</Button>
</div>
</div>
{/* 第二块:图片内容 */}
<div className={styles.rightBottomSection}>
<div className={styles.imagePlaceholder}>
<div className={styles.imageIcon}>📊</div>
<div className={styles.imageText}>数据图表</div>
</div>
</div>
</div>
</div>
<div className={styles.containerTwo}>
{/* 第一个大块:标题 */}
<div className={styles.containerTwoTitle}>
<div className={styles.titleLeft}>
<div className={styles.titleIcon}></div>
<div>重大危险源安全包信息库</div>
</div>
</div>
{/* 第二个大块:搜索和按钮 */}
<div className={styles.containerTwoActions}>
<div className={styles.searchSection}>
<Input placeholder="搜索危险源编号" />
</div>
<div className={styles.buttonSection}>
<Button>新增责任人</Button>
<Button>导出清单</Button>
</div>
</div>
{/* 第三个大块:表格 */}
<div className={styles.containerTwoTable}>
<StandardTable
columns={columns} // ======== 表格列配置 ========
data={{ // ======== 数据对象包含list和pagination ========
list: tableData, // ======== 表格数据列表 ========
pagination: { // ======== 分页配置 ========
currentPage: 1, // ======== 当前页码 ========
pageSize: 5, // ======== 每页显示10条数据 ========
total: tableData.length, // ======== 总数据条数 ========
} // ======== 分页配置结束 ========
}} // ======== 数据对象结束 ========
selectedRows={[]} // ======== 选中的行数据,初始为空数组 ========
onSelectRow={() => {}} // ======== 行选择事件处理函数 ========
onChange={() => {}} // ======== 表格变化事件处理函数 ========
/>
</div>
</div>
</div>
);
};
export default ResponsibilityImplementation;

@ -0,0 +1,413 @@
.containerR {
padding: 8px 6px;
height: 100%;
display: flex;
flex-direction: column;
.warningBox {
width: 100%;
background-color: #FFF3CD;
border: 1px solid #F4E3AE;
border-radius: 4px;
padding: 8px 20px;
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
.warningIcon {
width: 18px;
height: 18px;
}
.warningText {
color: #8C6C0B;
font-size: 12px;
line-height: 1.4;
}
}
.containerOne {
height: 40%;
flex-shrink: 0;
display: flex;
margin-bottom: 10px;
gap: 10px;
.containerOneLeft{
background-color: white;
width: calc(50% - 5px);
display: flex;
flex-direction: column;
padding: 15px;
border: 1px solid #f0f0f0;
border-radius: 4px;
.leftTopSection {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
.titleLeft {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.buttonGroup {
display: flex;
gap: 8px;
.actionBtn {
display: flex;
align-items: center;
gap: 4px;
border: 1px solid #DFE4F6;
border-radius: 4px;
color: #2E4CD4;
font-weight: 500;
font-size: 12px;
padding: 4px 8px;
background: transparent;
cursor: pointer;
transition: all 0.2s;
&:hover {
background-color: #F0F2FF;
border-color: #2E4CD4;
}
.btnIcon {
font-size: 12px;
}
}
}
}
.leftBottomSection {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 4px;
.imagePlaceholder {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
color: #999999;
.imageIcon {
font-size: 32px;
opacity: 0.6;
}
.imageText {
font-size: 14px;
font-weight: 400;
}
}
}
}
.containerOneRight{
background-color: white;
width: calc(50% - 5px);
display: flex;
flex-direction: column;
padding: 15px;
border: 1px solid #f0f0f0;
border-radius: 4px;
.rightTopSection {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
.rightTopLeft {
display: flex;
align-items: center;
.titleLeft {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
}
.rightTopRight {
.exportBtn {
background-color: #2E4CD4 !important;
border-color: #2E4CD4 !important;
color: #fff !important;
font-size: 14px !important;
font-weight: 500 !important;
height: 32px;
padding: 0 16px;
&:hover {
background-color: #1e3bb8 !important;
border-color: #1e3bb8 !important;
}
}
}
}
.rightBottomSection {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
.imagePlaceholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background-color: #f5f5f5;
border: 2px dashed #d9d9d9;
border-radius: 4px;
.imageIcon {
font-size: 48px;
margin-bottom: 10px;
}
.imageText {
font-size: 14px;
color: #999999;
}
}
}
}
}
.containerTwo{
flex: 1;
background-color: white;
display: flex;
flex-direction: column;
padding: 15px;
border: 1px solid #f0f0f0;
border-radius: 4px;
.containerTwoTitle {
margin-bottom: 15px;
.titleLeft {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
}
.containerTwoActions {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
.searchSection {
flex: 1;
max-width: 300px;
:global(.ant-input) {
height: 32px;
border-radius: 4px;
border: 1px solid #d9d9d9;
&:focus {
border-color: #2E4CD4;
box-shadow: 0 0 0 2px rgba(46, 76, 212, 0.2);
}
}
}
.buttonSection {
display: flex;
gap: 8px;
:global(.ant-btn) {
height: 32px;
padding: 0 16px;
border-radius: 4px;
font-size: 14px;
border: 1px solid #d9d9d9;
background-color: #fff;
color: #333;
&:hover {
border-color: #2E4CD4;
color: #2E4CD4;
}
&:focus {
border-color: #2E4CD4;
color: #2E4CD4;
}
}
}
}
.containerTwoTable {
flex: 1;
overflow: hidden;
:global(.ant-table) {
font-size: 12px;
}
:global(.ant-table-thead > tr > th) {
background-color: #f5f5fa;
font-weight: 500;
font-size: 14px;
color: #333333;
border-bottom: 1px solid #f0f0f0;
padding: 8px 12px;
text-align: center;
}
:global(.ant-table-tbody > tr > td) {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
text-align: center;
}
:global(.ant-table-tbody > tr:hover > td) {
background-color: #f5f5f5;
}
:global(.ant-pagination) {
margin-top: 16px;
text-align: right;
}
}
}
}
.rightTopSelect{
// 下拉框本身的样式
:global(.ant-select-selector) {
background-color: #f8f9fa !important;
border: 1px solid #d9d9d9 !important;
border-radius: 6px !important;
height: 32px !important;
min-height: 32px !important;
&:hover {
border-color: #2E4CD4 !important;
}
&:focus {
border-color: #2E4CD4 !important;
box-shadow: 0 0 0 2px rgba(46, 76, 212, 0.2) !important;
}
}
// 下拉框内的文字样式
:global(.ant-select-selection-item) {
color: #333333 !important;
font-size: 14px !important;
font-weight: 500 !important;
line-height: 30px !important;
}
// 下拉箭头样式
:global(.ant-select-arrow) {
color: #666666 !important;
font-size: 12px !important;
}
// 下拉菜单容器样式
:global(.ant-select-dropdown) {
background-color: #ffffff !important;
border: 1px solid #e8e8e8 !important;
border-radius: 8px !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
padding: 4px 0 !important;
}
// 下拉选项样式
:global(.ant-select-item) {
color: #333333 !important;
font-size: 14px !important;
padding: 8px 12px !important;
border-radius: 4px !important;
margin: 2px 8px !important;
&:hover {
background-color: #f0f2ff !important;
color: #2E4CD4 !important;
}
&.ant-select-item-option-selected {
background-color: #e6f7ff !important;
color: #2E4CD4 !important;
font-weight: 600 !important;
}
}
// 选中状态的样式
:global(.ant-select-focused .ant-select-selector) {
border-color: #2E4CD4 !important;
box-shadow: 0 0 0 2px rgba(46, 76, 212, 0.2) !important;
}
}
// 自定义选项样式
.customOption {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 0;
.optionIcon {
font-size: 16px;
color: #2E4CD4;
}
.optionText {
font-size: 14px;
color: #333333;
font-weight: 500;
}
}

@ -0,0 +1,425 @@
import React, { useState, useEffect } from 'react';
import { Card, Table, Button, Modal, Form, Input, Select, message, Space, Tag, Rate, Progress } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import './RiskAssessment.less';
const { Option } = Select;
const RiskAssessment = () => {
const [form] = Form.useForm();
const [dataSource, setDataSource] = useState([]);
const [loading, setLoading] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const [editingRecord, setEditingRecord] = useState(null);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 0,
});
// 模拟数据
const mockData = [
{
id: 1,
riskName: '化学品泄漏风险',
riskType: '环境风险',
riskLevel: '高',
probability: 4,
impact: 5,
riskScore: 20,
assessor: '张三',
assessDate: '2024-01-15',
status: '待处理',
description: '生产过程中化学品可能发生泄漏,造成环境污染',
controlMeasures: '加强设备维护,安装泄漏检测装置',
},
{
id: 2,
riskName: '设备故障风险',
riskType: '设备风险',
riskLevel: '中',
probability: 3,
impact: 3,
riskScore: 9,
assessor: '李四',
assessDate: '2024-01-10',
status: '已处理',
description: '关键设备可能发生故障,影响生产安全',
controlMeasures: '定期维护保养,建立备件库存',
},
{
id: 3,
riskName: '人员操作风险',
riskType: '人员风险',
riskLevel: '低',
probability: 2,
impact: 2,
riskScore: 4,
assessor: '王五',
assessDate: '2024-01-08',
status: '监控中',
description: '操作人员误操作可能导致安全事故',
controlMeasures: '加强培训,完善操作规程',
},
];
useEffect(() => {
fetchData();
}, [pagination.current, pagination.pageSize]);
const fetchData = async () => {
setLoading(true);
try {
// 模拟API调用
setTimeout(() => {
setDataSource(mockData);
setPagination(prev => ({ ...prev, total: mockData.length }));
setLoading(false);
}, 500);
} catch (error) {
message.error('获取数据失败');
setLoading(false);
}
};
const handleAdd = () => {
setEditingRecord(null);
form.resetFields();
setModalVisible(true);
};
const handleEdit = (record) => {
setEditingRecord(record);
form.setFieldsValue(record);
setModalVisible(true);
};
const handleDelete = (record) => {
Modal.confirm({
title: '确认删除',
content: `确定要删除风险评估"${record.riskName}"吗?`,
onOk: () => {
setDataSource(dataSource.filter(item => item.id !== record.id));
message.success('删除成功');
},
});
};
const handleView = (record) => {
Modal.info({
title: '查看风险评估详情',
content: (
<div>
<p><strong>风险名称</strong>{record.riskName}</p>
<p><strong>风险类型</strong>{record.riskType}</p>
<p><strong>风险等级</strong>
<Tag color={record.riskLevel === '高' ? 'red' : record.riskLevel === '中' ? 'orange' : 'green'}>
{record.riskLevel}
</Tag>
</p>
<p><strong>发生概率</strong>
<Rate disabled value={record.probability} />
<span style={{ marginLeft: 8 }}>({record.probability}/5)</span>
</p>
<p><strong>影响程度</strong>
<Rate disabled value={record.impact} />
<span style={{ marginLeft: 8 }}>({record.impact}/5)</span>
</p>
<p><strong>风险评分</strong>{record.riskScore}</p>
<p><strong>评估人</strong>{record.assessor}</p>
<p><strong>评估日期</strong>{record.assessDate}</p>
<p><strong>处理状态</strong>
<Tag color={record.status === '已处理' ? 'green' : record.status === '待处理' ? 'red' : 'blue'}>
{record.status}
</Tag>
</p>
<p><strong>风险描述</strong>{record.description}</p>
<p><strong>控制措施</strong>{record.controlMeasures}</p>
</div>
),
width: 700,
});
};
const handleSubmit = async (values) => {
try {
const riskScore = values.probability * values.impact;
const riskLevel = riskScore >= 16 ? '高' : riskScore >= 9 ? '中' : '低';
const formData = {
...values,
riskScore,
riskLevel,
};
if (editingRecord) {
// 编辑
setDataSource(dataSource.map(item =>
item.id === editingRecord.id ? { ...item, ...formData } : item
));
message.success('编辑成功');
} else {
// 新增
const newRecord = {
id: Date.now(),
...formData,
assessor: '当前用户',
assessDate: new Date().toISOString().split('T')[0],
status: '待处理',
};
setDataSource([...dataSource, newRecord]);
message.success('添加成功');
}
setModalVisible(false);
form.resetFields();
} catch (error) {
message.error('操作失败');
}
};
const getRiskLevelColor = (level) => {
switch (level) {
case '高': return 'red';
case '中': return 'orange';
case '低': return 'green';
default: return 'default';
}
};
const getStatusColor = (status) => {
switch (status) {
case '已处理': return 'green';
case '待处理': return 'red';
case '监控中': return 'blue';
default: return 'default';
}
};
const columns = [
{
title: '风险名称',
dataIndex: 'riskName',
key: 'riskName',
width: 150,
},
{
title: '风险类型',
dataIndex: 'riskType',
key: 'riskType',
width: 120,
},
{
title: '风险等级',
dataIndex: 'riskLevel',
key: 'riskLevel',
width: 100,
render: (level) => (
<Tag color={getRiskLevelColor(level)}>
{level}
</Tag>
),
},
{
title: '风险评分',
dataIndex: 'riskScore',
key: 'riskScore',
width: 100,
render: (score) => (
<div>
<Progress
percent={(score / 25) * 100}
size="small"
status={score >= 16 ? 'exception' : score >= 9 ? 'active' : 'success'}
showInfo={false}
/>
<span style={{ fontSize: '12px' }}>{score}</span>
</div>
),
},
{
title: '发生概率',
dataIndex: 'probability',
key: 'probability',
width: 120,
render: (probability) => (
<div>
<Rate disabled value={probability} style={{ fontSize: '12px' }} />
<span style={{ marginLeft: 4, fontSize: '12px' }}>({probability}/5)</span>
</div>
),
},
{
title: '影响程度',
dataIndex: 'impact',
key: 'impact',
width: 120,
render: (impact) => (
<div>
<Rate disabled value={impact} style={{ fontSize: '12px' }} />
<span style={{ marginLeft: 4, fontSize: '12px' }}>({impact}/5)</span>
</div>
),
},
{
title: '处理状态',
dataIndex: 'status',
key: 'status',
width: 100,
render: (status) => (
<Tag color={getStatusColor(status)}>
{status}
</Tag>
),
},
{
title: '评估人',
dataIndex: 'assessor',
key: 'assessor',
width: 100,
},
{
title: '评估日期',
dataIndex: 'assessDate',
key: 'assessDate',
width: 120,
},
{
title: '操作',
key: 'action',
width: 200,
render: (_, record) => (
<Space size="small">
<Button
type="link"
icon={<EyeOutlined />}
onClick={() => handleView(record)}
>
查看
</Button>
<Button
type="link"
icon={<EditOutlined />}
onClick={() => handleEdit(record)}
>
编辑
</Button>
<Button
type="link"
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(record)}
>
删除
</Button>
</Space>
),
},
];
return (
<div className="risk-assessment">
<Card
title="风险评估管理"
extra={
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
新增评估
</Button>
}
>
<Table
columns={columns}
dataSource={dataSource}
loading={loading}
rowKey="id"
pagination={{
...pagination,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total, range) =>
`${range[0]}-${range[1]} 条/共 ${total}`,
onChange: (page, pageSize) => {
setPagination(prev => ({
...prev,
current: page,
pageSize: pageSize || prev.pageSize,
}));
},
}}
/>
</Card>
<Modal
title={editingRecord ? '编辑风险评估' : '新增风险评估'}
open={modalVisible}
onCancel={() => {
setModalVisible(false);
form.resetFields();
}}
onOk={() => form.submit()}
width={700}
>
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
>
<Form.Item
name="riskName"
label="风险名称"
rules={[{ required: true, message: '请输入风险名称' }]}
>
<Input placeholder="请输入风险名称" />
</Form.Item>
<Form.Item
name="riskType"
label="风险类型"
rules={[{ required: true, message: '请选择风险类型' }]}
>
<Select placeholder="请选择风险类型">
<Option value="环境风险">环境风险</Option>
<Option value="设备风险">设备风险</Option>
<Option value="人员风险">人员风险</Option>
<Option value="管理风险">管理风险</Option>
<Option value="技术风险">技术风险</Option>
</Select>
</Form.Item>
<Form.Item
name="probability"
label="发生概率 (1-5分)"
rules={[{ required: true, message: '请选择发生概率' }]}
>
<Rate />
</Form.Item>
<Form.Item
name="impact"
label="影响程度 (1-5分)"
rules={[{ required: true, message: '请选择影响程度' }]}
>
<Rate />
</Form.Item>
<Form.Item
name="description"
label="风险描述"
rules={[{ required: true, message: '请输入风险描述' }]}
>
<Input.TextArea rows={3} placeholder="请输入风险描述" />
</Form.Item>
<Form.Item
name="controlMeasures"
label="控制措施"
rules={[{ required: true, message: '请输入控制措施' }]}
>
<Input.TextArea rows={3} placeholder="请输入控制措施" />
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default RiskAssessment;

@ -0,0 +1,241 @@
.risk-assessment {
padding: 20px;
.ant-card {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-radius: 6px;
.ant-card-head {
border-bottom: 1px solid #f0f0f0;
.ant-card-head-title {
font-size: 16px;
font-weight: 600;
color: #262626;
}
}
.ant-card-body {
padding: 20px;
}
}
.ant-table {
.ant-table-thead > tr > th {
background-color: #fafafa;
font-weight: 600;
color: #262626;
border-bottom: 1px solid #f0f0f0;
}
.ant-table-tbody > tr > td {
border-bottom: 1px solid #f0f0f0;
}
.ant-table-tbody > tr:hover > td {
background-color: #f5f5f5;
}
}
.ant-btn {
border-radius: 4px;
&.ant-btn-primary {
background-color: #1890ff;
border-color: #1890ff;
&:hover {
background-color: #40a9ff;
border-color: #40a9ff;
}
}
&.ant-btn-link {
padding: 4px 8px;
height: auto;
&:hover {
background-color: #f5f5f5;
}
}
}
.ant-tag {
border-radius: 4px;
font-size: 12px;
padding: 2px 8px;
}
.ant-rate {
font-size: 14px;
.ant-rate-star {
margin-right: 2px;
}
}
.ant-progress {
.ant-progress-bg {
border-radius: 2px;
}
&.ant-progress-small {
.ant-progress-text {
font-size: 10px;
}
}
}
.ant-modal {
.ant-modal-header {
border-bottom: 1px solid #f0f0f0;
.ant-modal-title {
font-size: 16px;
font-weight: 600;
color: #262626;
}
}
.ant-modal-body {
padding: 24px;
}
.ant-form-item-label > label {
font-weight: 500;
color: #262626;
}
.ant-input,
.ant-select-selector {
border-radius: 4px;
border: 1px solid #d9d9d9;
&:hover {
border-color: #40a9ff;
}
&:focus,
&.ant-select-focused .ant-select-selector {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
}
}
.ant-pagination {
margin-top: 16px;
text-align: right;
.ant-pagination-total-text {
color: #8c8c8c;
margin-right: 16px;
}
}
// 风险等级颜色
.risk-level-high {
color: #ff4d4f;
font-weight: 600;
}
.risk-level-medium {
color: #faad14;
font-weight: 600;
}
.risk-level-low {
color: #52c41a;
font-weight: 600;
}
// 状态指示器
.status-indicator {
display: inline-flex;
align-items: center;
gap: 4px;
&.processed {
color: #52c41a;
}
&.pending {
color: #ff4d4f;
}
&.monitoring {
color: #1890ff;
}
}
// 评分显示
.score-display {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
.score-value {
font-weight: 600;
font-size: 14px;
}
.score-label {
font-size: 12px;
color: #8c8c8c;
}
}
// 概率和影响程度显示
.rate-display {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
.rate-stars {
display: flex;
align-items: center;
gap: 2px;
}
.rate-text {
font-size: 12px;
color: #8c8c8c;
}
}
// 风险矩阵样式
.risk-matrix {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 8px;
margin: 16px 0;
.matrix-cell {
padding: 8px;
text-align: center;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 12px;
&.high-risk {
background-color: #fff2f0;
border-color: #ff4d4f;
color: #ff4d4f;
}
&.medium-risk {
background-color: #fffbe6;
border-color: #faad14;
color: #faad14;
}
&.low-risk {
background-color: #f6ffed;
border-color: #52c41a;
color: #52c41a;
}
}
}
}

@ -87,6 +87,12 @@ const SystemContentList = (props) => {
setMenuItems(newList) setMenuItems(newList)
const fixedMenuItems = [ const fixedMenuItems = [
{
path: '/topnavbar00/business/basic',
icon: <img src={fireHydrant1} alt="基础信息管理" style={{ width: '16px', height: '16px' }} />,
key: "/topnavbar00/business/basic",
"label": "基础信息管理"
},
{ {
"path": "/topnavbar00/business/basicinformation", "path": "/topnavbar00/business/basicinformation",
icon: <img src={fireHydrant1} alt="安全管理基础信息" style={{ width: '16px', height: '16px' }} />, icon: <img src={fireHydrant1} alt="安全管理基础信息" style={{ width: '16px', height: '16px' }} />,

@ -8,6 +8,10 @@ import Logo from '@/assets/logo.png'
import { userInfo } from '@/utils/globalCommon' import { userInfo } from '@/utils/globalCommon'
const menuItem = [ const menuItem = [
{
label: '基础信息管理',
key: '/topnavbar00/business/basic',
},
{ {
label: '安全管理基础信息', label: '安全管理基础信息',
key: '/topnavbar00/business/basicinformation', key: '/topnavbar00/business/basicinformation',

Loading…
Cancel
Save