Commit 83229b62 by yuananting

feat:导入联调,填空待修改

parent 462c5b38
@font-face {
font-family: 'iconfont'; /* project id 2223403 */
src: url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.eot');
src: url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.eot?#iefix') format('embedded-opentype'),
url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.woff2') format('woff2'),
url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.woff') format('woff'),
url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.ttf') format('truetype'),
url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.svg#iconfont') format('svg');
src: url('//at.alicdn.com/t/font_2223403_v302rtv070a.eot');
src: url('//at.alicdn.com/t/font_2223403_v302rtv070a.eot?#iefix') format('embedded-opentype'),
url('//at.alicdn.com/t/font_2223403_v302rtv070a.woff2') format('woff2'),
url('//at.alicdn.com/t/font_2223403_v302rtv070a.woff') format('woff'),
url('//at.alicdn.com/t/font_2223403_v302rtv070a.ttf') format('truetype'),
url('//at.alicdn.com/t/font_2223403_v302rtv070a.svg#iconfont') format('svg');
}
.iconfont{
font-family:"iconfont" !important;
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-03-03 15:13:12
* @LastEditors: yuananting
* @LastEditTime: 2021-03-16 15:11:25
* @LastEditTime: 2021-03-17 11:40:41
* @Description: 助学工具接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -10,49 +10,49 @@
import Service from "@/common/js/service";
export function queryCategoryTree(params: object) {
return Service.Hades("anon/hades/category/queryCategoryTree", params);
return Service.Hades("public/hades/queryCategoryTree", params);
}
export function addCategory(params: object) {
return Service.Hades("anon/hades/category/addCategory", params);
return Service.Hades("public/hades/addCategory", params);
}
export function delCategory(params: object) {
return Service.Hades("anon/hades/category/delCategory", params);
return Service.Hades("public/hades/delCategory", params);
}
export function editCategory(params: object) {
return Service.Hades("anon/hades/category/editCategory", params);
return Service.Hades("public/hades/editCategory", params);
}
export function editCategoryTree(params: object) {
return Service.Hades("anon/hades/category/editCategoryTree", params);
return Service.Hades("public/hades/editCategoryTree", params);
}
export function queryQuestionCategoryTree(params: object) {
return Service.Hades("anon/hades/question/queryCategoryTree", params);
return Service.Hades("public/hades/queryQuestionCategoryTree", params);
}
export function queryQuestionPageList(params: object) {
return Service.Hades("anon/hades/question/queryQuestionPageList", params);
return Service.Hades("public/hades/queryQuestionPageList", params);
}
export function addQuestion(params: object) {
return Service.Hades("anon/hades/question/addQuestion", params);
return Service.Hades("public/hades/addQuestion", params);
}
export function deleteQuestion(params: object) {
return Service.Hades("anon/hades/question/deleteQuestion", params);
return Service.Hades("public/hades/deleteQuestion", params);
}
export function queryQuestionDetails(params: object) {
return Service.Hades("anon/hades/question/queryQuestionDetails", params);
return Service.Hades("public/hades/queryQuestionDetails", params);
}
export function editQuestion(params: object) {
return Service.Hades("anon/hades/question/editQuestion", params);
return Service.Hades("public/hades/editQuestion", params);
}
export function batchImport(params: object) {
return Service.Hades("anon/hades/question/batchImport", params);
return Service.Hades("public/hades/batchImport", params);
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-02-25 13:46:35
* @LastEditors: yuananting
* @LastEditTime: 2021-03-16 16:44:23
* @LastEditTime: 2021-03-17 10:45:10
* @Description: 助学工具-题库-题目管理-新增题目
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -151,7 +151,7 @@ class AddNewQuestion extends Component {
break;
case "GAP_FILLING":
if (this.gapRef.checkInput() === 0) {
// this.saveCurrentQuestion(gapFillingContent);
this.saveCurrentQuestion(gapFillingContent);
}
break;
case "INDEFINITE_CHOICE":
......@@ -251,8 +251,8 @@ class AddNewQuestion extends Component {
}}
questionInfo={gapFillingContent}
onSetState={(newContent) => {
console.log("gapFillingContent:", newContent);
Object.assign(gapFillingContent, newContent);
console.log("gapFillingContent:", gapFillingContent);
}}
/>
</TabPane>
......
import React, { Component } from "react";
import E from "wangeditor";
import { message } from "antd";
import { message, Button } from "antd";
import UploadOss from "@/core/upload";
import "./QuestionEditor.less";
const MEDIA_MAP = [
......@@ -37,7 +37,11 @@ class QuestionEditor extends Component {
isShowSingleInput: true,
contentLength: 0,
errorInput: false,
detailInfo: props.detailInfo || {}
isGapFilling: props.isGapFilling,
contentType: props.contentType,
detailInfo: props.detailInfo || {},
gapFillingAnswer: props.gapFillingAnswer || [],
blanksList: props.blanksList || [],
};
}
......@@ -48,9 +52,9 @@ class QuestionEditor extends Component {
shouldComponentUpdate(nextProps, nextState) {
const { detailInfo } = nextProps;
if (this.props.detailInfo !== detailInfo) {
this.setState({detailInfo: nextProps.detailInfo}, () => {
this.setState({ detailInfo: nextProps.detailInfo }, () => {
this.renderEditor();
})
});
}
return true;
}
......@@ -83,7 +87,7 @@ class QuestionEditor extends Component {
};
renderEditor() {
const { editorId, detailInfo} = this.state;
const { editorId, detailInfo } = this.state;
const { onChange, bindChangeContent } = this.props;
const editorRoot = new E(
`#editor${editorId}_tabbar`,
......@@ -114,15 +118,16 @@ class QuestionEditor extends Component {
str = str.replace(/<\/?[^>]*>/g, "");
str = str.replace(/[ | ]*\n/g, "\n");
str = str.replace(/\&nbsp\;/gi, " ");
str = str.replace(/[\r\n]/g,"");
str = str.replace(/[\r\n]/g, "");
if (str.length > 1000) {
str = str.substring(0, 1000);
message.error("内容过长,不能超过1000字");
}
return str
return str;
};
editorRoot.customConfig.onchange = (html) => {
console.log("触发:+——+++++++", this.state.blanksList);
const { focusFlag } = this.state;
const textLength = editorRoot.txt.text().replace(/\&nbsp\;/gi, " ")
.length;
......@@ -137,7 +142,9 @@ class QuestionEditor extends Component {
} else {
this.setState({ isShowSingleInput: true });
}
if (this.state.isGapFilling) {
this.props.changeBlankCount(this.state.blanksList);
}
this.setState(
{ contentLength, visiblePlacehold: contentLength === 0 && !focusFlag },
() => {
......@@ -191,6 +198,18 @@ class QuestionEditor extends Component {
bindChangeContent && bindChangeContent(this.handleChangeContent);
}
insertBlank = (blanks) => {
var blanks = `<input class="add-fill-line" disabled answerTagList="" id=${window.random_string(
16
)} value="填空"/>`;
this.editorRoot.cmd.do("insertHTML", blanks);
document.getSelection().collapseToEnd();
var _blanksList = [];
_blanksList = document.getElementsByClassName("add-fill-line");
this.setState({ blanksList: _blanksList });
// this.props.changeBlankCount(_blanksList);
};
render() {
const {
editorId,
......@@ -201,6 +220,8 @@ class QuestionEditor extends Component {
contentLength,
isShowSingleInput,
errorInput,
isGapFilling,
contentType,
} = this.state;
const {
placehold,
......@@ -229,13 +250,12 @@ class QuestionEditor extends Component {
});
}}
>
<div
className="editor-box"
id={`editor${editorId}_tabbar`}
style={{ display: "none" }}
></div>
<div
<div
className={
isShowSingleInput ? "editor-box-single " : "editor-box-multiple"
}
......@@ -255,6 +275,18 @@ class QuestionEditor extends Component {
/{limitLength}
</div>
</div>
{isGapFilling && contentType === "QUESTION_STEM" && (
<div className="editor-fill-info">
在需要填写答案的地方
<Button
type="link"
className="editor-fill-info_icon icon iconfont"
onClick={this.insertBlank}
>
&#xe7fd; 插入占位符
</Button>
</div>
)}
<div
className={`editor-limit-tip${
contentLength > limitLength ? " mt6" : ""
......
......@@ -2,7 +2,28 @@
position: relative;
z-index: 9;
background-color: #ffffff;
.add-fill-line {
padding: 0 10px;
border-bottom: 1px solid !important;
margin: 0 4px;
text-align: center;
border: none;
width: 54px;
}
.editor-fill-info {
height: 20px;
font-size: 14px;
line-height: 20px;
color: #999999;
margin-top: 8px;
.editor-fill-info_icon {
color: #5289fa;
font-size: 14px;
padding-left: 9px;
cursor: pointer;
}
}
.editor-box-single {
border-radius: 4px;
padding: 4px 0;
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-02-25 11:23:47
* @LastEditors: yuananting
* @LastEditTime: 2021-03-16 15:34:57
* @LastEditTime: 2021-03-17 12:43:53
* @Description: 助学工具-题库-题目管理主页面列表数据
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -218,7 +218,8 @@ class QuestionManageContent extends Component {
dataIndex: "questionStem",
render: (val, record) => {
var handleVal = val;
handleVal = handleVal.replace(/<(?!img).*?>/g, "");
handleVal = handleVal.replace(/<(?!img|input).*?>/g, "");
handleVal = handleVal.replace(/<\s?input[^>]*>/gi, "_、");
handleVal = handleVal.replace(/<\s?img[^>]*>/gi, "【图片】");
handleVal = handleVal.replace(/\&nbsp\;/gi, " ");
return (
......@@ -351,27 +352,17 @@ class QuestionManageContent extends Component {
};
batchImportQuestion = () => {
const ImportQuestionModal = <BatchImportQuestionModal
close={() => {
this.setState({ ImportQuestionModal: null });
}}
onEdit={(record) => {
this.handleEditRecord(record);
}}
showTip={() => {
Modal.confirm({
title: `提醒`,
content: '系统拼命处理中,为避免你等待时间过长,请前往“任务中心”查看导入结果',
onOk: () => {
RCHistory.push(`/download_center/import_record`);
},
okText: '前往任务中心',
cancelText: '关闭',
});
}}
/>;
const { categoryId } = this.state.query;
const ImportQuestionModal = (
<BatchImportQuestionModal
close={() => {
this.setState({ ImportQuestionModal: null });
}}
categoryId={categoryId}
/>
);
this.setState({ ImportQuestionModal });
}
};
render() {
const { dataSource = [], total, query } = this.state;
......
......@@ -6,52 +6,98 @@
*/
import React, { Component } from "react";
import { Modal, Button, Upload, message } from "antd";
import { Modal, Button, Upload, message, Spin, Progress } from "antd";
import "./BatchImportQuestionModal.less";
import SelectPrepareFileModal from "@/modules/prepare-lesson/modal/SelectPrepareFileModal";
import User from "@/common/js/user";
import QuestionBankService from "@/domains/question-bank-domain/QuestionBankService";
import { LoadingOutlined } from "@ant-design/icons";
class BatchImportQuestionModal extends Component {
constructor(props) {
super(props);
this.state = {
showSelectFileModal: false, // 云盘列表弹窗显隐
diskList: [], // 资料云盘文件列表
fileList: [], // 上传的文件列表
uploadBtnType: "add", // 上传按钮类型(上传文件 | 重新上传)
uploadBlob: null, // 上传的文件
uploadProps: {
accept: "application/vnd.ms-excel",
beforeUpload(file) {
this.setState({ uploadBtnType: "reAdd" });
this.setState({ uploadBlob: file });
return false;
},
onChange(info) {
let fileList = [...info.fileList];
fileList = fileList.slice(-1);
this.setState({ fileList });
},
onRemove() {
this.setState({ uploadBtnType: "add" });
},
},
uploadFile: null, // 上传的文件
uploadResult: {
elapsedTime: 0,
failCnt: 0,
resourceUrl: null,
successCnt: 0,
}, // 上传返回结果
message: null,
status: "init",
};
}
// 下载题目模板
handleDownTemplate = () => {
console.log("下载");
const a = document.createElement("a");
a.href = "https://image.xiaomaiketang.com/xm/question_template.xlsx";
a.click();
};
// 选择云盘资源
handleSelectExcel = (file) => {
console.log(file);
this.setState({ uploadFile: file });
this.setState({
showSelectFileModal: false,
});
};
// 导入
handleImport = async () => {
const { uploadFile } = this.state;
if (!uploadFile) {
message.warning("请选择要导入的文件");
} else {
let flag = false;
setTimeout(() => {
if (!flag) {
this.setState({ status: "uploading" });
}
}, 1000);
let params = {
categoryId: this.props.categoryId,
resourceId: this.state.uploadFile.resourceId,
source: 0,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
};
const res = await QuestionBankService.batchImport(params);
const { result } = res;
const { bizSuccess, bizMessage } = result;
if (res) {
flag = true;
this.setState({ status: bizSuccess ? "success" : "fail" });
this.setState({ uploadResult: result });
this.setState({ message: bizMessage });
}
}
};
// 下载错误报告
downErrorReport = async () => {
const { uploadResult } = this.state;
const { resourceUrl } = uploadResult;
const a = document.createElement("a");
a.href = resourceUrl;
a.click();
};
render() {
const {
diskList,
showSelectFileModal,
uploadBtnType,
uploadProps,
uploadFile,
status,
uploadResult,
message,
} = this.state;
const { failCnt, resourceUrl, successCnt } = uploadResult;
const loadingIcon = <LoadingOutlined style={{ fontSize: 76 }} spin />;
return (
<div>
<Modal
......@@ -60,44 +106,149 @@ class BatchImportQuestionModal extends Component {
visible={true}
width={560}
maskClosable={false}
footer={[
<Button onClick={this.props.close}>取消</Button>,
<Button type="primary" onClick={this.handleImport}>
导入
</Button>,
]}
footer={
status === "init" && [
<Button onClick={this.props.close}>取消</Button>,
<Button type="primary" onClick={this.handleImport}>
导入
</Button>,
]
}
onCancel={() => {
this.props.close();
}}
>
<div className="step-section">
<h4 className="step-title">1.下载导入模板,按要求填写信息</h4>
<div
className="down-btn"
style={{ fontSize: "14px" }}
onClick={this.handleDownTemplate}
>
下载题目导入模板
{status === "init" && (
<div>
<div className="step-section">
<h4 className="step-title">1.下载导入模板,按要求填写信息</h4>
<div
className="down-btn"
style={{ fontSize: "14px" }}
onClick={this.handleDownTemplate}
>
下载题目导入模板
</div>
</div>
<div className="step-section">
<h4 className="step-title">2.选择需要导入的Excel文件</h4>
<Button
type="primary"
className="add-btn"
onClick={() => this.setState({ showSelectFileModal: true })}
>
{uploadFile ? "重新添加" : "添加文件"}
</Button>
{uploadFile && (
<div className="file-box">
<div>
<img
className="link-img"
src="https://image.xiaomaiketang.com/xm/link.png"
/>
<span>{uploadFile.folderName}</span>
<img
className="del-img"
src="https://image.xiaomaiketang.com/xm/del.png"
onClick={() => this.setState({ uploadFile: null })}
/>
</div>
</div>
)}
</div>
</div>
)}
{status === "uploading" && (
<div className="import-status-box">
<div className="status-content">
<Spin indicator={loadingIcon} />
<p className="status">题目导入中...</p>
<p className="status-tip">请勿关闭页面和此弹窗</p>
<p className="status-tip">
<span>以防题目导入失败</span>
</p>
</div>
</div>
</div>
<div className="step-section">
<h4 className="step-title">2.选择需要导入的Excel文件</h4>
<Button type="primary" className="add-btn" onClick={() => this.setState({showSelectFileModal: true})}>
{uploadBtnType == "reAdd" ? "重新添加" : "添加文件"}
</Button>
</div>
)}
{status === "success" && (
<div className="import-status-box">
<div className="status-content">
<img
src="https://image.xiaomaiketang.com/xm/success.png"
alt=""
/>
<p className="status">导入完成</p>
<p className="status-tip">
<span style={{ color: successCnt === 0 ? "" : "#FF9D14" }}>
{successCnt}
</span>
个题目导入成功,
<span style={{ color: failCnt === 0 ? "" : "#FF9D14" }}>
{failCnt}
</span>
个题目导入失败。
</p>
{resourceUrl ? (
<Button
type="primary"
className="down-btn"
onClick={() => this.downErrorReport(resourceUrl)}
>
下载错误报告
</Button>
) : (
<Button
onClick={() => {
this.props.close();
}}
>
关闭
</Button>
)}
</div>
</div>
)}
{status === "fail" && (
<div className="import-status-box">
<div className="status-content">
<img src="https://image.xiaomaiketang.com/xm/fail.png" alt="" />
<p className="status">导入失败</p>
<p className="status-tip">{message}</p>
{message ? (
<Button
type="primary"
className="down-btn"
// onClick={}
>
重新上传文件
</Button>
) : (
<Button
onClick={() => {
this.props.close();
}}
>
关闭
</Button>
)}
</div>
</div>
)}
</Modal>
<SelectPrepareFileModal
operateType="select"
accept="image/jpeg,image/png,image/jpg"
selectTypeList={["JPG", "JPEG", "PNG"]}
tooltip="支持文件类型:jpg、jpeg、png"
accept="xls/xlsx"
selectTypeList={[
"vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"vnd.ms-excel",
]}
tooltip="支持文件类型:xls、xlsx"
isOpen={showSelectFileModal}
diskList={diskList}
onClose={() => {
this.setState({ showSelectFileModal: false });
}}
onSelect={this.handleSelectImg}
onSelect={this.handleSelectExcel}
/>
</div>
);
......
......@@ -29,6 +29,32 @@
font-weight: 400;
}
}
.file-box {
line-height: 20px;
color: #999999;
line-height: 20px;
margin-top: 10px;
span {
vertical-align: middle;
margin-right: 16px;
}
.link-img {
width: 14px;
vertical-align: middle;
margin-right: 4px;
}
.del-img {
width: 18px;
vertical-align: middle;
visibility: hidden;
}
}
.file-box :hover {
background-color: #FFF8E8;
.del-img {
visibility: visible !important;
}
}
.remark-input {
width: 304px;
margin-bottom: 8px;
......
......@@ -214,7 +214,11 @@
.question-preview-modal.ant-modal {
max-height: 60% !important;
}
.fill-line {
.add-fill-line {
padding: 0 10px;
border-bottom: 1px solid;
border-bottom: 1px solid !important;
margin: 0 4px;
text-align: center;
border: none;
width: 54px;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment