初始化

This commit is contained in:
Vectorune
2025-09-13 16:18:30 +08:00
commit 754f4d97b3
91 changed files with 29581 additions and 0 deletions

View File

@ -0,0 +1,146 @@
import React, {useEffect} from 'react';
import {Button, Flex, Form, Input, message, Spin, Table, Typography} from "antd";
import creatMessageCommonAxios from "../../../http/CreatMessageCommonAxios";
const ChooseUser = props => {
const {allowNext, request, setRequest} = props;
const {Title, Text} = Typography;
const [messageApi, contextHolder] = message.useMessage();
const [form] = Form.useForm();
const commonAxios = creatMessageCommonAxios(messageApi);
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
hidden: true,
width: 0,
},
{
title: '工号',
dataIndex: 'staffNumber',
width: 150
},
{
title: '姓名',
dataIndex: 'name',
width: 200
},
{
title: '学院',
dataIndex: 'college',
},
{
title: '系别',
dataIndex: 'department',
width: 300
},
];
const [staffInfo, setStaffInfo] = React.useState([]);
const [staffNumberPrefix, setStaffNumberPrefix] = React.useState("");
const [spinning, setSpinning] = React.useState(true);
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
allowNext(selectedRows.length > 0);
setRequest({...request, stuffNumber: selectedRows[0].staffNumber});
sessionStorage.setItem('chooseUser', selectedRowKeys)
sessionStorage.removeItem('certificateParam');
sessionStorage.removeItem('targetKeys');
sessionStorage.removeItem('generate-request');
},
defaultSelectedRowKeys: () => {
const selectedRowKeys = [];
let selected = sessionStorage.getItem('chooseUser');
if (selected) {
// 转换为number
selectedRowKeys.push(selected);
allowNext(selectedRowKeys.length > 0);
}
return selectedRowKeys;
}
};
useEffect(() => {
commonAxios.get(`/api/auth/query/registered?page=1&size=10`).then((response) => {
if (response.data.data != null) {
let list = response.data.data.list;
// 为list设置key=id
list.forEach((item) => {
item.key = item.id;
});
setStaffInfo(list);
} else {
setStaffInfo([]);
}
});
setSpinning(false);
}, []);
const onSearch = () => {
setSpinning(true);
commonAxios.get(`/api/auth/query/registered?staffNumber=${staffNumberPrefix}&page=1&size=10`).then((response) => {
if (response.data.data != null) {
let list = response.data.data.list;
// 为list设置key=id
list.forEach((item) => {
item.key = item.id;
});
setStaffInfo(list);
} else {
setStaffInfo([]);
}
setSpinning(false);
});
}
return (
<div>
{contextHolder}
<Flex vertical justiffy={"start"} align={"start"} gap={"large"}>
<div>
<Title level={4}>选择用户</Title>
<Text level={4}
type={'secondary'}>指示EFC系统为何人生成报告选择后这份报告将会出现在您和该同事的EFC系统证明管理画面中</Text>
</div>
<Form
form={form}
layout={"inline"}
>
<Form.Item
name='staff-number'
>
<Input placeholder={"工号"} onChange={(event) => setStaffNumberPrefix(event.target.value)}/>
</Form.Item>
<Button type={"primary"} onClick={() => onSearch()}>搜索</Button>
</Form>
<div style={{width: '100%'}}>
<Spin spinning={spinning}>
<Table
columns={columns}
dataSource={staffInfo}
pagination={false}
rowSelection={{
type: 'radio',
...rowSelection,
}}
/>
</Spin>
</div>
</Flex>
</div>
)
;
}
;
ChooseUser.propTypes = {};
export default ChooseUser;

View File

@ -0,0 +1,203 @@
import React, {useEffect, useState} from 'react';
import {Flex, message, Spin, Tag, Typography} from "antd";
import TableTransfer from "../../../component/TableTransfer";
import creatMessageCommonAxios from "../../../http/CreatMessageCommonAxios";
const ContentConfig = props => {
const {allowNext, request, setRequest} = props;
const {Title, Paragraph, Text} = Typography;
const [messageApi, contentHolder] = message.useMessage();
const commonAxios = creatMessageCommonAxios(messageApi);
const generateTag = (courseNature) => {
let color = '';
let text = '';
if (courseNature === '01') {
color = 'green';
text = '公共必修';
}
if (courseNature === '02') {
color = 'blue';
text = '院选修';
}
if (courseNature === '03') {
color = 'purple';
text = '专业必修';
}
return (
<Tag color={color} key={courseNature}>
{text}
</Tag>
);
}
const tableColumns = [
{
dataIndex: 'semester',
title: '学期',
},
{
dataIndex: 'courseNature',
title: '课程性质',
render: (_, {courseNature}) => (
<>
{
generateTag(courseNature)
}
</>
),
},
{
dataIndex: 'courseName',
title: '课程名称',
width: 200
},
{
dataIndex: 'actualClassSize',
title: '学生人数',
},
{
dataIndex: 'totalClassHours',
title: '总学时数',
},
]
const mockData = [
{
key: '1',
semester: '2020-2021-1',
courseNature: '必修',
courseName: '计算机网络',
actualClassSize: '30',
totalClassHours: '48',
},
{
key: '2',
semester: '2020-2021-1',
courseNature: '必修',
courseName: '计算机网络',
actualClassSize: '30',
totalClassHours: '48',
},
{
key: '3',
semester: '2020-2021-1',
courseNature: '必修',
courseName: '计算机网络',
actualClassSize: '30',
totalClassHours: '48',
},
{
key: '4',
semester: '2020-2021-1',
courseNature: '必修',
courseName: '计算机网络',
actualClassSize: '30',
totalClassHours: '48',
},
{
key: '5',
semester: '2020-2021-1',
courseNature: '必修',
courseName: '计算机网络',
actualClassSize: '30',
totalClassHours: '48',
},
]
const [targetKeys, setTargetKeys] = useState([]);
const [disabled, setDisabled] = useState(false);
const [spinning, setSpinning] = useState(true);
const [workloadList, setWorkloadList] = useState([
{
key: '',
semester: '',
courseNature: '',
courseName: '',
actualClassSize: '',
totalClassHours: '',
}
]);
const onChange = (nextTargetKeys) => {
setTargetKeys(nextTargetKeys);
sessionStorage.setItem('targetKeys', JSON.stringify(nextTargetKeys));
setRequest({...request, ids: nextTargetKeys});
if (nextTargetKeys.length === 0) {
allowNext(false)
} else {
allowNext(true)
}
};
const toggleDisabled = (checked) => {
setDisabled(checked);
};
useEffect(() => {
commonAxios.get(`/api/v1/workload/detail/queryList/${request.stuffNumber}`)
.then((response) => {
if (response.data.data) {
console.log(response.data.data);
let dataInfo = response.data.data.map((item, index) => {
return {
key: item.id,
semester: item.semesterInfo,
courseNature: item.courseNature,
courseName: item.courseName,
actualClassSize: item.actualClassSize,
totalClassHours: item.totalClassHours,
}
}
);
setWorkloadList(dataInfo);
setSpinning(false)
}
});
let item = sessionStorage.getItem('targetKeys');
if (item !== null) {
item = JSON.parse(item);
setTargetKeys(item);
if (item.length === 0) {
allowNext(false)
} else {
allowNext(true)
}
}
}, [request]);
return (
<div>
{contentHolder}
<Flex vertical justify={"start"} align={"start"} gap={"middle"}>
<div>
<Title level={4}>证书内容配置</Title>
<Text level={4}
type={'secondary'}>选择您想体现在证明上的工作量数据无论是课时证明还是任职后工作情况证明都将会通过您的选择进而渲染合适的数据到您需要的报告中</Text>
</div>
<div style={{width: '100%'}}>
<Spin spinning={spinning}>
<TableTransfer
dataSource={workloadList}
targetKeys={targetKeys}
disabled={disabled}
// showSearch
showSelectAll={false}
onChange={onChange}
// filterOption={filterOptxion}
leftColumns={tableColumns}
rightColumns={tableColumns}
/>
</Spin>
</div>
</Flex>
</div>
);
};
ContentConfig.propTypes = {};
export default ContentConfig;

View File

@ -0,0 +1,175 @@
import React, {useEffect, useState} from 'react';
import PropTypes from 'prop-types';
import {Flex, Form, Input, Radio, Typography} from "antd";
const ParameterConfig = props => {
const {allowNext, request, setRequest} = props;
const {Title, Text} = Typography;
const [form] = Form.useForm();
const [hideWorkloadParam, setHideWorkloadParam] = React.useState(true);
const [admin, setAdmin] = React.useState(true);
const [tempParam, setTempParam] = useState({
recordType: null,
total: '',
annual: '',
})
useEffect(() => {
let item = sessionStorage.getItem("certificateParam");
// TODO 判断是否是管理员
setAdmin(true);
if (item !== null) {
item = JSON.parse(item);
setTempParam(item);
allowNext(item.recordType !== null)
if (item.recordType === '02' && admin) {
setHideWorkloadParam(false);
}
}
}, []);
return (
<div>
<Flex vertical justify={"start"} align={"start"} gap={"middle"}>
<div>
<Title level={4}>参数配置</Title>
<Text level={4}
type={'secondary'}>配置这份证明如指定这份证明的类型手动设定它的计算数据这将决定您即将看到的报告的样式和数据</Text>
</div>
<div style={{width: '100%'}}>
<Form
form={form}
labelCol={{
span: 4,
}}
style={{
maxWidth: 500,
}}
>
<Form.Item
label={"证书类型"}
name='recordType'
rules={[{required: true, message: '请选择证书类型'}]}
>
<Radio.Group
onChange={(event) => {
const recordType = event.target.value;
let item = sessionStorage.getItem("certificateParam");
if (item !== null) {
item = JSON.parse(item);
} else item = tempParam;
item.recordType = recordType;
setTempParam(item)
if (recordType === '02' && admin) {
setHideWorkloadParam(false);
setRequest({...request, recordType: recordType});
} else {
setHideWorkloadParam(true);
setRequest({
...request,
recordType: recordType,
totalTeachingWorkload: '',
annualAverageTeachingWorkload: ''
});
item.total = '';
item.annual = '';
form.setFieldsValue({
total: '',
annual: ''
});
}
sessionStorage.setItem("certificateParam", JSON.stringify(item))
allowNext(true);
}}
defaultValue={() => {
let item = sessionStorage.getItem("certificateParam");
if (item !== null) {
item = JSON.parse(item);
return item.recordType;
}
}}
options={[
{label: '本科课堂工时证明', value: '01'},
{label: '任现职后工作情况证明', value: '02'}
]}
/>
</Form.Item>
<Form.Item
label={"总工作量"}
name='total'
hidden={hideWorkloadParam}
>
<Input
type={'number'}
addonAfter={'课时'}
allowClear
onChange={(event) => {
const total = event.target.value;
let item = sessionStorage.getItem("certificateParam");
if (item !== null) {
item = JSON.parse(item);
} else item = tempParam;
item.total = total;
setTempParam(item)
sessionStorage.setItem("certificateParam", JSON.stringify(item))
setRequest({...request, totalTeachingWorkload: total});
}}
defaultValue={() => {
let item = sessionStorage.getItem("certificateParam");
if (item !== null) {
item = JSON.parse(item);
return item.total;
}
}}
/>
</Form.Item>
<Form.Item
label={"年均工作量"}
name='annual'
hidden={hideWorkloadParam}
>
<Input
addonAfter={'课时'}
allowClear
type={'number'}
onChange={(event) => {
const annualHours = event.target.value;
let item = sessionStorage.getItem("certificateParam");
if (item !== null) {
item = JSON.parse(item);
} else item = tempParam;
item.annual = annualHours;
setTempParam(item)
sessionStorage.setItem("certificateParam", JSON.stringify(item))
setRequest({
...request,
annualAverageTeachingWorkload: annualHours
});
}}
defaultValue={() => {
let item = sessionStorage.getItem("certificateParam");
if (item !== null) {
item = JSON.parse(item);
return item.annual;
}
}}
/>
</Form.Item>
</Form>
</div>
</Flex>
</div>
);
};
ParameterConfig.propTypes = {};
export default ParameterConfig;

View File

@ -0,0 +1,228 @@
import React, {useEffect, useState} from 'react';
import {Descriptions, Flex, message, Skeleton, Table, Tag, Typography} from "antd";
import creatMessageCommonAxios from "../../../http/CreatMessageCommonAxios";
const generateTag = (courseNature) => {
let color = '';
let text = '';
if (courseNature === '01') {
color = 'green';
text = '公共必修';
}
if (courseNature === '02') {
color = 'blue';
text = '院选修';
}
if (courseNature === '03') {
color = 'purple';
text = '专业必修';
}
return (
<Tag color={color} key={courseNature}>
{text}
</Tag>
);
}
const tableColumns = [
{
dataIndex: 'semester',
title: '学期',
},
{
dataIndex: 'courseNature',
title: '课程性质',
render: (_, {courseNature}) => (
<>
{
generateTag(courseNature)
}
</>
),
},
{
dataIndex: 'courseName',
title: '课程名称',
},
{
dataIndex: 'actualClassSize',
title: '学生人数',
},
{
dataIndex: 'totalClassHours',
title: '总学时数',
},
]
const mockData = [
{
key: '1',
semester: '2020-2021-1',
courseNature: '必修',
courseName: '计算机网络',
actualClassSize: '30',
totalClassHours: '48',
},
{
key: '2',
semester: '2020-2021-1',
courseNature: '必修',
courseName: '计算机网络',
actualClassSize: '30',
totalClassHours: '48',
},
{
key: '3',
semester: '2020-2021-1',
courseNature: '必修',
courseName: '计算机网络',
actualClassSize: '30',
totalClassHours: '48',
},
{
key: '4',
semester: '2020-2021-1',
courseNature: '必修',
courseName: '计算机网络',
actualClassSize: '30',
totalClassHours: '48',
},
{
key: '5',
semester: '2020-2021-1',
courseNature: '必修',
courseName: '计算机网络',
actualClassSize: '30',
totalClassHours: '48',
},
]
const ReadyToGenerate = props => {
const {allowNext, request, setRequest} = props;
const [messageApi, contentHolder] = message.useMessage();
const commonAxios = creatMessageCommonAxios(messageApi);
const {Title, Text} = Typography;
const [loading, setLoading] = useState(true);
const [user, setUser] = React.useState({
id: "",
name: "",
staffNumber: "",
gender: "",
college: "",
department: "",
researchRoom: ""
});
const [workloadList, setWorkloadList] = React.useState([{
key: '',
semester: '',
courseNature: '',
courseName: '',
actualClassSize: '',
totalClassHours: '',
}]);
useEffect(() => {
allowNext(true)
console.log(request);
commonAxios.get(`/api/auth/query/registered?staffNumber=${request.stuffNumber}`).then(response => {
if (response.data.data.list) {
console.log(response.data.data.list[0])
let userInfo = response.data.data.list[0];
setUser(userInfo);
}
});
commonAxios.post(`/api/v1/workload/detail/queryListByIds`, request.ids)
.then((response) => {
if (response.data.data) {
console.log(response.data.data);
let dataInfo = response.data.data.map((item, index) => {
return {
key: item.id,
semester: item.semesterInfo,
courseNature: item.courseNature,
courseName: item.courseName,
actualClassSize: item.actualClassSize,
totalClassHours: item.totalClassHours,
}
}
);
setWorkloadList(dataInfo);
setLoading(false)
}
});
}, [request]);
useEffect(() => {
console.log('数据写入=》', user)
}, [user]);
return (
<div>
{contentHolder}
<Flex vertical justify={"start"} align={"start"} gap={"middle"}>
<div>
<Title level={4}>数据确认</Title>
<Text level={4} type={'secondary'}>请确认您的数据点击生成按钮生成证书</Text>
</div>
<div style={{width: '100%'}}>
<Skeleton active={true} loading={loading}>
<Descriptions
bordered
items={[
{
key: 'staffNumber',
label: '工号',
children: user.staffNumber,
},
{
key: 'name',
label: '姓名',
children: user.name,
},
{
key: 'department',
label: '学院',
children: user.college,
},
{
key: 'recordType',
label: '证明类型',
children: request.recordType === '01' ? '本科教学课时证明' : '任职后工作情况证明',
},
{
key: 'totalTeachingWorkload',
label: '总学时数',
children: request.totalTeachingWorkload || '自动生成',
// span: 2,
},
{
key: 'annualAverageTeachingWorkload',
label: '年均工作量',
children: request.annualAverageTeachingWorkload || '自动生成',
},
{
key: 'workloadList',
label: '证明内容',
children: (
<>
<Table columns={tableColumns} dataSource={workloadList} pagination={false}/>
</>
),
},
]}/>
</Skeleton>
</div>
</Flex>
</div>
);
};
ReadyToGenerate.propTypes = {};
export default ReadyToGenerate;

View File

@ -0,0 +1,228 @@
import React, {useEffect} from 'react';
import {Button, Flex, message, Result, Space, Spin, Steps} from "antd";
import CardDiv from "../../../component/CardDiv/CardDiv";
import ChooseUser from "./ChooseUser";
import ParameterConfig from "./ParameterConfig";
import ContentConfig from "./ContentConfig";
import ReadyToGenerate from "./ReadyToGenerate";
import creatMessageCommonAxios from "../../../http/CreatMessageCommonAxios";
import {useNavigate} from "react-router-dom";
const GenerateCertificate = props => {
const [messageApi, contextHolder] = message.useMessage();
const commonAxios = creatMessageCommonAxios(messageApi);
const navigate = useNavigate();
const [current, setCurrent] = React.useState(() => {
let nowStep = sessionStorage.getItem('nowStep');
if (nowStep === null) {
return 0;
} else {
return parseInt(nowStep);
}
});
const [request, setRequest] = React.useState({
ids: [],
stuffNumber: "",
recordType: "",
totalTeachingWorkload: "",
annualAverageTeachingWorkload: "",
});
const [resultStatus, setResultStatus] = React.useState('error');
const [result, setResult] = React.useState({});
const [loading, setLoading] = React.useState(false);
const [pageConfig, setPageConfig] = React.useState({});
const steps = [
{
title: '选择用户',
description: '选择生成证书的用户',
},
{
title: '证书参数配置',
description: '证书类型、参数配置',
},
{
title: '证书内容配置',
description: '选择证书的工作量记录',
},
{
title: '数据确认',
description: '确认数据,准备生成',
},
{
title: '生成证明',
description: '生成进行中',
},
];
const stepMap = {
0:
<ChooseUser
allowNext={(allow) => allowNext(allow)}
request={request}
setRequest={req => setRequest(req)}
/>,
1:
<ParameterConfig
allowNext={(allow) => allowNext(allow)}
request={request}
setRequest={req => setRequest(req)}
/>,
2:
<ContentConfig
allowNext={(allow) => allowNext(allow)}
request={request}
setRequest={req => setRequest(req)}
/>,
3:
<ReadyToGenerate
allowNext={(allow) => allowNext(allow)}
request={request}
setRequest={req => setRequest(req)}
/>,
4:
<Result
status={resultStatus}
title={resultStatus === 'success' ? '成功' : '失败'}
subTitle={resultStatus === 'success' ?
`编号: ${result.id} 的证书打印请求已成功进入队列,正在生成证书,请前往证书打印页面查看。` :
'证书生成失败'}
extra={[
<Button type="primary" key="console" onClick={() => navigate('/data-print')}>
{resultStatus === 'success' ? '查看结果' : '返回'}
</Button>,
]}
/>
}
const [preButton, setPreButton] = React.useState({
type: "default",
disabled: true,
hidden: false,
text: "上一步",
});
const [nextButton, setNextButton] = React.useState({
type: "primary",
disabled: true,
hidden: false,
text: "下一步",
})
const onNext = () => {
let nextCurrent = current + 1;
setPreButton({...preButton, disabled: false,});
if (nextCurrent === 3) {
setCurrent(nextCurrent);
sessionStorage.setItem('nowStep', nextCurrent.toString());
}
if (nextCurrent >= 4) {
setLoading(true);
commonAxios.post('/api/v1/workload/certificate/generate', request).then((response) => {
console.log(response);
console.log(response.data.data !== null)
console.log(response.data.data.success === true)
if (response.data.data !== null && response.data.success === true) {
setResultStatus('success');
setResult(response.data.data);
} else {
setResultStatus('error');
setResult(response.data.errorDetails);
messageApi.error(response.data.errorDetails.message + `(${response.data.errorDetails.code})`);
}
setPreButton({...preButton, hidden: true,});
setNextButton({...nextButton, hidden: true,});
setCurrent(4);
setLoading(false);
});
sessionStorage.removeItem('nowStep');
sessionStorage.removeItem('chooseUser');
sessionStorage.removeItem('certificateParam');
sessionStorage.removeItem('targetKeys');
sessionStorage.removeItem('generate-request');
} else {
setCurrent(nextCurrent);
allowNext(false);
sessionStorage.setItem('nowStep', nextCurrent.toString());
}
}
const onPrev = () => {
let nextCurrent = current - 1;
setCurrent(nextCurrent);
let editFlag = sessionStorage.getItem('edit-flag');
if (nextCurrent <= 0) {
setCurrent(0)
setPreButton({...preButton, disabled: true,})
}
sessionStorage.setItem('nowStep', nextCurrent.toString());
}
const allowNext = (allow) => {
setNextButton({...nextButton, disabled: !allow})
}
useEffect(() => {
console.log(request);
if (request.stuffNumber !== '') {
sessionStorage.setItem('generate-request', JSON.stringify(request));
}
}, [request]);
useEffect(() => {
let request = sessionStorage.getItem('generate-request');
if (request !== null) {
request = JSON.parse(request);
setRequest(request);
}
}, []);
return (
<div>
{contextHolder}
<Space direction="vertical" size="large" style={{display: "flex"}}>
<CardDiv>
<Steps
current={current}
items={steps}
/>
</CardDiv>
<Spin spinning={loading}>
<CardDiv>
{stepMap[current]}
<Flex justify={"start"} align={"center"} gap={"middle"} style={{marginTop: 20}}
hidden={current >= 4}>
<Button
type={preButton.type}
disabled={preButton.disabled}
onClick={onPrev}
style={{display: current === 4 ? 'none' : 'inline-block'}}>
{preButton.text}
</Button>
<Button
type={nextButton.type}
disabled={nextButton.disabled}
onClick={onNext}
style={{display: current === 4 ? 'none' : 'inline-block'}}>
{current === 3 ? '开始生成' : nextButton.text}
</Button>
</Flex>
</CardDiv>
</Spin>
</Space>
</div>
);
};
GenerateCertificate.propTypes = {};
export default GenerateCertificate;