You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
645 lines
28 KiB
645 lines
28 KiB
import React, { Component } from 'react'; |
|
import { Form, Input, Button, Radio, Checkbox, Select, message, Modal, Table } from 'antd'; |
|
import { getRandomQuestions, addExamPaper, editExamPaper, getExamPaperDetail} from 'api/examPaper'; |
|
import { getList ,findIndustry} from 'api/question'; |
|
import { dictionary } from "../../api/dict"; |
|
import TextArea from "antd/es/input/TextArea"; |
|
|
|
const { Option } = Select; |
|
|
|
interface optionsState { |
|
value: string; |
|
label: string; |
|
} |
|
|
|
interface QuestionState { |
|
id: string; |
|
serviceTypeId: string; |
|
questionTypes: string; |
|
questionContent: string; |
|
answerOptions: optionsState[]; |
|
answer: string | null; |
|
} |
|
|
|
interface States { |
|
id: string| null; |
|
isEdit: string| null; |
|
num: number; |
|
page: number; |
|
total: number; |
|
industryDict: any; |
|
serviceTypeDict: any; |
|
loading: boolean; |
|
questions: QuestionState[]; |
|
isModalVisible: boolean; |
|
selectedParams: any; |
|
selectedQuestionList: QuestionState[]; |
|
questionSelectList: QuestionState[]; |
|
selectedRowKeys: string[]; // 修改为存储 id 的数组 |
|
} |
|
|
|
class ExamPaperAdd extends Component<any, States> { |
|
formRef: any; |
|
formRefSub: any; |
|
constructor(props: any) { |
|
super(props); |
|
this.formRef = React.createRef(); |
|
this.formRefSub = React.createRef(); |
|
this.state = { |
|
id: null, |
|
isEdit: null, |
|
num: 10, |
|
page: 1, |
|
total: 0, |
|
industryDict: undefined, |
|
serviceTypeDict: undefined, |
|
loading: false, |
|
questions: [], |
|
isModalVisible: false, |
|
selectedParams: {}, |
|
selectedQuestionList: [], |
|
questionSelectList: [], |
|
selectedRowKeys: [] |
|
}; |
|
} |
|
|
|
componentDidMount() { |
|
this.handleFindDict(); |
|
const id = sessionStorage.getItem('id'); |
|
const isEdit= sessionStorage.getItem('isEdit'); |
|
this.setState({ id: id ,isEdit: isEdit}); |
|
sessionStorage.removeItem('id'); |
|
sessionStorage.removeItem('isEdit'); |
|
if(isEdit === 'true'){ |
|
this.handleGetDetail(id); |
|
} |
|
} |
|
|
|
// 字典 |
|
handleFindDict() { |
|
// 监管行业 |
|
findIndustry() |
|
.then((res: any) => { |
|
if (res.data) { |
|
this.setState({ industryDict: res.data }); |
|
} |
|
}) |
|
.catch(() => { |
|
message.error("获取监管行业字典数据失败,请稍后重试"); |
|
}); |
|
|
|
// AQ服务类型 |
|
dictionary("serviceTypeDict") |
|
.then((res) => { |
|
if (res.data) { |
|
this.setState({ serviceTypeDict: res.data }); |
|
} |
|
}) |
|
.catch(() => { |
|
message.error("获取AQ服务类型字典数据失败,请稍后重试"); |
|
}); |
|
} |
|
|
|
// 试卷详情 |
|
handleGetDetail = (id: any) => { |
|
getExamPaperDetail(id).then((res: any) => { |
|
if (res.data) { |
|
const newQuestions: QuestionState[] = res.data.data.map((questionData: any) => { |
|
const options = questionData.options.split(','); |
|
const answerOptions = options.map((option: any) => { |
|
const [value, label] = option.split('.'); |
|
return { value, label }; |
|
}); |
|
return { |
|
id: String(questionData.id), |
|
serviceTypeId: String(questionData.serviceTypeId), |
|
questionTypes: String(questionData.questionTypes), |
|
questionContent: String(questionData.questionContent), |
|
answerOptions: answerOptions, |
|
answer: String(questionData.answer) |
|
}; |
|
}); |
|
this.setState({ questions: newQuestions }); |
|
const formValues = {}; |
|
formValues['paperName'] = res.data.head.paperName; |
|
formValues['industryId'] = String(res.data.head.industryId); |
|
formValues['questionCount'] = res.data.head.questionCount; |
|
formValues['totalScore'] = res.data.head.totalScore; |
|
formValues['examDuration'] = res.data.head.examDuration; |
|
formValues['durationType'] = String(res.data.head.durationType); |
|
formValues['paperContent'] = res.data.head.paperContent; |
|
this.formRef.current.setFieldsValue(formValues); |
|
} |
|
}).catch(() => { |
|
message.error('获取题目详情失败,请重试'); |
|
}); |
|
}; |
|
|
|
// 随机生成 |
|
handleRandomQuestion = () => { |
|
this.formRef.current.validateFields(['industryId', 'questionCount']) |
|
.then((values: any) => { |
|
const data = { |
|
industryId: values.industryId, |
|
questionCount: values.questionCount |
|
}; |
|
getRandomQuestions(data) |
|
.then((res: any) => { |
|
if (res.data) { |
|
if (res.data.length == 0) { |
|
message.warning('题库题目不足'); |
|
this.setState({ questions: [] }); |
|
return; |
|
} |
|
const newQuestions: QuestionState[] = res.data.map((questionData: any) => { |
|
const options = questionData.options.split(','); |
|
const answerOptions = options.map((option: any) => { |
|
const [value, label] = option.split('.'); |
|
return { value, label }; |
|
}); |
|
return { |
|
id: String(questionData.id), |
|
serviceTypeId: String(questionData.serviceTypeId), |
|
questionTypes: String(questionData.questionTypes), |
|
questionContent: String(questionData.questionContent), |
|
answerOptions: answerOptions, |
|
answer: String(questionData.answer) |
|
}; |
|
}); |
|
this.setState({ questions: newQuestions }); |
|
message.success('成功获取题目'); |
|
} else { |
|
message.error('未获取到随机题目'); |
|
} |
|
}) |
|
.catch(() => { |
|
message.error('获取随机题目失败'); |
|
}); |
|
}) |
|
.catch(() => { }); |
|
}; |
|
|
|
// 手动选题 |
|
handleOpenModal = () => { |
|
const { questions, selectedQuestionList } = this.state; |
|
this.formRef.current.validateFields(['industryId']) |
|
.then((values: any) => { |
|
const industryId = values.industryId; |
|
const questionCount = values.questionCount; |
|
this.setState({ |
|
isModalVisible: true, |
|
selectedQuestionList: questions, |
|
selectedParams: { selectedQuestionList, industryId, questionCount } |
|
}, () => { |
|
this.handleQuery(); // 打开模态框时执行查询 |
|
}); |
|
}) |
|
.catch(() => { }); |
|
}; |
|
|
|
// 关闭选题 |
|
handleCloseModal = () => { |
|
this.setState({ |
|
isModalVisible: false, |
|
selectedParams: {}, |
|
selectedQuestionList: [], |
|
questionSelectList: [], |
|
selectedRowKeys: [], |
|
num: 10, |
|
page: 1, |
|
total: 0, |
|
}); |
|
if (this.formRefSub.current) { |
|
this.formRefSub.current.resetFields(); |
|
} |
|
}; |
|
|
|
// 处理删除已选试题 |
|
handleDeleteQuestion = (record: QuestionState) => { |
|
const { selectedQuestionList} = this.state; |
|
const newQuestion = selectedQuestionList.filter(item => item.id!== record.id); |
|
const newSelectedRowKeys = newQuestion.map(item => item.id); |
|
this.setState({ selectedQuestionList: newQuestion, selectedRowKeys: newSelectedRowKeys }); |
|
}; |
|
|
|
// 处理查询 |
|
handleQuery = () => { |
|
this.setState({ loading: true }); |
|
const values = this.formRefSub.current.getFieldsValue(); |
|
const { num, page, selectedParams } = this.state; |
|
const industryId = selectedParams.industryId; |
|
const data = { |
|
...values, |
|
industryId, |
|
num, |
|
page |
|
}; |
|
getList(data) |
|
.then((res: any) => { |
|
if (res.data) { |
|
this.setState({ total: res.data.total }); |
|
const newQuestions: QuestionState[] = res.data.data.map((questionData: any) => { |
|
const options = questionData.options.split(','); |
|
const answerOptions = options.map((option: any) => { |
|
const [value, label] = option.split('.'); |
|
return { value, label }; |
|
}); |
|
return { |
|
id: String(questionData.id), |
|
serviceTypeId: String(questionData.serviceTypeId), |
|
questionTypes: String(questionData.questionTypes), |
|
questionContent: String(questionData.questionContent), |
|
answerOptions: answerOptions, |
|
answer: String(questionData.answer) |
|
}; |
|
}); |
|
const { selectedQuestionList } = this.state; |
|
const newSelectedRowKeys = selectedQuestionList.map(item => item.id); |
|
this.setState({ questionSelectList: newQuestions, selectedRowKeys: newSelectedRowKeys }); |
|
} |
|
}) |
|
.catch(() => { |
|
message.error('获取数据失败'); |
|
}) |
|
.finally(() => { |
|
this.setState({ loading: false }); |
|
}); |
|
}; |
|
|
|
// 确认 |
|
handleConfirmSelection = () => { |
|
const { selectedQuestionList } = this.state; |
|
this.setState({ |
|
questions: selectedQuestionList, |
|
isModalVisible: false, |
|
selectedParams: {}, |
|
selectedQuestionList: [], |
|
questionSelectList: [], |
|
selectedRowKeys: [], |
|
num: 10, |
|
page: 1, |
|
total: 0, |
|
}); |
|
if (this.formRefSub.current) { |
|
this.formRefSub.current.resetFields(); |
|
} |
|
this.formRef.current.setFieldsValue({ questionCount : selectedQuestionList.length }); |
|
}; |
|
|
|
// 保存试卷 |
|
handleSaveExamPaper = () => { |
|
const { isEdit, id } = this.state; |
|
this.formRef.current.validateFields() |
|
.then((values: any) => { |
|
const { questions } = this.state; |
|
if (questions === null || questions.length === 0) { |
|
message.error('请选择题目'); |
|
return; |
|
} |
|
const questionIds = questions.map(question => question.id); |
|
const data = { |
|
...values, |
|
id, |
|
questionIds |
|
}; |
|
if (isEdit === 'true') { |
|
editExamPaper(data) |
|
.then((res) => { |
|
const success = res["success"]; |
|
if (success) { |
|
message.success('试卷更新成功'); |
|
this.props.history.push('/examPaperList'); |
|
} else { |
|
message.error('试卷更新失败'); |
|
} |
|
}) |
|
.catch(() => { |
|
message.error('试卷更新时出错,请稍后重试'); |
|
}) |
|
} else { |
|
addExamPaper(data) |
|
.then((res) => { |
|
const success = res["success"]; |
|
if (success) { |
|
message.success('试卷保存成功'); |
|
this.props.history.push('/examPaperList'); |
|
} else { |
|
message.error('试卷保存失败'); |
|
} |
|
}) |
|
.catch(() => { |
|
message.error('试卷保存时出错,请稍后重试'); |
|
}) |
|
} |
|
}); |
|
}; |
|
|
|
render() { |
|
const { |
|
industryDict, |
|
serviceTypeDict, |
|
questions, |
|
loading, |
|
isModalVisible, |
|
selectedQuestionList, |
|
questionSelectList, |
|
selectedRowKeys |
|
} = this.state; |
|
const changePage = (current: number, pageSize: number) => { |
|
setTimeout(() => { |
|
this.setState({ page: current, num: pageSize }); |
|
this.handleQuery(); |
|
}, 0); |
|
}; |
|
const handleListQuestion = () => { |
|
this.props.history.push('/examPaperList'); |
|
}; |
|
const columns: any = [ |
|
{ title: '序号', dataIndex: 'index', align: 'center', width: 60, |
|
render: (_: number, __: number, index: number) => index + 1 |
|
}, |
|
{ title: 'AQ服务类型', dataIndex: 'serviceTypeId', key: 'serviceTypeId', align: 'center', width: 150, |
|
render: (serviceTypeId: any) => { |
|
const serviceType = serviceTypeDict?.find((item: { dictKey: string | number; dictValue: string }) => String(item.dictKey) === serviceTypeId); |
|
return serviceType? serviceType.dictValue : serviceTypeId; |
|
} |
|
}, |
|
{ title: '题型', dataIndex: 'questionTypes', key: 'questionTypes', align: 'center', width: 80, |
|
render: (questionTypes: any) => { |
|
if (questionTypes === '1') { |
|
return '单选题'; |
|
} else if (questionTypes === '2') { |
|
return '多选题'; |
|
} |
|
return questionTypes; |
|
} |
|
}, |
|
{ title: '题干', dataIndex: 'questionContent', key: 'questionContent', align: 'center', width: 450 }, |
|
{ title: '答案', dataIndex: 'answer', key: 'answer', align: 'center', width: 120 }, |
|
{ title: '操作', dataIndex: 'operation', align: 'center', width: 120, |
|
render: (_: number, record: QuestionState) => ( |
|
<Button type="link" danger onClick={() => this.handleDeleteQuestion(record)}>删除</Button> |
|
) |
|
} |
|
]; |
|
|
|
const columnsWithoutOperation = columns.filter((column: any) => column.dataIndex!== 'operation'); |
|
return ( |
|
<div className="container"> |
|
<Form ref={this.formRef}> |
|
<h3 style={{ fontWeight: 'bold' }}>基本信息</h3> |
|
<Form.Item |
|
label="试卷名称:" |
|
name="paperName" |
|
style={{ width: 1190 }} |
|
rules={[{ required: true, message: '请输入试卷名称' }]} |
|
> |
|
<Input placeholder="请输入试卷名称" /> |
|
</Form.Item> |
|
<div style={{ display: 'flex', gap: 20 }}> |
|
<Form.Item |
|
label="监管行业:" |
|
name={`industryId`} |
|
rules={[{ required: true, message: '请选择监管行业' }]} |
|
> |
|
<Select placeholder="请选择监管行业" style={{ width: 240 }} allowClear> |
|
{industryDict && industryDict.length > 0? ( |
|
(() => { |
|
let rows = []; |
|
for (let i = 0; i < industryDict.length; i++) { |
|
const item = industryDict[i]; |
|
rows.push( |
|
<Option value={item.industryId}>{item.industryName}</Option> |
|
); |
|
} |
|
return rows; |
|
})() |
|
) : ( |
|
<Option disabled>暂无数据</Option> |
|
)} |
|
</Select> |
|
</Form.Item> |
|
<Form.Item |
|
label="题目数量:" |
|
name="questionCount" |
|
style={{ width: 240 }} |
|
rules={[{ required: true, message: '请输入题目数量' }]} |
|
> |
|
<Input type="number" style={{ textAlign: 'right' }} placeholder="请输入题目数量" min={1} /> |
|
</Form.Item> |
|
<Form.Item |
|
label="总分值:" |
|
name="totalScore" |
|
style={{ width: 240 }} |
|
rules={[{ required: true, message: '请输入总分值' }]} |
|
> |
|
<Input type="number" style={{ textAlign: 'right' }} placeholder="请输入总分值" min={1} /> |
|
</Form.Item> |
|
<Form.Item |
|
label="考试时长:" |
|
name="examDuration" |
|
style={{ width: 240 }} |
|
rules={[{ required: true, message: '请输入考试时长' }]} |
|
> |
|
<Input type="number" style={{ textAlign: 'right' }} placeholder="请输入考试时长" min={1} /> |
|
</Form.Item> |
|
<Form.Item |
|
name="durationType" |
|
style={{ width: 90 }} |
|
rules={[{ required: true, message: '请输入考试时长' }]} |
|
> |
|
<Select> |
|
<Option value="1">分(min)</Option> |
|
<Option value="2">时(h)</Option> |
|
</Select> |
|
</Form.Item> |
|
</div> |
|
<Form.Item |
|
label="内容描述:" |
|
name="paperContent" |
|
style={{ width: 1190 }} |
|
rules={[{ required: true, message: '请输入内容描述' }]} |
|
> |
|
<Input.TextArea placeholder="请输入内容描述" maxLength={200} showCount/> |
|
</Form.Item> |
|
<div style={{ display: 'flex' }}> |
|
<h3 style={{ fontWeight: 'bold' }}>试题详情</h3> |
|
<div> |
|
<Button type="link" onClick={this.handleRandomQuestion}> |
|
随机生成 |
|
</Button> |
|
<Button type="link" onClick={this.handleOpenModal}> |
|
手动选题 |
|
</Button> |
|
</div> |
|
</div> |
|
{questions.map((question, index) => ( |
|
<div |
|
style={{ |
|
border: '1px solid #e8e8e8', |
|
padding: 10, |
|
marginBottom: 10 |
|
}} |
|
> |
|
<span style={{ fontWeight: 'bold' }}>{index + 1}. {question.questionContent}</span> |
|
<Form.Item> |
|
{question.questionTypes === '1'? ( |
|
<Radio.Group value={question.answer}> |
|
{question.answerOptions.map((option) => ( |
|
<Radio value={option.value}> |
|
{option.value}. {option.label} |
|
</Radio> |
|
))} |
|
</Radio.Group> |
|
) : ( |
|
<Checkbox.Group value={question.answer? question.answer.split(',') : []}> |
|
{question.answerOptions.map((option) => ( |
|
<Checkbox value={option.value}> |
|
{option.value}. {option.label} |
|
</Checkbox> |
|
))} |
|
</Checkbox.Group> |
|
)} |
|
</Form.Item> |
|
</div> |
|
))} |
|
<div |
|
style={{ |
|
textAlign: 'right', |
|
position: 'fixed', |
|
bottom: 10, |
|
right: 10, |
|
width: '100%', |
|
backgroundColor: 'white', |
|
zIndex: 1000 |
|
}} |
|
> |
|
<Button type="default" htmlType="button" onClick={handleListQuestion} style={{ marginRight: 10 }}> |
|
取消 |
|
</Button> |
|
<Button type="primary" htmlType="submit" onClick={this.handleSaveExamPaper}> |
|
确定 |
|
</Button> |
|
</div> |
|
</Form> |
|
<Modal |
|
title="选择试题" |
|
open={isModalVisible} |
|
maskClosable={false} |
|
onCancel={this.handleCloseModal} |
|
footer={[ |
|
<Button key="cancel" onClick={this.handleCloseModal}> |
|
取消 |
|
</Button>, |
|
<Button key="ok" type="primary" onClick={this.handleConfirmSelection}> |
|
确定 |
|
</Button> |
|
]} |
|
width={1100} |
|
style={{ top: 20 }} |
|
> |
|
<div> |
|
<h3 style={{ fontWeight: 'bold' }}>已选列表</h3> |
|
<Table |
|
dataSource={selectedQuestionList} |
|
columns={columns} |
|
bordered={true} |
|
scroll={{ y: 200 }} |
|
size={'small'} |
|
/> |
|
</div> |
|
<div style={{ marginTop: '20px' }}> |
|
<h3 style={{ fontWeight: 'bold' }}>试题选择</h3> |
|
<Form |
|
className="filter" |
|
layout="inline" |
|
ref={this.formRefSub} |
|
> |
|
<Form.Item |
|
label="AQ服务类型:" |
|
name="serviceTypeId" |
|
> |
|
<Select placeholder="请选择AQ服务类型" |
|
style={{ width: 240 }} |
|
allowClear> |
|
{ |
|
serviceTypeDict && serviceTypeDict.length > 0? |
|
(() => { |
|
let rows = []; |
|
for (let i = 0; i < serviceTypeDict.length; i++) { |
|
const item = serviceTypeDict[i]; |
|
rows.push( |
|
<Option value={item.dictKey}>{item.dictValue}</Option> |
|
); |
|
} |
|
return rows; |
|
})() |
|
: |
|
<Option disabled>暂无数据</Option> |
|
} |
|
</Select> |
|
</Form.Item> |
|
<Form.Item |
|
label="题干条件:" |
|
name="questionContent" |
|
> |
|
<Input placeholder="请输入题干条件" style={{ width: 500 }}/> |
|
</Form.Item> |
|
<Form.Item> |
|
<Button type="primary" onClick={this.handleQuery}>查询</Button> |
|
</Form.Item> |
|
</Form> |
|
</div> |
|
<Table |
|
dataSource={questionSelectList} |
|
columns={columnsWithoutOperation} |
|
bordered={true} |
|
size={'small'} |
|
rowKey="id" |
|
scroll={{ y: 200 }} |
|
loading={loading} |
|
rowSelection={{ |
|
selectedRowKeys: selectedRowKeys, |
|
onChange: (newSelectedRowKeys, selectedRows) => { |
|
const { selectedQuestionList,questionSelectList } = this.state; |
|
const newSelectedQuestionList = selectedRows.reduce((acc, row) => { |
|
if (!acc.some(item => item.id === row.id)) { |
|
acc.push(row); |
|
} |
|
return acc; |
|
}, [...selectedQuestionList]); |
|
|
|
const deselectedIds = questionSelectList |
|
.filter(item =>!newSelectedRowKeys.includes(item.id)) |
|
.map(item => item.id); |
|
|
|
const finalSelectedQuestionList = newSelectedQuestionList.filter( |
|
item =>!deselectedIds.includes(item.id) |
|
); |
|
|
|
const selectedRowKeys = finalSelectedQuestionList.map(item => String(item.id)); |
|
this.setState({ |
|
selectedQuestionList: finalSelectedQuestionList, |
|
selectedRowKeys: selectedRowKeys |
|
}); |
|
}, |
|
getCheckboxProps: () => ({ |
|
disabled: false |
|
}) |
|
}} |
|
pagination={{ |
|
total: this.state.total, |
|
current: this.state.page, |
|
showQuickJumper: true, |
|
showSizeChanger: true, |
|
showTotal: (total) => `共 ${total} 条`, |
|
onChange: changePage |
|
}} |
|
/> |
|
</Modal> |
|
</div> |
|
); |
|
} |
|
} |
|
|
|
export default ExamPaperAdd; |