123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- import React from 'react';
- import { Form, Input, InputNumber, Tabs, Switch, Checkbox, Row, Col, Button, Tag, Dropdown, Menu } from 'antd';
- import './index.less';
- import Page from '@src/containers/Page';
- import Block from '@src/components/Block';
- import Editor from '@src/components/Editor';
- // import ContextMenuLayout from '@src/layouts/ContextMenuLayout';
- // import Select from '@src/components/Select';
- // import FileUpload from '@src/components/FileUpload';
- import { formatFormError, generateUUID } from '@src/services/Tools';
- import { asyncSMessage } from '@src/services/AsyncTools';
- import { SentenceOption } from '../../../../Constant';
- // import { Preview } from '../../../stores/preview';
- import { Exercise } from '../../../stores/exercise';
- import { Sentence } from '../../../stores/sentence';
- import { System } from '../../../stores/system';
- import config from './index';
- const stopWords = [',', '.', ':', '!', ';', '?', '\\', '/', '`', '\'', '', '。', ',', '?', '!', '“', '”'];
- const htmlReg = /[.,!?。,!?“”]*<[^>]+>[.,!?。,!?“”]*/i;
- const sReg = /[.,!?。,!?“”]+/i;
- // const uuidReg = /@(.*)@(.*)/i;
- export default class extends Page {
- constructor(props) {
- super(props);
- this.targetWord = null;
- this.uuidMap = {};
- this.uuidList = [];
- const { id } = this.params;
- if (id) {
- config.title = '编辑长难句题目';
- } else {
- config.title = '添加长难句题目';
- }
- }
- initState() {
- return { currentKey: 'subject' };
- }
- init() {
- Exercise.allStruct().then(result => {
- result = result.filter(row => row.level === 2).map(row => { row.title = `${row.titleZh}/${row.titleEn}`; row.value = row.id; return row; });
- this.setState({ exercise: result });
- });
- }
- initData() {
- const { id } = this.params;
- const { form } = this.props;
- this.uuidMap = {};
- this.uuidList = [];
- this.targetWord = null;
- let handler;
- if (id) {
- handler = Sentence.getQuestion({ id });
- this.setState({ mode: 'setting' });
- } else {
- // "<p><span uuid='CmS0'>The</span> <span uuid='2BKs'>furthest</span> <span uuid='3JxJ'>distance</span> <span uuid='hVsF'>in</span> <span uuid='OlAJ'>the</span> <span uuid='RrCt'>world</span> <span uuid='Axff'></span> <span uuid='wovI'></span></p><p><span uuid='odI3'>Is</span> <span uuid='HHXm'>not</span> <span uuid='ld8s'>between</span> <span uuid='wuh2'>life</span> <span uuid='o29m'>and</span> <span uuid='NLCc'>death</span> <span uuid='GCaB'></span></p><p><span uuid='WENt'>But</span> <span uuid='kHpB'>when</span> <span uuid='nMb4'>I</span> <span uuid='CNoW'>stand</span> <span uuid='EwJh'>in</span> <span uuid='MOL4'>front</span> <span uuid='Vg1e'>of</span> <span uuid='aZAW'>you</span> <span uuid='z7zD'></span></p><p><span uuid='hLr7'>Yet</span> <span uuid='s7lJ'>you</span> <span uuid='xhmd'>don\"t</span> <span uuid='uKQk'>know</span> <span uuid='mDus'>that</span> <span uuid='neve'>I</span> <span uuid='yWSc'>love</span> <span uuid='7JRW'>you</span></p>"
- handler = Promise.resolve({ no: 1, question: { stem: '' } });
- }
- handler
- .then(result => {
- result.isPaper = !!result.isPaper;
- result.isTrail = !!result.isTrail;
- result.question.questionType = 'sentence';
- result.question.answer = result.question.answer || {};
- form.getFieldDecorator('question.answer.subject');
- form.getFieldDecorator('question.answer.predicate');
- form.getFieldDecorator('question.answer.object');
- form.getFieldDecorator('question.answer.options');
- form.getFieldDecorator('question.qxContent');
- form.getFieldDecorator('question.stem');
- form.getFieldDecorator('question.questionType');
- form.getFieldDecorator('question.place');
- form.getFieldDecorator('question.chineseContent');
- form.getFieldDecorator('question.id');
- form.getFieldDecorator('questionId');
- form.getFieldDecorator('title');
- form.getFieldDecorator('isPaper');
- form.getFieldDecorator('isTrail');
- form.getFieldDecorator('no');
- form.getFieldDecorator('id');
- form.setFieldsValue(result);
- const { stem } = result.question;
- const { subject, predicate, object } = result.question.answer;
- this.setState({ data: result, subject, predicate, object, stem });
- // 生成uuid列表
- this.uuidList = (stem || '').replace(/<span\suuid=["']([^>]*)["']>([^<>]*)<\/span>/g, '@@$1_@_$2@@').split('@@').filter(row => row.indexOf('_@_') >= 0).map(row => {
- const [uuid, content] = row.split('_@_');
- this.uuidMap[uuid] = content;
- return uuid;
- });
- console.log(this.uuidList, this.uuidMap);
- });
- }
- submit() {
- const { form } = this.props;
- const { mode } = this.state;
- if (mode !== 'setting') {
- asyncSMessage('请先退出编辑模式', 'warn');
- return;
- }
- form.validateFields((err) => {
- if (!err) {
- const data = form.getFieldsValue();
- data.isTrail = data.isTrail ? 1 : 0;
- data.isPaper = data.isPaper ? 1 : 0;
- data.question.stem = this.state.stem;
- data.question.description = data.question.stem.replace(/<[^>]+>/g, '');
- data.question.answer = data.question.answer || {};
- let handler;
- if (!data.id) {
- handler = Sentence.addQuestion(data);
- } else {
- handler = Sentence.editQuestion(data);
- }
- handler.then((result) => {
- asyncSMessage('保存成功');
- if (data.id) {
- linkTo(`/subject/sentence/question/${data.id}`);
- } else {
- linkTo(`/subject/sentence/question/${result.id}`);
- }
- }).catch((e) => {
- if (e.result) form.setFields(formatFormError(data, e.result));
- else {
- asyncSMessage(e.message, 'error');
- }
- });
- }
- });
- }
- removeAnswer(key, index) {
- const { setFieldsValue, getFieldValue } = this.props.form;
- const data = getFieldValue(`question.answer.${key}`) || [];
- if (data.length > index) {
- data.splice(index, 1);
- }
- setFieldsValue({ [`question.answer.${key}`]: data });
- this.setState({ [key]: data });
- }
- addAnswer(key, uuid, text) {
- const { setFieldsValue, getFieldValue } = this.props.form;
- const data = getFieldValue(`question.answer.${key}`) || [];
- data.push([{ text, uuid }]);
- setFieldsValue({ [`question.answer.${key}`]: data });
- this.setState({ [key]: data });
- }
- appendAnswer(key, index, uuid, text) {
- const { setFieldsValue, getFieldValue } = this.props.form;
- const data = getFieldValue(`question.answer.${key}`) || [];
- data[index].push({ text, uuid });
- setFieldsValue({ [`question.answer.${key}`]: data });
- this.setState({ [key]: data });
- }
- renderContextMenu() {
- const { getFieldValue } = this.props.form;
- const { currentKey } = this.state;
- const uuid = this.targetWord.getAttribute('uuid');
- const text = this.targetWord.innerText;
- const data = getFieldValue(`question.answer.${currentKey}`) || [];
- const items = [];
- items.push({ title: '新答案', command: 'new' });
- data.forEach((row, i) => {
- // 过滤已经在的答案
- if (row.filter(r => r.uuid !== uuid).length !== row.length) return;
- items.push({ title: `插入: ${row.map(r => r.text).join(', ')}`, command: 'append', i });
- });
- return (
- <Menu onClick={k => this.clickContentMenu(currentKey, k, uuid, text)}>
- {items.map((item, i) => {
- return <Menu.Item key={i} command={item.command} i={item.i}>{item.title || item.name}</Menu.Item>;
- })}
- </Menu>
- );
- }
- clickContentMenu(key, k, uuid, text) {
- const item = k.item.props;
- switch (item.command) {
- case 'new':
- this.addAnswer(key, uuid, text);
- break;
- case 'append':
- this.appendAnswer(key, item.i, uuid, text);
- break;
- default:
- }
- }
- generateContent(content) {
- const { setFieldsValue } = this.props.form;
- // 处理编辑器未清除的标签: 主要是包含空格的标签属性
- content = content.replace(/<[^>]+\s+[^>]+>([^<>]*)<\/[^>]+>/g, '$1');
- this.index = 0;
- const list = ['</p><p>', ' ', ',', '.', '!', '?', ' ', ':', ';'];
- const result = this.splitList(list, 0, content);
- // console.log(this.uuidList, this.uuidMap);
- // 对比答案,是否uuid存在
- let { subject = [], predicate = [], object = [] } = this.state;
- let flag = false;
- subject = subject.map(row => {
- return row.filter(r => {
- if (this.uuidMap[r.uuid] === r.text) return true;
- flag = true;
- return false;
- });
- }).filter(row => row.length);
- predicate = predicate.map(row => {
- return row.filter(r => {
- if (this.uuidMap[r.uuid] === r.text) return true;
- flag = true;
- return false;
- });
- }).filter(row => row.length);
- object = object.map(row => {
- return row.filter(r => {
- if (this.uuidMap[r.uuid] === r.text) return true;
- flag = true;
- return false;
- });
- }).filter(row => row.length);
- if (flag) {
- asyncSMessage('修改影响答案,请再次确认答案', 'warn');
- setFieldsValue({ 'question.answer.subject': subject, 'question.answer.predicate': predicate, 'question.answer.object': object });
- // console.log(subject, predicate, object);
- this.setState({ subject, predicate, object });
- }
- return result;
- }
- splitList(list, index, content) {
- if (list.length === index) {
- return this.generateText(content);
- }
- const result = content.split(list[index]).map(row => {
- return this.splitList(list, index + 1, row);
- });
- if (result.length === 0) return content;
- if (result.length === 1) return result[0];
- return result.join(list[index]);
- }
- generateText(content) {
- if (content.indexOf(stopWords) >= 0) return content;
- // 判断是否包含html标签
- const r = htmlReg.exec(content);
- if (r === null) {
- const sr = sReg.exec(content);
- if (sr === null) {
- return this.generateTag(content);
- }
- // 句尾标点符号
- return `${this.generateTag(content.replace(sr[0], ''))}${sr[0]}`;
- }
- if (r.index === 0) {
- // 头部html标签
- return `${r[0]}${this.generateTag(content.replace(r[0], ''))}`;
- }
- // 尾部html标签
- return `${this.generateTag(content.replace(r[0], ''))}${r[0]}`;
- }
- generateTag(content) {
- // return `<span>${content}</span>`;
- // 处理uuid信息
- // const r = uuidReg.exec(content);
- // if (r === null) {
- let uuid;
- if (this.uuidList.length <= this.index) {
- do {
- uuid = generateUUID(4);
- } while (this.uuidMap[uuid]);
- this.uuidMap[uuid] = content;
- this.uuidList.push(uuid);
- } else {
- uuid = this.uuidList[this.index];
- this.uuidMap[uuid] = content;
- }
- this.index += 1;
- return `<span uuid='${uuid}'>${content}</span>`;
- // }
- // uuidMap[r[1]] = `${r[2]}`;
- // return `<span uuid='${r[1]}'>${r[2]}</span>`;
- }
- renderTitle() {
- const { getFieldDecorator } = this.props.form;
- return <Block>
- <Form>
- {getFieldDecorator('id')(<input hidden />)}
- <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='题目序号'>
- {getFieldDecorator('no', {
- rules: [
- { required: true, message: '请输入序号' },
- // {
- // validator: (rule, value, callback) => {
- // if (this.partList.indexOf(value) >= 0) callback('该part已被使用');
- // else callback();
- // callback();
- // },
- // },
- ],
- })(
- <InputNumber min={1} precision={0} formatter={(v) => parseInt(v, 10) || 1} />,
- )}
- </Form.Item>
- <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='名称'>
- {getFieldDecorator('title', {
- rules: [
- { required: true, message: '请输入名称' },
- ],
- })(
- <Input placeholder='请输入' />,
- )}
- </Form.Item>
- <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='开放试用'>
- {getFieldDecorator('isTrail', {
- valuePropName: 'checked',
- })(
- <Switch checkedChildren='on' unCheckedChildren='off' />,
- )}
- </Form.Item>
- {/* 不允许修改组卷逻辑 */}
- <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='组卷题目' >
- {getFieldDecorator('isPaper', {
- valuePropName: 'checked',
- })(
- <Switch checkedChildren='on' unCheckedChildren='off' />,
- )}
- </Form.Item>
- </Form>
- </Block>;
- }
- renderBase() {
- const { getFieldDecorator, getFieldValue, setFieldsValue } = this.props.form;
- const { stem, mode } = this.state;
- return <Block flex>
- <h1>题干信息:
- <Switch checked={mode !== 'setting'} checkedChildren='编辑模式' unCheckedChildren='设置模式' onChange={(value) => {
- if (value) {
- // console.log(stem);
- getFieldDecorator('question.stem');
- setFieldsValue({ 'question.stem': stem });
- this.setState({ mode: 'edit' });
- } else {
- // 编辑器未修改会使用添加好uuid标签的结果
- // 通过保留onChange获取到最后一次修改记录
- this.setState({ mode: 'setting', stem: this.generateContent(this.stem || getFieldValue('question.stem') || '') });
- }
- }} /></h1>
- <Form>
- {getFieldDecorator('question.id')(<input hidden />)}
- {<Form.Item style={{ display: (mode === 'edit' || !mode) ? 'block' : 'none' }}>
- {getFieldDecorator('question.stem')(
- <Editor
- // onChange={(content, delta, source, editor) => {
- // console.log(content, delta, source, editor);
- // setFieldsValue({ 'question.stem': content });
- // }}
- onChange={(content) => {
- this.stem = content;
- // console.log(this.stem);
- }}
- placeholder='请输入内容' />,
- )}
- </Form.Item>}
- {mode === 'setting' && <Dropdown overlay={() => this.renderContextMenu()} trigger={['click']}><div className='stem-content' dangerouslySetInnerHTML={{ __html: stem }} onClick={(e) => {
- this.targetWord = e.target;
- }} /></Dropdown>}
- </Form>
- </Block>;
- }
- renderAnswer() {
- const { getFieldDecorator } = this.props.form;
- const { subject = [], predicate = [], object = [], currentKey } = this.state;
- return <Block flex>
- <h1>题目答案: <span>进入设置模式,选择主谓宾后,点击答案单词</span></h1>
- <table boarder cellSpacing className='answer'>
- <tr>
- <td width={30}><Checkbox checked={currentKey === 'subject'} onClick={(value) => value.target.checked && this.setState({ currentKey: 'subject' })} /> 主语</td>
- <td><Form.Item>
- {getFieldDecorator('question.answer.subject', {
- rules: [{ required: true, message: '请输入主语' }],
- })(<input hidden />)}
- {subject.map((row, index) => {
- return <Tag key={index} closable visible onClose={() => {
- this.removeAnswer('subject', index);
- }}>{row.map(r => r.text).join(', ')}</Tag>;
- })}
- </Form.Item></td>
- </tr>
- <tr>
- <td><Checkbox checked={currentKey === 'predicate'} onClick={(value) => value.target.checked && this.setState({ currentKey: 'predicate' })} /> 谓语</td>
- <td><Form.Item>
- {getFieldDecorator('question.answer.predicate', {
- rules: [{ required: true, message: '请输入谓语' }],
- })(<input hidden />)}
- {predicate.map((row, index) => {
- return <Tag key={index} closable visible onClose={() => {
- this.removeAnswer('predicate', index);
- }}>{row.map(r => r.text).join(', ')}</Tag>;
- })}
- </Form.Item></td>
- </tr>
- <tr>
- <td><Checkbox checked={currentKey === 'object'} onClick={(value) => value.target.checked && this.setState({ currentKey: 'object' })} /> 宾语</td>
- <td><Form.Item>
- {getFieldDecorator('question.answer.object', {
- rules: [{ required: true, message: '请输入宾语' }],
- })(<input hidden />)}
- {object.map((row, index) => {
- return <Tag key={index} closable visible onClose={() => {
- this.removeAnswer('object', index);
- }}>{row.map(r => r.text).join(', ')}</Tag>;
- })}
- </Form.Item></td>
- </tr>
- </table>
- </Block >;
- }
- renderOption() {
- const { getFieldDecorator } = this.props.form;
- return <Block flex>
- <h1>选项信息(长难句)</h1>
- <Form>
- <Form.Item>
- {getFieldDecorator('question.answer.options', {
- rules: [{ required: true, message: '请输入选项信息' }],
- })(
- <Checkbox.Group options={SentenceOption} />,
- )}
- </Form.Item>
- </Form>
- </Block>;
- }
- renderQX() {
- const { getFieldDecorator } = this.props.form;
- return <Block flex>
- <Form>
- <Form.Item label='千行解析'>
- {getFieldDecorator('question.qxContent', {
- })(
- <Editor placeholder='输入内容' onUpload={(file) => System.uploadImage(file)} />,
- )}
- </Form.Item>
- </Form>
- </Block>;
- }
- renderChinese() {
- const { getFieldDecorator } = this.props.form;
- return <Block flex>
- <Form>
- <Form.Item label='中文解析'>
- {getFieldDecorator('question.chineseContent', {
- })(
- <Editor placeholder='输入内容' onUpload={(file) => System.uploadImage(file)} />,
- )}
- </Form.Item>
- </Form>
- </Block>;
- }
- renderTab() {
- return <Tabs activeKey='sentence' onChange={(tab) => {
- switch (tab) {
- case 'textbook':
- linkTo('/subject/textbook/question');
- break;
- case 'base':
- linkTo('/subject/question');
- break;
- default:
- }
- }}>
- <Tabs.TabPane key='base' tab='考试题型' />
- <Tabs.TabPane key='sentence' tab='长难句' />
- <Tabs.TabPane key='textbook' tab='数学机经' />
- </Tabs>;
- }
- renderView() {
- return <div flex >
- {this.renderTab()}
- {this.renderTitle()}
- {this.renderBase()}
- {this.renderAnswer()}
- {this.renderOption()}
- {this.renderQX()}
- {this.renderChinese()}
- <Row type="flex" justify="center">
- <Col>
- <Button type="primary" onClick={() => {
- this.submit();
- }}>保存</Button>
- </Col>
- </Row>
- </div>;
- }
- }
|