|
|
@ -1,8 +1,12 @@ |
|
|
|
import React, { useEffect, useState } from 'react'; |
|
|
|
import React, { useEffect, useState, useRef } from 'react'; |
|
|
|
import { Table, Input, Button, Select, DatePicker, Modal, Checkbox, message, Form } from 'antd'; |
|
|
|
import {Table, Input, Button, Select, DatePicker, Modal, Checkbox, message, Form, Space } from 'antd'; |
|
|
|
|
|
|
|
import { |
|
|
|
|
|
|
|
UndoOutlined |
|
|
|
|
|
|
|
} from '@ant-design/icons'; |
|
|
|
import { Link, withRouter } from 'react-router-dom'; |
|
|
|
import { Link, withRouter } from 'react-router-dom'; |
|
|
|
import moment, { Moment } from 'moment'; // 引入 moment
|
|
|
|
import moment, { Moment } from 'moment'; // 引入 moment
|
|
|
|
import { getList, doDelete, doCancel, doPublish, getIndustryList } from "api/exam-online/index"; |
|
|
|
import { saveQrCode, getList, doDelete, doCancel, doPublish, getIndustryList } from "api/exam-online/index"; |
|
|
|
|
|
|
|
import { QRCode } from 'react-qrcode-logo'; |
|
|
|
|
|
|
|
|
|
|
|
const { Option } = Select; |
|
|
|
const { Option } = Select; |
|
|
|
|
|
|
|
|
|
|
@ -17,9 +21,11 @@ type Exam = { |
|
|
|
validTo: string; |
|
|
|
validTo: string; |
|
|
|
examDuration: string; |
|
|
|
examDuration: string; |
|
|
|
publishStatus: 0 | 1 | 2; // 0: 待发布, 1: 已发布, 2: 已撤回
|
|
|
|
publishStatus: 0 | 1 | 2; // 0: 待发布, 1: 已发布, 2: 已撤回
|
|
|
|
|
|
|
|
captcha: string; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
|
|
|
|
const qrCodeRef = useRef(null); |
|
|
|
const [searchForm, setSearchForm] = useState({ |
|
|
|
const [searchForm, setSearchForm] = useState({ |
|
|
|
examName: '', |
|
|
|
examName: '', |
|
|
|
paperName: '', |
|
|
|
paperName: '', |
|
|
@ -35,6 +41,7 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
pageSize: 10, |
|
|
|
pageSize: 10, |
|
|
|
total: 0, |
|
|
|
total: 0, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
const [loading, setLoading] = useState<boolean>(false); |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
useEffect(() => { |
|
|
|
const fetchData = async () => { |
|
|
|
const fetchData = async () => { |
|
|
@ -64,6 +71,7 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
|
|
|
|
|
|
|
|
// 查询
|
|
|
|
// 查询
|
|
|
|
const handleSearch = async (page = 1) => { |
|
|
|
const handleSearch = async (page = 1) => { |
|
|
|
|
|
|
|
setLoading(true) |
|
|
|
try { |
|
|
|
try { |
|
|
|
// 构建请求参数
|
|
|
|
// 构建请求参数
|
|
|
|
const params = { |
|
|
|
const params = { |
|
|
@ -82,6 +90,7 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
const endIndex = startIndex + pagination.pageSize; |
|
|
|
const endIndex = startIndex + pagination.pageSize; |
|
|
|
setCurrentPageExamList(data.data.slice(startIndex, endIndex)); |
|
|
|
setCurrentPageExamList(data.data.slice(startIndex, endIndex)); |
|
|
|
setPagination({ ...pagination, current: page, total: data.total }); |
|
|
|
setPagination({ ...pagination, current: page, total: data.total }); |
|
|
|
|
|
|
|
setLoading(false) |
|
|
|
} catch (error) { |
|
|
|
} catch (error) { |
|
|
|
console.error('查询出错:', error); |
|
|
|
console.error('查询出错:', error); |
|
|
|
} |
|
|
|
} |
|
|
@ -190,10 +199,10 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
|
|
|
|
|
|
|
|
// 编辑
|
|
|
|
// 编辑
|
|
|
|
const handleEdit = (examId: number) => { |
|
|
|
const handleEdit = (examId: number) => { |
|
|
|
sessionStorage.setItem('examId', String(examId)); |
|
|
|
history.push({ |
|
|
|
history.push('/exam-edit'); |
|
|
|
pathname: '/exam-edit', |
|
|
|
// 实现编辑跳转逻辑
|
|
|
|
state: { examId }, |
|
|
|
console.log('跳转到编辑页面,examId:', examId); |
|
|
|
}); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleDetail = (examId: number) => { |
|
|
|
const handleDetail = (examId: number) => { |
|
|
@ -203,11 +212,22 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const [qrCodeVisible, setQrCodeVisible] = useState(false); |
|
|
|
const [qrCodeVisible, setQrCodeVisible] = useState(false); |
|
|
|
|
|
|
|
const [examId, setExamId] = useState(''); |
|
|
|
const [qrCodeData, setQrCodeData] = useState(''); |
|
|
|
const [qrCodeData, setQrCodeData] = useState(''); |
|
|
|
|
|
|
|
const [captcha, setCaptcha] = useState(''); |
|
|
|
|
|
|
|
const [examName, setExamName] = useState(''); |
|
|
|
|
|
|
|
|
|
|
|
const handleGenerateQrCode = (examId: number) => { |
|
|
|
const handleGenerateQrCode = (exam: any) => { |
|
|
|
// 实现生成考试码逻辑
|
|
|
|
// 实现生成考试码逻辑
|
|
|
|
setQrCodeData(`examId: ${examId}`); |
|
|
|
setExamId(exam.examId); |
|
|
|
|
|
|
|
setQrCodeData(exam.examId); |
|
|
|
|
|
|
|
setExamName(exam.examName); |
|
|
|
|
|
|
|
if (exam.captcha) { |
|
|
|
|
|
|
|
setCaptcha(exam.captcha); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
setCaptcha(generateCaptcha()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
setQrCodeVisible(true); |
|
|
|
setQrCodeVisible(true); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -338,12 +358,12 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
key: 'examRegion', |
|
|
|
key: 'examRegion', |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
title: '考试有效时间', |
|
|
|
title: '考试有效日期', |
|
|
|
dataIndex: 'validFrom', |
|
|
|
dataIndex: 'validFrom', |
|
|
|
key: 'validFrom', |
|
|
|
key: 'validFrom', |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
title: '考试无效时间', |
|
|
|
title: '考试无效日期', |
|
|
|
dataIndex: 'validTo', |
|
|
|
dataIndex: 'validTo', |
|
|
|
key: 'validTo', |
|
|
|
key: 'validTo', |
|
|
|
}, |
|
|
|
}, |
|
|
@ -373,23 +393,27 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
title: '操作', |
|
|
|
title: '操作', |
|
|
|
key: 'action', |
|
|
|
key: 'action', |
|
|
|
render: (cellValue: number, record: Exam) => ( |
|
|
|
render: (cellValue: number, record: Exam) => ( |
|
|
|
<div> |
|
|
|
<Space size="middle"> |
|
|
|
<a onClick={() => handleEdit(record.examId)}>编辑</a> |
|
|
|
<a onClick={() => handleEdit(record.examId)}>编辑</a> |
|
|
|
<span style={{ margin: '0 8px' }}>|</span> |
|
|
|
{record.publishStatus === 1 ? ( |
|
|
|
{record.publishStatus === 1 ? (<a |
|
|
|
<a onClick={(e) => { |
|
|
|
onClick={(e) => { |
|
|
|
|
|
|
|
if (record.publishStatus !== 1) { |
|
|
|
if (record.publishStatus !== 1) { |
|
|
|
e.preventDefault(); |
|
|
|
e.preventDefault(); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
handleGenerateQrCode(record.examId); |
|
|
|
handleGenerateQrCode(record); |
|
|
|
}} |
|
|
|
}} |
|
|
|
> |
|
|
|
> |
|
|
|
生成考试码 |
|
|
|
生成考试码 |
|
|
|
</a>) : ( |
|
|
|
</a>) : ( |
|
|
|
<span style={{ color: 'gray' }}>生成考试码</span> |
|
|
|
<span style={{ color: 'gray' }} >生成考试码</span> |
|
|
|
)} |
|
|
|
)} |
|
|
|
<span style={{ margin: '0 8px' }}>|</span> |
|
|
|
{/*<a onClick={(e) => {*/} |
|
|
|
|
|
|
|
{/* handleGenerateQrCode(record);*/} |
|
|
|
|
|
|
|
{/*}}*/} |
|
|
|
|
|
|
|
{/*>*/} |
|
|
|
|
|
|
|
{/* 生成考试码*/} |
|
|
|
|
|
|
|
{/*</a>*/} |
|
|
|
{record.publishStatus === 0 ? ( |
|
|
|
{record.publishStatus === 0 ? ( |
|
|
|
<a onClick={() => handlePublishSingle(record.examId)}>发布</a> |
|
|
|
<a onClick={() => handlePublishSingle(record.examId)}>发布</a> |
|
|
|
) : record.publishStatus === 1 ? ( |
|
|
|
) : record.publishStatus === 1 ? ( |
|
|
@ -397,9 +421,8 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
) : ( |
|
|
|
) : ( |
|
|
|
<span style={{ color: 'gray' }}>撤回</span> |
|
|
|
<span style={{ color: 'gray' }}>撤回</span> |
|
|
|
)} |
|
|
|
)} |
|
|
|
<span style={{ margin: '0 8px' }}>|</span> |
|
|
|
|
|
|
|
<a onClick={() => handleDeleteSingle(record.examId)}>删除</a> |
|
|
|
<a onClick={() => handleDeleteSingle(record.examId)}>删除</a> |
|
|
|
</div> |
|
|
|
</Space> |
|
|
|
), |
|
|
|
), |
|
|
|
}, |
|
|
|
}, |
|
|
|
]; |
|
|
|
]; |
|
|
@ -411,6 +434,43 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
handleSearch(pagination.current); |
|
|
|
handleSearch(pagination.current); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleDownloadQRCode = () => { |
|
|
|
|
|
|
|
let formData = { |
|
|
|
|
|
|
|
id: examId, |
|
|
|
|
|
|
|
captcha: captcha, |
|
|
|
|
|
|
|
path: qrCodeData |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
saveQrCode(formData).then(res => { |
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
|
|
const canvas = qrCodeRef.current.querySelector('canvas'); |
|
|
|
|
|
|
|
if (canvas) { |
|
|
|
|
|
|
|
const dataUrl = canvas.toDataURL('image/png'); |
|
|
|
|
|
|
|
const link = document.createElement('a'); |
|
|
|
|
|
|
|
link.href = dataUrl; |
|
|
|
|
|
|
|
link.download = examName + '-考试码.png'; |
|
|
|
|
|
|
|
link.click(); |
|
|
|
|
|
|
|
setQrCodeVisible(false) |
|
|
|
|
|
|
|
handleSearch(pagination.current); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleRefreshQRCode = (examId: any) => { |
|
|
|
|
|
|
|
const str = '/' + Math.floor(Math.random() * 900000); |
|
|
|
|
|
|
|
setQrCodeData(examId + str); |
|
|
|
|
|
|
|
setCaptcha(generateCaptcha()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const generateCaptcha = () => { |
|
|
|
|
|
|
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; |
|
|
|
|
|
|
|
let captcha = ''; |
|
|
|
|
|
|
|
for (let i = 0; i < 6; i++) { |
|
|
|
|
|
|
|
const randomIndex = Math.floor(Math.random() * characters.length); |
|
|
|
|
|
|
|
captcha += characters[randomIndex]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return captcha; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<div className="list-filter"> |
|
|
|
<div className="list-filter"> |
|
|
|
<Form |
|
|
|
<Form |
|
|
@ -462,7 +522,7 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
</Select> |
|
|
|
</Select> |
|
|
|
</Form.Item> |
|
|
|
</Form.Item> |
|
|
|
<Form.Item |
|
|
|
<Form.Item |
|
|
|
label="考试有效时间:" |
|
|
|
label="考试有效日期:" |
|
|
|
> |
|
|
|
> |
|
|
|
<DatePicker |
|
|
|
<DatePicker |
|
|
|
value={searchForm.validTime} // 使用 Moment 类型
|
|
|
|
value={searchForm.validTime} // 使用 Moment 类型
|
|
|
@ -497,6 +557,7 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<Table |
|
|
|
<Table |
|
|
|
columns={columns} |
|
|
|
columns={columns} |
|
|
|
|
|
|
|
loading={loading} |
|
|
|
dataSource={currentPageExamList} |
|
|
|
dataSource={currentPageExamList} |
|
|
|
rowKey="examId" |
|
|
|
rowKey="examId" |
|
|
|
pagination={pagination} |
|
|
|
pagination={pagination} |
|
|
@ -505,12 +566,24 @@ const ExamListPage = ({ history }: { history: any }) => { |
|
|
|
onChange={handleTableChange} |
|
|
|
onChange={handleTableChange} |
|
|
|
/> |
|
|
|
/> |
|
|
|
<Modal |
|
|
|
<Modal |
|
|
|
title="考试码二维码" |
|
|
|
title={<div style={{ textAlign: 'center' }}>生成考试码</div>} |
|
|
|
visible={qrCodeVisible} |
|
|
|
visible={qrCodeVisible} |
|
|
|
onCancel={() => setQrCodeVisible(false)} |
|
|
|
onCancel={() => setQrCodeVisible(false)} |
|
|
|
footer={null} |
|
|
|
onOk={handleDownloadQRCode} |
|
|
|
|
|
|
|
centered |
|
|
|
|
|
|
|
okText="确认" |
|
|
|
> |
|
|
|
> |
|
|
|
{/*<QRCode value={qrCodeData} size={200} />*/} |
|
|
|
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: '20px' }}> |
|
|
|
|
|
|
|
<div ref={qrCodeRef} className="qrCodeRef"> |
|
|
|
|
|
|
|
<QRCode value={'https://4s27589r64.zicp.fun/wx/'+qrCodeData} size={200} fgColor="#000000" ecLevel="H"/> |
|
|
|
|
|
|
|
<div className="overlay"> |
|
|
|
|
|
|
|
<a onClick={() => handleRefreshQRCode(examId)} className="refresh-link"><UndoOutlined />点击刷新</a> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: '20px',fontWeight: 'bolder', fontSize: 'larger' }}> |
|
|
|
|
|
|
|
<span>验证码:{captcha}</span> |
|
|
|
|
|
|
|
</div> |
|
|
|
</Modal> |
|
|
|
</Modal> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
); |
|
|
|
); |
|
|
|