|
@@ -1,10 +1,808 @@
|
|
|
import React from 'react';
|
|
|
+import { Tabs, Form, Tag, InputNumber, Radio, Row, Col, Checkbox, Icon, Input, Button, List, Cascader } from 'antd';
|
|
|
import './index.less';
|
|
|
+import DragList from '@src/components/DragList';
|
|
|
+import Editor from '@src/components/Editor';
|
|
|
import Page from '@src/containers/Page';
|
|
|
import Block from '@src/components/Block';
|
|
|
+import Select from '@src/components/Select';
|
|
|
+import { getMap, formatFormError, formatTreeData } from '@src/services/Tools';
|
|
|
+import { asyncSMessage, asyncGet } from '@src/services/AsyncTools';
|
|
|
+import { QuestionType, QuestionDifficult, QuestionStyleType, QuestionRadioDirection } from '../../../../Constant';
|
|
|
+import QuestionNoList from '../../../components/QuestionNoList';
|
|
|
+import { System } from '../../../stores/system';
|
|
|
+import { Question } from '../../../stores/question';
|
|
|
+import { Examination } from '../../../stores/examination';
|
|
|
+import { Exercise } from '../../../stores/exercise';
|
|
|
+
|
|
|
+const QuestionStyleTypeMap = getMap(QuestionStyleType, 'value', 'label');
|
|
|
|
|
|
export default class extends Page {
|
|
|
+ constructor(props) {
|
|
|
+ super(props);
|
|
|
+ this.placeList = [];
|
|
|
+ this.placeSetting = null;
|
|
|
+ this.associationContent = [];
|
|
|
+ this.uuid = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ init() {
|
|
|
+ Promise.all([
|
|
|
+ Exercise.allStruct().then(result => {
|
|
|
+ return { value: 'exercise', key: 'exercise', label: '练习', title: '练习', children: formatTreeData(result.map(row => { row.title = `${row.titleZh}/${row.titleEn}`; return row; }), 'id', 'title', 'parentId') };
|
|
|
+ }),
|
|
|
+ Examination.allStruct().then(result => {
|
|
|
+ return { value: 'examination', key: 'examination', label: '模考', title: '模考', children: formatTreeData(result.map(row => { row.title = `${row.titleZh}/${row.titleEn}`; return row; }), 'id', 'title', 'parentId') };
|
|
|
+ }),
|
|
|
+ ]).then(result => {
|
|
|
+ console.log(result);
|
|
|
+ this.setState({ moduleStructData: result });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ initData() {
|
|
|
+ const { id } = this.params;
|
|
|
+ const { form } = this.props;
|
|
|
+ let handler;
|
|
|
+ if (id) {
|
|
|
+ handler = Question.get({ id }).then(result => {
|
|
|
+ result.content = result.content || { questions: [], steps: [] };
|
|
|
+ result.keyword = result.keyword || [];
|
|
|
+ return result;
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ handler = Promise.resolve({ content: { number: 1, type: 'single', typeset: 'one', questions: [], steps: [] } });
|
|
|
+ }
|
|
|
+ handler.then(result => {
|
|
|
+ this.uuid[0] = -1;
|
|
|
+ result.content.questions.forEach((row, index) => {
|
|
|
+ const keys = [];
|
|
|
+ this.uuid[index] = 0;
|
|
|
+ if (row.select && row.select.length > 0) {
|
|
|
+ for (; this.uuid[index] < row.select.length; this.uuid[index] += 1) {
|
|
|
+ keys.push(this.uuid[index]);
|
|
|
+ form.getFieldDecorator(`content.questions[${index}].select[${this.uuid}]`);
|
|
|
+ form.getFieldDecorator(`content.questions[${index}].answer[${this.uuid}]`);
|
|
|
+ }
|
|
|
+ const answerMap = {};
|
|
|
+ row.answer.forEach(r => {
|
|
|
+ answerMap[r] = true;
|
|
|
+ });
|
|
|
+ row.answer = row.select.map(r => answerMap[r] || false);
|
|
|
+ } else if (row.answer && row.answer.length > 0) {
|
|
|
+ for (; this.uuid[index] < row.answer.length; this.uuid[index] += 1) {
|
|
|
+ keys.push(this.uuid);
|
|
|
+ form.getFieldDecorator(`content.questions[${index}].answer[${this.uuid}]`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ form.getFieldDecorator(`content.questions[${index}].keys`);
|
|
|
+ row.keys = keys;
|
|
|
+ return row;
|
|
|
+ });
|
|
|
+ result.content.steps.forEach((row, index) => {
|
|
|
+ form.getFieldDecorator(`content.steps[${index}].title`);
|
|
|
+ form.getFieldDecorator(`content.steps[${index}].stem`);
|
|
|
+ });
|
|
|
+
|
|
|
+ form.getFieldDecorator('content.step');
|
|
|
+ form.getFieldDecorator('content.number');
|
|
|
+ form.setFieldsValue(result);
|
|
|
+ return result;
|
|
|
+ })
|
|
|
+ .then((result) => {
|
|
|
+ this.setState({ associationContentIds: result.associationContent || [] });
|
|
|
+ console.log(result.questionNoIds);
|
|
|
+ if (result.questionNoIds && result.questionNoIds.length > 0) {
|
|
|
+ return Question.listNo({ ids: result.questionNoIds }).then(list => {
|
|
|
+ this.setState({ questionNos: list });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ refreshPlace(type) {
|
|
|
+ let handler = null;
|
|
|
+ if (this.placeSetting) {
|
|
|
+ handler = Promise.resolve(this.placeSetting);
|
|
|
+ } else {
|
|
|
+ handler = System.getPlace();
|
|
|
+ }
|
|
|
+ handler.then(result => {
|
|
|
+ this.placeSetting = result;
|
|
|
+ this.placeList = result[type] || [];
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ addNo() {
|
|
|
+ const { form } = this.props;
|
|
|
+ form.validateFields(['moduleStruct', 'questionNo'], (err) => {
|
|
|
+ if (!err) {
|
|
|
+ const data = form.getFieldsValue(['id', 'moduleStruct', 'questionNo']);
|
|
|
+ data.moduleStruct = data.moduleStruct.map(row => row);
|
|
|
+ data.module = data.moduleStruct.shift();
|
|
|
+ data.questionId = data.id || 0;
|
|
|
+ delete data.id;
|
|
|
+ data.no = data.questionNo;
|
|
|
+ delete data.questionNo;
|
|
|
+ // todo 合并struct+no:-连接
|
|
|
+ Question.addNo(data).then((result) => {
|
|
|
+ const { questionNos = [] } = this.state;
|
|
|
+ questionNos.push(result);
|
|
|
+ this.setState({ questionNos });
|
|
|
+ form.setFieldsValue({ moduleStruct: [], questionNo: '' });
|
|
|
+ asyncSMessage('保存成功');
|
|
|
+ }).catch((e) => {
|
|
|
+ if (e.result) form.setFields(formatFormError(data, e.result));
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ removeNo(noId) {
|
|
|
+ let { questionNos } = this.state;
|
|
|
+ questionNos = questionNos.filter(row => row.id !== noId);
|
|
|
+ Question.delNo({ id: noId }).then(() => {
|
|
|
+ this.setState({ questionNos });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ changeType(type) {
|
|
|
+ const { getFieldValue, setFieldsValue, getFieldDecorator } = this.props.form;
|
|
|
+ const number = getFieldValue('content.number');
|
|
|
+ // const keys = [];
|
|
|
+ this.uuid = [];
|
|
|
+ for (let index = 0; index < Number(number); index += 1) {
|
|
|
+ this.uuid[index] = 0;
|
|
|
+ switch (type) {
|
|
|
+ case 'double':
|
|
|
+ getFieldDecorator(`content.questions[${index}].direction`);
|
|
|
+ setFieldsValue({ [`content.questions[${index}].direction`]: 'landscape' });
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ removeQuestion(index, k) {
|
|
|
+ const { form } = this.props;
|
|
|
+ const keys = form.getFieldValue(`content.questions[${index}].keys`);
|
|
|
+ if (keys.length === 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ form.setFieldsValue({
|
|
|
+ [`content.questions[${index}].keys`]: keys.filter(key => key !== k),
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ addQuestion(index) {
|
|
|
+ const { form } = this.props;
|
|
|
+ const keys = form.getFieldValue(`content.questions[${index}].keys`) || [];
|
|
|
+ this.uuid[index] += 1;
|
|
|
+ const nextKeys = keys.concat(this.uuid[index]);
|
|
|
+ form.setFieldsValue({
|
|
|
+ [`content.questions[${index}].keys`]: nextKeys,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ orderQuestion(index, oldIndex, newIndex) {
|
|
|
+ const { form } = this.props;
|
|
|
+ const keys = form.getFieldValue(`content.questions[${index}].keys`) || [];
|
|
|
+ const tmp = keys[oldIndex];
|
|
|
+ keys[oldIndex] = keys[newIndex];
|
|
|
+ keys[newIndex] = tmp;
|
|
|
+ form.setFieldsValue({
|
|
|
+ [`content.questions[${index}].keys`]: keys,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ changeQuestion(index, k, value) {
|
|
|
+ const { form } = this.props;
|
|
|
+ let answer = form.getFieldValue(`content.questions[${index}].answer`) || [];
|
|
|
+ answer = answer.map(() => !value);
|
|
|
+ answer[k] = !!value;
|
|
|
+ form.setFieldsValue({ [`content.questions[${index}].answer`]: answer });
|
|
|
+ }
|
|
|
+
|
|
|
+ changeDouble(index, k, o, value) {
|
|
|
+ const { form } = this.props;
|
|
|
+ const direction = form.getFieldValue(`content.questions[${index}].direction`);
|
|
|
+ let answer = form.getFieldValue(`content.questions[${index}].answer`) || [];
|
|
|
+ switch (direction) {
|
|
|
+ case 'landscape':
|
|
|
+ answer[k] = answer[k].map(() => !value);
|
|
|
+ if (o >= 0) {
|
|
|
+ answer[k][o] = !!value;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 'portrait':
|
|
|
+ answer = answer.map((row) => { row[o] = !value; return row; });
|
|
|
+ if (o >= 0) {
|
|
|
+ answer[k][o] = !!value;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ }
|
|
|
+ form.setFieldsValue({ [`content.questions[${index}].answer`]: answer });
|
|
|
+ }
|
|
|
+
|
|
|
+ submit() {
|
|
|
+ const { form } = this.props;
|
|
|
+ const fields = ['type', 'place', 'difficult', 'content', 'stem', 'keyword', 'questionNoIds', 'officialContent', 'qxContent', 'associationContent'];
|
|
|
+ form.validateFields(fields, (err) => {
|
|
|
+ if (!err) {
|
|
|
+ const data = form.getFieldsValue(fields);
|
|
|
+ let handler;
|
|
|
+ data.associationContent = this.state.associationContent.map(row => row.id);
|
|
|
+ data.description = data.stem.replace(/<[^>]+>/g, '');
|
|
|
+ data.content.questions = data.content.questions.map(row => {
|
|
|
+ row.answer = row.answer.filter(r => r);
|
|
|
+ row.select = row.select.filter(r => r);
|
|
|
+ return row;
|
|
|
+ });
|
|
|
+ if (data.id) {
|
|
|
+ handler = Question.add(data);
|
|
|
+ } else {
|
|
|
+ handler = Question.edit(data);
|
|
|
+ }
|
|
|
+ handler.then(() => {
|
|
|
+ asyncSMessage('保存成功');
|
|
|
+ }).catch((e) => {
|
|
|
+ if (e.result) form.setFields(formatFormError(data, e.result));
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ searchStem() {
|
|
|
+ const { form } = this.props;
|
|
|
+ const content = form.getFieldValue('stem').replace(/<[^>]+>/g, '');
|
|
|
+ Question.searchStem({ content })
|
|
|
+ .then(result => {
|
|
|
+ if (result.list.length > 0) {
|
|
|
+ asyncGet(() => import('../../../components/SimilarQuestionNo'),
|
|
|
+ { title: '找到可匹配题目', questionNos: result.list, modal: true },
|
|
|
+ (questionNo) => {
|
|
|
+ this.inited = false;
|
|
|
+ linkTo(`/subject/question/${questionNo.questionId}`);
|
|
|
+ return Promise.resolve();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ asyncSMessage('无可匹配题目');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ renderBase() {
|
|
|
+ const { getFieldDecorator } = this.props.form;
|
|
|
+ return <Block flex>
|
|
|
+ <h1>题干信息</h1>
|
|
|
+ <Form>
|
|
|
+ {getFieldDecorator('id')(<input hidden />)}
|
|
|
+ {getFieldDecorator('stem', {
|
|
|
+ })(
|
|
|
+ <Editor modules={{
|
|
|
+ toolbar: {
|
|
|
+ container: [
|
|
|
+ ['image', 'table', 'select'],
|
|
|
+ [{ header: '1' }, { header: '2' }],
|
|
|
+ ['bold', 'underline', 'blockquote'],
|
|
|
+ [{ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }],
|
|
|
+ ],
|
|
|
+ handlers: {
|
|
|
+ table: (quill) => {
|
|
|
+ const range = quill.getSelection(true);
|
|
|
+ quill.insertText(range.index, '#table#');
|
|
|
+ },
|
|
|
+ select: (quill) => {
|
|
|
+ const range = quill.getSelection(true);
|
|
|
+ quill.insertText(range.index, '#select#');
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }} placeholder='请输入内容' onUpload={(file) => System.uploadImage(file)} />,
|
|
|
+ )}
|
|
|
+ <Button style={{ marginBottom: '10px', marginTop: '10px' }} onClick={() => {
|
|
|
+ this.searchStem();
|
|
|
+ }}>查询相似</Button>
|
|
|
+ <Row>
|
|
|
+ <Col span={12}>
|
|
|
+ <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='递进层次'>
|
|
|
+ {getFieldDecorator('content.step', {
|
|
|
+ normalize: (value, preValue) => {
|
|
|
+ if (value === undefined || value === '' || value === null) return preValue;
|
|
|
+ if (Math.abs(value - preValue) > 1) return preValue;
|
|
|
+ return value;
|
|
|
+ },
|
|
|
+ })(
|
|
|
+ <InputNumber defaultValue={0} min={1} max={10} placeholder='输入数量' />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ </Col>
|
|
|
+ <Col span={12}>
|
|
|
+ <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='折叠表格'>
|
|
|
+ <Col span={1}>行</Col>
|
|
|
+ <Col span={9} offset={1}>
|
|
|
+ {getFieldDecorator('content.table.row')(
|
|
|
+ <InputNumber placeholder='行' defaultValue={0} precision={0} min={0} />,
|
|
|
+ )}
|
|
|
+ </Col>
|
|
|
+ <Col span={1} offset={1}>列</Col>
|
|
|
+ <Col span={9} offset={1}>
|
|
|
+ {getFieldDecorator('content.table.col')(
|
|
|
+ <InputNumber placeholder='列' defaultValue={0} precision={0} min={0} />,
|
|
|
+ )}
|
|
|
+ </Col>
|
|
|
+ </Form.Item>
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ {this.renderTable()}
|
|
|
+ </Form>
|
|
|
+ </Block>;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderTable() {
|
|
|
+ const { getFieldDecorator, getFieldValue } = this.props.form;
|
|
|
+ const row = Number(getFieldValue('content.table.row'));
|
|
|
+ const col = Number(getFieldValue('content.table.col'));
|
|
|
+ const table = [];
|
|
|
+ if (row === 0 || col === 0) return table;
|
|
|
+ const span = 100 / col;
|
|
|
+
|
|
|
+ const colums = [];
|
|
|
+ for (let i = 0; i < col; i += 1) {
|
|
|
+ colums.push(<div style={{ width: `${span}%` }} className='table-col'>{getFieldDecorator(`content.table.header[${i}]`)(<Input size='small' />)}</div>);
|
|
|
+ }
|
|
|
+ table.push(<Row style={{ width: '100%' }} className='table-header' gutter={10}>{colums}</Row>);
|
|
|
+ for (let index = 0; index < row; index += 1) {
|
|
|
+ const cols = [];
|
|
|
+ for (let i = 0; i < col; i += 1) {
|
|
|
+ cols.push(<div style={{ width: `${span}%` }} className='table-col'>{getFieldDecorator(`content.table.data[${index}][${i}]`)(<Input size='small' />)}</div>);
|
|
|
+ }
|
|
|
+ table.push(<Row style={{ width: '100%' }} gutter={10}>{cols}</Row>);
|
|
|
+ }
|
|
|
+ return table;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderStep() {
|
|
|
+ const { getFieldDecorator, getFieldValue } = this.props.form;
|
|
|
+ const number = getFieldValue('content.step');
|
|
|
+ const result = [];
|
|
|
+ for (let index = 0; index < Number(number); index += 1) {
|
|
|
+ result.push(<Block flex>
|
|
|
+ <h1>递进层次 {index + 1}</h1>
|
|
|
+ <Form>
|
|
|
+ <Form.Item>
|
|
|
+ {getFieldDecorator(`content.steps[${index}].title`, {
|
|
|
+ rules: [
|
|
|
+ { required: true, message: '请输入标题' },
|
|
|
+ ],
|
|
|
+ })(
|
|
|
+ <Input placeholder='请输入标题' />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item>
|
|
|
+ {getFieldDecorator(`content.steps[${index}].stem`, {
|
|
|
+ })(
|
|
|
+ <Editor placeholder='请输入内容' />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Block>);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderIdentity() {
|
|
|
+ const { questionNos = [] } = this.state;
|
|
|
+ const { getFieldDecorator } = this.props.form;
|
|
|
+ return <Block flex>
|
|
|
+ <h1>题目身份</h1>
|
|
|
+ <Form>
|
|
|
+ <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 17 }} label='请输入题目id'>
|
|
|
+ {getFieldDecorator('questionNoIds', {
|
|
|
+ rules: [{
|
|
|
+ required: true, message: '添加关联题目ID',
|
|
|
+ }],
|
|
|
+ })(<input hidden />)}
|
|
|
+ {questionNos.map((no, index) => {
|
|
|
+ return <Tag key={index} closable onClose={() => {
|
|
|
+ this.removeNo(no.id);
|
|
|
+ }}>
|
|
|
+ {no.no}
|
|
|
+ </Tag>;
|
|
|
+ })}
|
|
|
+ <Row>
|
|
|
+ <Col span={14}>
|
|
|
+ <Form.Item>
|
|
|
+ {getFieldDecorator('moduleStruct', {
|
|
|
+ rules: [{
|
|
|
+ required: true, message: '选择题目编号关系',
|
|
|
+ }],
|
|
|
+ })(<Cascader fieldNames={{ label: 'title', value: 'value', children: 'children' }} onClick={(value) => {
|
|
|
+ this.setState({ moduleStruct: value });
|
|
|
+ }} placeholder='选择' options={this.state.moduleStructData} />)}
|
|
|
+ </Form.Item>
|
|
|
+ </Col>
|
|
|
+ <Col span={4} offset={1}>
|
|
|
+ <Form.Item>
|
|
|
+ {getFieldDecorator('questionNo', {
|
|
|
+ rules: [{
|
|
|
+ required: true, message: '输入编号',
|
|
|
+ }],
|
|
|
+ })(<Input placeholder='题目id' onClick={(value) => {
|
|
|
+ this.setState({ questionNo: value });
|
|
|
+ }} />)}
|
|
|
+ </Form.Item>
|
|
|
+ </Col>
|
|
|
+ <Col span={4} offset={1}>
|
|
|
+ <Button size='small' onClick={() => {
|
|
|
+ this.addNo();
|
|
|
+ }}><Icon type='plus' /></Button>
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label={'关键词'}>
|
|
|
+ {getFieldDecorator('keyword')(
|
|
|
+ <Select mode='tags' maxTagCount={200} notFoundContent={null} placeholder='输入多个关键词, 逗号分隔' tokenSeparators={[',', ',']} />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Block>;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderAttr() {
|
|
|
+ const { getFieldDecorator } = this.props.form;
|
|
|
+ return <Block flex>
|
|
|
+ <h1>题目属性</h1>
|
|
|
+ <Form>
|
|
|
+ <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='题型'>
|
|
|
+ {getFieldDecorator('type', {
|
|
|
+ rules: [
|
|
|
+ { required: true, message: '请选择题型' },
|
|
|
+ ],
|
|
|
+ })(
|
|
|
+ <Select select={QuestionType} placeholder='请选择题型' onChange={(v) => {
|
|
|
+ this.refreshPlace(v);
|
|
|
+ }} />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='考点'>
|
|
|
+ {getFieldDecorator('place', {
|
|
|
+ rules: [
|
|
|
+ { required: true, message: '请选择考点' },
|
|
|
+ ],
|
|
|
+ })(
|
|
|
+ <Select select={this.placeList} placeholder='请选择考点' onChange={(v) => {
|
|
|
+ this.refreshPart(v);
|
|
|
+ }} />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='难度'>
|
|
|
+ {getFieldDecorator('difficult', {
|
|
|
+ rules: [
|
|
|
+ { required: true, message: '请选择难度' },
|
|
|
+ ],
|
|
|
+ })(
|
|
|
+ <Select select={QuestionDifficult} placeholder='请选择难度' />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Block>;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderStyle() {
|
|
|
+ const { getFieldDecorator } = this.props.form;
|
|
|
+ return <Block flex>
|
|
|
+ <h1>题目样式</h1>
|
|
|
+ <Form>
|
|
|
+ <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='选项类型'>
|
|
|
+ {getFieldDecorator('content.type', {
|
|
|
+ rules: [
|
|
|
+ { required: true, message: '请选择类型' },
|
|
|
+ ],
|
|
|
+ })(
|
|
|
+ <Select select={QuestionStyleType} placeholder='请选择类型' onChange={(type) => {
|
|
|
+ this.changeType(type);
|
|
|
+ }} />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='题目数量'>
|
|
|
+ {getFieldDecorator('content.number', {
|
|
|
+ normalize: (value, preValue) => {
|
|
|
+ if (value === undefined || value === '' || value === null) return preValue;
|
|
|
+ if (Math.abs(value - preValue) > 1) return preValue;
|
|
|
+ return value;
|
|
|
+ },
|
|
|
+ })(
|
|
|
+ <InputNumber defaultValue={1} min={1} max={10} placeholder='请输入数量' />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='排版方式'>
|
|
|
+ {getFieldDecorator('content.typeset')(
|
|
|
+ <Radio.Group defaultValue='one'>
|
|
|
+ <Radio value='one'>单排</Radio>
|
|
|
+ <Radio value='two'>双排</Radio>
|
|
|
+ </Radio.Group>,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Block>;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderSelect() {
|
|
|
+ const { getFieldDecorator, getFieldValue } = this.props.form;
|
|
|
+ const number = getFieldValue('content.number');
|
|
|
+ const type = getFieldValue('content.type');
|
|
|
+ const result = [];
|
|
|
+ let handler = null;
|
|
|
+ switch (type) {
|
|
|
+ case 'single':
|
|
|
+ handler = (index) => this.renderSelectSingle(index);
|
|
|
+ break;
|
|
|
+ case 'double':
|
|
|
+ handler = (index) => this.renderSelectDouble(index);
|
|
|
+ break;
|
|
|
+ case 'inline':
|
|
|
+ handler = (index) => this.renderSelectInline(index);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ }
|
|
|
+ for (let index = 0; index < Number(number); index += 1) {
|
|
|
+ result.push(<Block flex className={type}>
|
|
|
+ <h1>选项信息({QuestionStyleTypeMap[type]})</h1>
|
|
|
+ <Form>
|
|
|
+ {type !== 'inline' && (
|
|
|
+ <Form.Item>
|
|
|
+ {getFieldDecorator(`content.questions[${index}].description`, {
|
|
|
+ })(
|
|
|
+ <Editor placeholder='选项问题说明' />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ )}
|
|
|
+ {handler(index)}
|
|
|
+ </Form>
|
|
|
+ </Block>);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderSelectSingle(index) {
|
|
|
+ const { getFieldDecorator, getFieldValue } = this.props.form;
|
|
|
+ getFieldDecorator(`content.questions[${index}].keys`);
|
|
|
+ const keys = getFieldValue(`content.questions[${index}].keys`) || [];
|
|
|
+ return [
|
|
|
+ <DragList
|
|
|
+ loading={false}
|
|
|
+ dataSource={keys || []}
|
|
|
+ handle={'.icon'}
|
|
|
+ onMove={(oldIndex, newIndex) => {
|
|
|
+ this.order(index, oldIndex, newIndex);
|
|
|
+ }}
|
|
|
+ renderItem={(k) => (
|
|
|
+ <List.Item actions={[<Icon type='bars' className='icon' />]}>
|
|
|
+ <Row key={k} style={{ width: '100%' }}>
|
|
|
+ <Col span={1}>
|
|
|
+ {getFieldDecorator(`content.questions[${index}].answer[${k}]`, {
|
|
|
+ valuePropName: 'checked',
|
|
|
+ })(
|
|
|
+ <Checkbox onChange={(value) => {
|
|
|
+ this.changeQuestion(index, k, value);
|
|
|
+ }} />,
|
|
|
+ )}
|
|
|
+ </Col>
|
|
|
+ <Col span={23}>
|
|
|
+ <Form.Item
|
|
|
+ key={k}
|
|
|
+ hidden
|
|
|
+ >
|
|
|
+ {getFieldDecorator(`content.questions[${index}].select[${k}]`, {
|
|
|
+ rules: [{
|
|
|
+ required: true,
|
|
|
+ whitespace: true,
|
|
|
+ message: '请填写选项信息',
|
|
|
+ }],
|
|
|
+ })(
|
|
|
+ <Input />,
|
|
|
+ )}
|
|
|
+ {keys.length > 1 ? (
|
|
|
+ <Icon
|
|
|
+ type='minus-circle-o'
|
|
|
+ disabled={keys.length === 1}
|
|
|
+ onClick={() => this.removeQuestion(index, k)}
|
|
|
+ />
|
|
|
+ ) : null}
|
|
|
+ </Form.Item>
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ </List.Item>
|
|
|
+ )}
|
|
|
+ />,
|
|
|
+ <Form.Item>
|
|
|
+ <Button type='dashed' onClick={() => this.addQuestion(index)} >
|
|
|
+ <Icon type='plus' /> 新增
|
|
|
+ </Button>
|
|
|
+ </Form.Item>,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ renderSelectDouble(index) {
|
|
|
+ const { getFieldDecorator, getFieldValue } = this.props.form;
|
|
|
+ getFieldDecorator(`content.questions[${index}].keys`);
|
|
|
+ const keys = getFieldValue(`content.questions[${index}].keys`) || [];
|
|
|
+ return [
|
|
|
+ <Form.Item className='no-validate' labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='单选方向'>
|
|
|
+ {getFieldDecorator(`content.questions[${index}].direction`)(
|
|
|
+ <Select select={QuestionRadioDirection} placeholder='请选择方向' onChange={(value) => {
|
|
|
+ keys.forEach((k) => this.changeDouble(index, k, -1, value));
|
|
|
+ }} />,
|
|
|
+ )}
|
|
|
+ </Form.Item>,
|
|
|
+ <List.Item actions={[<Icon type='bars' style={{ display: 'none' }} className='icon' />]}><Row style={{ width: '100%' }}>
|
|
|
+ <Col span={4}>
|
|
|
+ {getFieldDecorator(`content.questions[${index}].first`)(
|
|
|
+ <Input placeholder='选项一' />,
|
|
|
+ )}
|
|
|
+ </Col>
|
|
|
+ <Col span={4} offset={1}>
|
|
|
+ {getFieldDecorator(`content.questions[${index}].second`)(
|
|
|
+ <Input placeholder='选项二' />,
|
|
|
+ )}
|
|
|
+ </Col>
|
|
|
+ </Row></List.Item>,
|
|
|
+ <DragList
|
|
|
+ loading={false}
|
|
|
+ dataSource={keys || []}
|
|
|
+ handle={'.icon'}
|
|
|
+ onMove={(oldIndex, newIndex) => {
|
|
|
+ this.orderQuestion(index, oldIndex, newIndex);
|
|
|
+ }}
|
|
|
+ renderItem={(k) => (
|
|
|
+ <List.Item actions={[<Icon type='bars' className='icon' />]}>
|
|
|
+ <Row key={k} style={{ width: '100%' }}>
|
|
|
+ <Col span={4}>
|
|
|
+ {getFieldDecorator(`content.questions[${index}].answer[${k}][0]`, {
|
|
|
+ valuePropName: 'checked',
|
|
|
+ })(
|
|
|
+ <Checkbox onChange={(value) => {
|
|
|
+ this.changeDouble(index, k, 0, value);
|
|
|
+ }} />,
|
|
|
+ )}
|
|
|
+ </Col>
|
|
|
+ <Col span={4} offset={1}>
|
|
|
+ {getFieldDecorator(`content.questions[${index}].answer[${k}][1]`, {
|
|
|
+ valuePropName: 'checked',
|
|
|
+ })(
|
|
|
+ <Checkbox onChange={(value) => {
|
|
|
+ this.changeDouble(index, k, 1, value);
|
|
|
+ }} />,
|
|
|
+ )}
|
|
|
+ </Col>
|
|
|
+ <Col span={14} offset={1}>
|
|
|
+ <Form.Item
|
|
|
+ key={k}
|
|
|
+ hidden
|
|
|
+ >
|
|
|
+ {getFieldDecorator(`content.questions[${index}].select[${k}]`, {
|
|
|
+ rules: [{
|
|
|
+ required: true,
|
|
|
+ whitespace: true,
|
|
|
+ message: '请填写选项信息',
|
|
|
+ }],
|
|
|
+ })(
|
|
|
+ <Input />,
|
|
|
+ )}
|
|
|
+ {keys.length > 1 ? (
|
|
|
+ <Icon
|
|
|
+ type='minus-circle-o'
|
|
|
+ disabled={keys.length === 1}
|
|
|
+ onClick={() => this.removeQuestion(index, k)}
|
|
|
+ />
|
|
|
+ ) : null}
|
|
|
+ </Form.Item>
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ </List.Item>
|
|
|
+ )}
|
|
|
+ />,
|
|
|
+ <Form.Item>
|
|
|
+ <Button type='dashed' onClick={() => this.addQuestion(index)} >
|
|
|
+ <Icon type='plus' /> 新增
|
|
|
+ </Button>
|
|
|
+ </Form.Item>,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ renderSelectInline(index) {
|
|
|
+ return this.renderSelectSingle(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ renderOffical() {
|
|
|
+ const { getFieldDecorator } = this.props.form;
|
|
|
+ return <Block flex>
|
|
|
+ <Form>
|
|
|
+ <Form.Item label='官方解析'>
|
|
|
+ {getFieldDecorator('officalContent', {
|
|
|
+ })(
|
|
|
+ <Editor placeholder='输入内容' />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Block>;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderQX() {
|
|
|
+ const { getFieldDecorator } = this.props.form;
|
|
|
+ return <Block flex>
|
|
|
+ <Form>
|
|
|
+ <Form.Item label='千行解析'>
|
|
|
+ {getFieldDecorator('qxContent', {
|
|
|
+ })(
|
|
|
+ <Editor placeholder='输入内容' />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Block>;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderAssociation() {
|
|
|
+ const { getFieldDecorator, setFieldsValue } = this.props.form;
|
|
|
+ return <Block flex>
|
|
|
+ <h1>题源联想</h1>
|
|
|
+ <Form>
|
|
|
+ <Form.Item>
|
|
|
+ {getFieldDecorator('associationContent')(
|
|
|
+ <Select mode='tags' maxTagCount={200} notFoundContent={null} placeholder='输入题目id, 逗号分隔' tokenSeparators={[',', ',']} onChange={(values) => {
|
|
|
+ this.setState({ associationContent: values });
|
|
|
+ }} />,
|
|
|
+ )}
|
|
|
+ </Form.Item>
|
|
|
+ <QuestionNoList loading={false} ids={this.state.associationContentIds} nos={this.state.associationContent} onChange={(questionNos) => {
|
|
|
+ this.associationContent = questionNos;
|
|
|
+ getFieldDecorator('associationContent');
|
|
|
+ setFieldsValue({ associationContent: questionNos.map(row => row.no) });
|
|
|
+ }} />
|
|
|
+ </Form>
|
|
|
+ </Block>;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderTab() {
|
|
|
+ return <Tabs activeKey='base' onChange={(tab) => {
|
|
|
+ switch (tab) {
|
|
|
+ case 'sentence':
|
|
|
+ linkTo('/subject/sentence/question');
|
|
|
+ break;
|
|
|
+ case 'textbook':
|
|
|
+ linkTo('/subject/textbook/question');
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ }
|
|
|
+ }}>
|
|
|
+ <Tabs.TabPane key='base' tab='考试题型' />
|
|
|
+ <Tabs.TabPane key='sentence' tab='长难句' />
|
|
|
+ <Tabs.TabPane key='textbook' tab='数学机经' />
|
|
|
+ </Tabs>;
|
|
|
+ }
|
|
|
+
|
|
|
renderView() {
|
|
|
- return <Block flex />;
|
|
|
+ return <div flex >
|
|
|
+ {this.renderTab()}
|
|
|
+ {this.renderBase()}
|
|
|
+ {this.renderStep()}
|
|
|
+ {this.renderIdentity()}
|
|
|
+ {this.renderAttr()}
|
|
|
+ {this.renderStyle()}
|
|
|
+ {this.renderSelect()}
|
|
|
+ {this.renderOffical()}
|
|
|
+ {this.renderQX()}
|
|
|
+ {this.renderAssociation()}
|
|
|
+ <Row type="flex" justify="center">
|
|
|
+ <Col>
|
|
|
+ <Button type="primary" onClick={() => {
|
|
|
+ this.submit();
|
|
|
+ }}>保存</Button>
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ </div>;
|
|
|
}
|
|
|
}
|