Commit 0e1a2057 by yuananting

feat:云盘上传文件

parent 83229b62
/*
* @Author: 陈剑宇
* @Date: 2020-10-28 14:27:07
* @LastEditTime: 2021-02-28 14:22:13
* @LastEditTime: 2021-03-17 20:27:44
* @LastEditors: yuananting
* @Description:
* @FilePath: /xiaomai-web-b/app/common/constants/punchClock/punchClock.js
......@@ -113,6 +113,12 @@ export const FILE_ACCEPT = {
VOICE: 'audio/x-mpeg,audio/mp3,audio/mpeg,audio/wav,audio/x-m4a'
}
export const MEDIA_FILE_ACCEPT = {
PICTURE: 'JPG,JPEG,PNG,BMP,GIF',
VIDEO: 'MP4',
VOICE: 'MP3'
}
export const QUESTION_FILE_ACCEPT = {
PICTURE: 'image/jpg,image/jpeg,image/png,image/gif',
VIDEO: 'audio/mp4,video/mp4',
......
......@@ -2,12 +2,12 @@
* @Author: yuananting
* @Date: 2021-02-25 13:46:35
* @LastEditors: yuananting
* @LastEditTime: 2021-03-17 10:45:10
* @LastEditTime: 2021-03-18 09:32:02
* @Description: 助学工具-题库-题目管理-新增题目
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import { Tabs, Button, Tooltip, message } from "antd";
import { Tabs, Button, Tooltip, message, Modal } from "antd";
import Breadcrumbs from "@/components/Breadcrumbs";
import ShowTips from "@/components/ShowTips";
import "./AddNewQuestion.less";
......@@ -21,8 +21,16 @@ const { TabPane } = Tabs;
class AddNewQuestion extends Component {
constructor(props) {
super(props);
let activeKey = ""
if (getParameterByName("type")) {
activeKey = getParameterByName("type")
} else if (getParameterByName("key")) {
activeKey = getParameterByName("key")
} else {
activeKey = "SINGLE_CHOICE"
}
this.state = {
activeKey: getParameterByName("type") || "SINGLE_CHOICE",
activeKey: activeKey,
// 构建题目基本结构
singleChoiceContent: defineQuestionInfo("SINGLE_CHOICE"), // 单选题
multiChoiceContent: defineQuestionInfo("MULTI_CHOICE"), // 多选题
......@@ -69,7 +77,7 @@ class AddNewQuestion extends Component {
});
};
saveCurrentQuestion = (content) => {
saveCurrentQuestion = (content, type, next) => {
content.questionStemList.map((item, index) => {
item.sort = index;
return item;
......@@ -101,9 +109,16 @@ class AddNewQuestion extends Component {
QuestionBankService.editQuestion(params).then((res) => {
if (res.success) {
message.success("保存成功");
window.RCHistory.push({
pathname: `/question-bank-index?categoryId=${params.categoryId}`,
});
if (next === 'add') {
window.RCHistory.push({
pathname: `/create-new-question?categoryId=${params.categoryId}&key=${type}`,
});
}
if (next === 'close') {
window.RCHistory.push({
pathname: `/question-bank-index?categoryId=${params.categoryId}`,
});
}
}
});
} else {
......@@ -117,15 +132,36 @@ class AddNewQuestion extends Component {
QuestionBankService.addQuestion(params).then((res) => {
if (res.success) {
message.success("保存成功");
window.RCHistory.push({
pathname: `/question-bank-index?categoryId=${params.categoryId}`,
});
if (next === 'add') {
window.RCHistory.push({
pathname: `/create-new-question?categoryId=${params.categoryId}&key=${type}`,
});
}
if (next === 'close') {
window.RCHistory.push({
pathname: `/question-bank-index?categoryId=${params.categoryId}`,
});
}
}
});
}
};
confirmSaveQuestion = () => {
// 取消编辑并返回上一级路由
handleGoBack = () => {
Modal.confirm({
title: '确定要返回吗?',
content: '返回后,本次编辑的内容将不被保存',
okText: '确认返回',
cancelText: '留在本页',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
onOk: () => {
window.RCHistory.goBack();
}
})
}
confirmSaveQuestion = (next) => {
const {
singleChoiceContent,
multiChoiceContent,
......@@ -136,27 +172,27 @@ class AddNewQuestion extends Component {
switch (this.state.activeKey) {
case "SINGLE_CHOICE":
if (this.singleChoiceRef.checkInput() === 0) {
this.saveCurrentQuestion(singleChoiceContent);
this.saveCurrentQuestion(singleChoiceContent, "SINGLE_CHOICE", next);
}
break;
case "MULTI_CHOICE":
if (this.multiChoiceRef.checkInput() === 0) {
this.saveCurrentQuestion(multiChoiceContent);
this.saveCurrentQuestion(multiChoiceContent, "MULTI_CHOICE", next);
}
break;
case "JUDGE":
if (this.judgeRef.checkInput() === 0) {
this.saveCurrentQuestion(judgeContent);
this.saveCurrentQuestion(judgeContent, "JUDGE", next);
}
break;
case "GAP_FILLING":
if (this.gapRef.checkInput() === 0) {
this.saveCurrentQuestion(gapFillingContent);
}
// if (this.gapRef.checkInput() === 0) {
this.saveCurrentQuestion(gapFillingContent, "GAP_FILLING", next);
// }
break;
case "INDEFINITE_CHOICE":
if (this.indefiniteRef.checkInput() === 0) {
this.saveCurrentQuestion(indefiniteChoiceContent);
this.saveCurrentQuestion(indefiniteChoiceContent, "INDEFINITE_CHOICE", next);
}
break;
}
......@@ -180,7 +216,7 @@ class AddNewQuestion extends Component {
<div className="page add-new-question">
<Breadcrumbs
navList={getParameterByName("id") ? "编辑题目" : "新增题目"}
goBack={() => RCHistory.goBack()}
goBack={() => this.handleGoBack()}
/>
<div className="box">
<div className="show-tips">
......@@ -198,12 +234,13 @@ class AddNewQuestion extends Component {
key="SINGLE_CHOICE"
>
<NewQuestionTab
questionTypeKey="SINGLE_CHOICE"
questionTypeKey={activeKey}
onRef={(ref) => {
this.singleChoiceRef = ref;
}}
questionInfo={singleChoiceContent}
onSetState={(newContent) => {
console.log("newContent:", newContent)
Object.assign(singleChoiceContent, newContent);
}}
onLogger={this.handleLogger}
......@@ -214,7 +251,7 @@ class AddNewQuestion extends Component {
key="MULTI_CHOICE"
>
<NewQuestionTab
questionTypeKey="MULTI_CHOICE"
questionTypeKey={activeKey}
onRef={(ref) => {
this.multiChoiceRef = ref;
}}
......@@ -230,7 +267,7 @@ class AddNewQuestion extends Component {
key="JUDGE"
>
<NewQuestionTab
questionTypeKey="JUDGE"
questionTypeKey={activeKey}
onRef={(ref) => {
this.judgeRef = ref;
}}
......@@ -245,14 +282,14 @@ class AddNewQuestion extends Component {
key="GAP_FILLING"
>
<NewQuestionTab
questionTypeKey="GAP_FILLING"
questionTypeKey={activeKey}
onRef={(ref) => {
this.gapRef = ref;
}}
questionInfo={gapFillingContent}
onSetState={(newContent) => {
Object.assign(gapFillingContent, newContent);
console.log("gapFillingContent:", gapFillingContent);
console.log("gapFillingContent:", newContent);
}}
/>
</TabPane>
......@@ -268,7 +305,7 @@ class AddNewQuestion extends Component {
key="INDEFINITE_CHOICE"
>
<NewQuestionTab
questionTypeKey="INDEFINITE_CHOICE"
questionTypeKey={activeKey}
onRef={(ref) => {
this.indefiniteRef = ref;
}}
......@@ -282,12 +319,14 @@ class AddNewQuestion extends Component {
</Tabs>
</div>
<div className="footer">
<Button>取消</Button>
<Button>保存并继续添加</Button>
<Button onClick={() => { this.handleGoBack() }}>取消</Button>
<Button onClick={() => {
this.confirmSaveQuestion("add");
}}>保存并继续添加</Button>
<Button
type="primary"
onClick={() => {
this.confirmSaveQuestion();
this.confirmSaveQuestion("close");
}}
>
保存
......
......@@ -2,11 +2,11 @@
* @Author: yuananting
* @Date: 2021-02-25 13:52:01
* @LastEditors: yuananting
* @LastEditTime: 2021-03-08 19:07:56
* @LastEditTime: 2021-03-18 09:32:11
* @Description: 助学工具-题库-题目管理-新增题目样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.add-new-question {
.add-new-question {
position: relative !important;
.box {
margin-bottom: 66px !important;
......
......@@ -33,7 +33,7 @@ class QuestionBankIndex extends Component {
}
};
updatedSiderTreeFromList = (updatedCategoryId) => {
updatedSiderTreeFromList = (updatedCategoryId) => {
this.setState({updatedCategoryId});
};
......
......@@ -2,11 +2,11 @@
* @Author: yuananting
* @Date: 2021-02-21 18:27:43
* @LastEditors: yuananting
* @LastEditTime: 2021-03-11 19:10:44
* @LastEditTime: 2021-03-18 09:32:24
* @Description: 助学工具-题库-题库主页面样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.question-bank-index {
.question-bank-index {
.content-body {
display: flex;
.site-layout-background {
......
......@@ -2,11 +2,11 @@
* @Author: yuananting
* @Date: 2021-02-23 19:41:42
* @LastEditors: yuananting
* @LastEditTime: 2021-03-05 09:34:00
* @LastEditTime: 2021-03-18 09:32:37
* @Description: 助学工具-题库-题目分类管理样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.question-category-manage {
.question-category-manage {
position: relative;
.search-condition {
width: 30%;
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-02-25 14:34:29
* @LastEditors: yuananting
* @LastEditTime: 2021-03-16 17:23:02
* @LastEditTime: 2021-03-18 09:33:34
* @Description: 助学工具-题库-题目管理-新建题目Tab
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -11,17 +11,17 @@ import { Form, Radio, message, Checkbox, Tag, Tooltip, Input } from "antd";
import "./NewQuestionTab.less";
import Upload from "@/core/upload";
import QuestionEditor from "./QuestionEditor";
import GapFillingStem from "./GapFillingStem";
import { PlusOutlined, CloseOutlined } from "@ant-design/icons";
import {
NUM_TO_WORD_MAP,
QUESTION_FILE_ACCEPT,
MEDIA_FILE_ACCEPT,
} from "@/common/constants/punchClock/punchClock";
import { defineOptionInfo } from "./model";
import { defineOptionInfo, defineJudgeOptionInfo } from "./model";
import UploadingProgress from "./UploadingProgress";
import XMAudio from "./XMAudio";
import XMRecord from "./XMRecord";
import ScanFileModal from "@/modules/resource-disk/modal/ScanFileModal";
import SelectPrepareFileModal from "@/modules/prepare-lesson/modal/SelectPrepareFileModal";
import _ from "lodash";
import UploadOss from "@/core/upload";
......@@ -41,13 +41,17 @@ class NewQuestionTab extends Component {
gapFillingAnswer: JSON.parse(JSON.stringify(gapFillingAnswerList)), // 填空题-选项列表
chooseOptions: JSON.parse(JSON.stringify(optionList)), // 单选多选不定项判断-选项列表
questionAnswerDesc: JSON.parse(JSON.stringify(questionAnswerDescList)), // 答案解析
accept: QUESTION_FILE_ACCEPT["PICTURE"], // 上传媒体类型
accept: MEDIA_FILE_ACCEPT["PICTURE"], // 上传媒体类型
fileType: "PICTURE", // 媒体枚举
showRecord: false, // 录音弹窗
showBox: showBox,
gapFillingOptions: [],
blanksList: [], // 填空列表
showSelectFileModal: false,
mediaType: "",
diskList: [], // 资料云盘文件列表
};
this.uploadInput = React.createRef();
this.markKey = window.random_string(16);
}
......@@ -67,6 +71,9 @@ class NewQuestionTab extends Component {
this.setState({ [`optionsText_${i}`]: "" });
}
}
} else if (this.props.questionTypeKey === "JUDGE") {
this.initJudgeOption("正确");
this.initJudgeOption("错误");
}
this.props.onRef(this);
}
......@@ -92,9 +99,21 @@ class NewQuestionTab extends Component {
shouldComponentUpdate(nextProps, nextState) {
const { questionInfo } = nextProps;
if (this.props.questionInfo !== questionInfo) {
this.setState({
stemContent: JSON.parse(JSON.stringify(questionInfo.questionStemList)),
}); // 题干内容
this.setState(
{
stemContent: JSON.parse(
JSON.stringify(questionInfo.questionStemList)
),
},
() => {
const editorHtml = this.transferStemDocument(
questionInfo.questionStemList[0].content
);
const _blanksList = editorHtml.getElementsByClassName("fill-line");
// this.setState({blanksList:_blanksList})
console.log("转:", editorHtml.getElementsByClassName("fill-line"));
}
); // 题干内容
this.setState({
chooseOptions: JSON.parse(JSON.stringify(questionInfo.optionList)),
}); // 单选多选不定项-选项列表
......@@ -123,20 +142,15 @@ class NewQuestionTab extends Component {
});
};
dataURItoBlob = (dataURI) => {
var mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0]; // mime类型
var byteString = atob(dataURI.split(",")[1]); //base64 解码
var arrayBuffer = new ArrayBuffer(byteString.length); //创建缓冲数组
var intArray = new Uint8Array(arrayBuffer); //创建视图
for (var i = 0; i < byteString.length; i++) {
intArray[i] = byteString.charCodeAt(i);
}
return new Blob([intArray], { type: mimeString });
transferStemDocument = (txt) => {
const template = `<div class='stem'>${txt}</div>`;
let doc = new DOMParser().parseFromString(template, "text/html");
let div = doc.querySelector(".stem");
return div;
};
// 保存校验
checkInput = async () => {
checkInput = () => {
let validateError = 0;
// 题干校验
let stemContent = _.find(
......@@ -144,23 +158,8 @@ class NewQuestionTab extends Component {
(contentItem) => contentItem.type === "RICH_TEXT"
);
const stem = stemContent.content.replace(/<[^>]+>/g, "");
if (this.props.questionTypeKey === "GAP_FILLING") {
// stemContent.content.replace(
// /<img [^>]*src=['"]([^'"]+)[^>]*>/gi,
// (match, capture) => {
// let blob = this.dataURItoBlob(capture);
// const imageUrl = await UploadOss.uploadBlobToOSS(
// blob,
// window.random_string(16) + ".png"
// );
// newImgHtml = `<img src=${imageUrl} alt="" />`;
// console.log("newImgHtml:", newImgHtml);
// }
// );
// console.log("修改:", stemContent);
// this._onSetState();
if (this.props.questionTypeKey === "GAP_FILLING") {
if (this.state.blanksList.length === 0) {
this.setState({ stemValidate: "error" });
this.setState({
......@@ -275,6 +274,17 @@ class NewQuestionTab extends Component {
};
/**
* 初始化判断选项
*
* @memberof QuestionInputItem
*/
initJudgeOption = (content) => {
const { chooseOptions } = this.state;
chooseOptions.push(defineJudgeOptionInfo(content));
this._onSetState();
};
/**
* 删除选项
*
* @memberof QuestionInputItem
......@@ -309,43 +319,67 @@ class NewQuestionTab extends Component {
*/
handleChangeMedia = (key, uploadItemTarget, contentType) => {
this.setState({ contentType });
const { mediaFirstType } = this.state;
const mediaArr = _.filter(uploadItemTarget, (mediaItem) => {
return mediaItem.type !== "RICH_TEXT";
const pictureMediaArr = _.filter(uploadItemTarget, (mediaItem) => {
return mediaItem.type === "PICTURE";
});
const voiceMediaArr = _.filter(uploadItemTarget, (mediaItem) => {
return mediaItem.type === "VOICE";
});
const recordMediaArr = _.filter(uploadItemTarget, (mediaItem) => {
return mediaItem.type === "RECORD";
});
const videodMediaArr = _.filter(uploadItemTarget, (mediaItem) => {
return mediaItem.type === "VIDEO";
});
if (mediaArr.length === 0) {
this.setState({ mediaFirstType: key });
}
switch (contentType) {
case "QUESTION_STEM":
case "QUESTION_OPTION":
if (mediaArr.length > 0 && key !== mediaFirstType) {
var existType = [];
if (pictureMediaArr.length > 0) {
existType.push("PICTURE");
}
if (voiceMediaArr.length > 0) {
existType.push("VOICE");
}
if (recordMediaArr.length > 0) {
existType.push("RECORD");
}
if (videodMediaArr.length > 0) {
existType.push("VIDEO");
}
if (existType.length > 0 && !existType.includes(key)) {
return message.warning("只能添加1种类型的多媒体文件");
} else {
if (key === "PICTURE" && pictureMediaArr.length > 8) {
return message.warning("只能添加9张图片");
}
if (key === "VOICE" && voiceMediaArr.length > 2) {
return message.warning("只能添加3个音频");
}
if (key === "VIDEO" && videodMediaArr.length > 2) {
return message.warning("只能添加3个视频");
}
}
if (mediaFirstType === "PICTURE" && mediaArr.length > 8) {
return message.warning("只能添加9张图片");
break;
case "QUESTION_OPTION":
var existType = [];
if (pictureMediaArr.length > 0) {
existType.push("PICTURE");
}
if (mediaFirstType === "VOICE" && mediaArr.length > 2) {
return message.warning("只能添加3个音频");
if (voiceMediaArr.length > 0) {
existType.push("VOICE");
}
if (recordMediaArr.length > 0) {
existType.push("RECORD");
}
if (videodMediaArr.length > 0) {
existType.push("VIDEO");
}
if (mediaFirstType === "RECORD" && mediaArr.length > 2) {
return message.warning("只能添加3个录音");
if (existType.length > 0) {
return message.warning("只能添加1个多媒体文件");
}
break;
case "QUESTION_ANSWER_DESC":
var existType = [];
const pictureMediaArr = _.filter(uploadItemTarget, (mediaItem) => {
return mediaItem.type === "PICTURE";
});
const voiceMediaArr = _.filter(uploadItemTarget, (mediaItem) => {
return mediaItem.type === "VOICE";
});
const recordMediaArr = _.filter(uploadItemTarget, (mediaItem) => {
return mediaItem.type === "RECORD";
});
const videodMediaArr = _.filter(uploadItemTarget, (mediaItem) => {
return mediaItem.type === "VIDEO";
});
if (pictureMediaArr.length > 0) {
existType.push("PICTURE");
}
......@@ -375,8 +409,6 @@ class NewQuestionTab extends Component {
}
}
break;
default:
break;
}
this.setState(
......@@ -384,15 +416,16 @@ class NewQuestionTab extends Component {
uploadItemTarget,
},
() => {
QUESTION_FILE_ACCEPT[key] &&
MEDIA_FILE_ACCEPT[key] &&
this.setState(
{
accept: QUESTION_FILE_ACCEPT[key],
accept: MEDIA_FILE_ACCEPT[key],
fileType: key,
},
() => {
this.uploadInput.current.value = "";
this.uploadInput.current.click();
this.setState({ showSelectFileModal: key !== "RECORD" });
// this.uploadInput.current.click();
}
);
......@@ -406,60 +439,65 @@ class NewQuestionTab extends Component {
);
};
async uploadFile(event) {
async uploadFile(mediaFile) {
const { fileType, uploadItemTarget, contentType } = this.state;
const mediaFile = event.target.files[0];
if (!mediaFile) return;
if (fileType === "VOICE") {
if (!QUESTION_FILE_ACCEPT.VOICE.split(",").includes(mediaFile.type)) {
if (
!MEDIA_FILE_ACCEPT.VOICE.split(",").includes(mediaFile.folderFormat)
) {
message.warning("文件格式不正确");
return;
}
if (mediaFile.size > 30 * 1024 * 1024) {
if (mediaFile.size > 20 * 1024 * 1024) {
Modal.warning({
title: "音频过大",
content: "音频大小超过30M,请压缩后上传",
content: "音频大小超过20M,请压缩后上传",
});
return;
}
}
if (fileType === "PICTURE") {
if (!QUESTION_FILE_ACCEPT.PICTURE.split(",").includes(mediaFile.type)) {
if (
!MEDIA_FILE_ACCEPT.PICTURE.split(",").includes(mediaFile.folderFormat)
) {
message.warning("文件格式不正确");
return;
}
if (mediaFile.size > 8 * 1024 * 1024) {
if (mediaFile.folderSize > 1 * 1024 * 1024) {
Modal.warning({
title: "图片过大",
content: "图片大小超过8M,请压缩后上传",
content: "图片大小超过1M,请压缩后上传",
});
return;
}
}
if (fileType === "VIDEO") {
if (!QUESTION_FILE_ACCEPT.VIDEO.split(",").includes(mediaFile.type)) {
if (
!MEDIA_FILE_ACCEPT.VIDEO.split(",").includes(mediaFile.folderFormat)
) {
message.warning("文件格式不正确");
return;
}
if (mediaFile.size > 1024 * 1024 * 1024) {
if (mediaFile.folderSize > 500 * 1024 * 1024) {
Modal.warning({
title: "视频过大",
content: "视频大小超过1G,请压缩后上传",
content: "视频大小超过500G,请压缩后上传",
});
return;
}
}
const originArr = mediaFile.name.split(".");
const originArr = mediaFile.folderName.split(".");
const originType = originArr[originArr.length - 1];
const uploadObj = {
contentType,
type: fileType,
contentName: `${window.random_string(16)}.${originType}`, // 文件名
fileType: originType, // 文件后缀
mediaFile, // url
fileType: mediaFile.folderFormat, // 文件后缀
content: mediaFile.ossUrl, // url
};
if (["VIDEO", "VOICE"].includes(fileType)) {
try {
......@@ -476,17 +514,18 @@ class NewQuestionTab extends Component {
console.log(error);
}
} else if (fileType === "PICTURE") {
uploadObj.size = mediaFile.size;
uploadObj.size = mediaFile.folderSize;
}
uploadItemTarget.push(uploadObj);
this.setState({}, () => {
this.handleReupload(uploadObj);
this._onSetState();
this.uploadInput.current.value = "";
});
}
changeBlankCount = (data) => {
data.map((item) => {
console.log("data:", data);
data.forEach((item) => {
if (!item.answerTagList) {
item.answerTagList = [];
}
......@@ -552,7 +591,7 @@ class NewQuestionTab extends Component {
return (
<div className="gap-answer-box" key={optionIndex}>
<span className="gap-answer-label">
{optionItem.innerHTML}
填空
{optionIndex + 1}.
</span>
<div className="gap-answer-content">
......@@ -618,10 +657,8 @@ class NewQuestionTab extends Component {
renderJudgeOption = (judgeOptions) => {
return (
<React.Fragment>
<div>{judgeOptions[0].content}</div>
</React.Fragment>
);
<div dangerouslySetInnerHTML={{ __html: judgeOptions[0].content }}/>
)
};
/**
* 渲染输入内容
......@@ -656,42 +693,28 @@ class NewQuestionTab extends Component {
return (
<React.Fragment>
<div>
{isGapFilling && contentType === "QUESTION_STEM" ? (
<GapFillingStem
placehold={placehold}
validateStatus={validateStatus}
detailInfo={contentList}
mediaBtn={mediaBtn}
changeBlankCount={this.changeBlankCount.bind(this)}
onChange={(html) => {
editorContent.content = html;
this._onSetState();
}}
onUploadMedia={(key) => {
this.handleChangeMedia(key, contentList, contentType);
}}
/>
) : (
<QuestionEditor
markKey={this.markKey}
placehold={placehold}
validateStatus={validateStatus}
detailInfo={editorContent}
mediaBtn={mediaBtn}
bindChangeContent={(cb, textElemId) => {
this.setState({ textElemId });
editorContent.handleChangeContent = cb;
}}
onChange={(content, textLength) => {
editorContent.content = content;
editorContent.textLength = textLength;
this._onSetState();
}}
onUploadMedia={(key) => {
this.handleChangeMedia(key, contentList, contentType);
}}
/>
)}
<QuestionEditor
markKey={this.markKey}
placehold={placehold}
validateStatus={validateStatus}
detailInfo={editorContent}
isGapFilling={isGapFilling}
contentType={contentType}
mediaBtn={mediaBtn}
changeBlankCount={this.changeBlankCount.bind(this)}
bindChangeContent={(cb, textElemId) => {
this.setState({ textElemId });
editorContent.handleChangeContent = cb;
}}
onChange={(content, textLength) => {
editorContent.content = content;
editorContent.textLength = textLength;
this._onSetState();
}}
onUploadMedia={(key) => {
this.handleChangeMedia(key, contentList, contentType);
}}
/>
</div>
{contentType === "QUESTION_ANSWER_DESC" ? (
<div className="question-desc-box">
......@@ -1008,6 +1031,7 @@ class NewQuestionTab extends Component {
* @memberof QuestionInputItem
*/
handleReupload = (uploadItem) => {
console.log("uploadItem:", uploadItem);
uploadItem.status = "init";
Upload.uploadToOSSEvent(
uploadItem.mediaFile,
......@@ -1056,10 +1080,15 @@ class NewQuestionTab extends Component {
* @memberof QuestionInputItem
*/
handleFinishRecord = (mp3URL, duration) => {
const originArr = mp3URL.split(".");
const originType = originArr[originArr.length - 1];
const { uploadItemTarget, contentType } = this.state;
uploadItemTarget.push({
contentType,
type: "RECORD",
contentName: `${window.random_string(16)}.${originType}`, // 文件名
fileType: originType, // 文件后缀
content: mp3URL,
size: duration,
});
......@@ -1075,6 +1104,13 @@ class NewQuestionTab extends Component {
this.setState({ showRecord: false });
};
handleSelectMedia = (file) => {
this.uploadFile(file);
this.setState({
showSelectFileModal: false,
});
};
render() {
const {
stemContent,
......@@ -1086,9 +1122,14 @@ class NewQuestionTab extends Component {
scanFileType,
scanFileAddress,
blanksList,
showSelectFileModal,
mediaType,
diskList,
} = this.state;
const { stemValidate, stemText, radioValidate, radioText } = this.state;
const isJudge = this.props.questionTypeKey === "JUDGE";
console.log("this.props.questionTypeKey:", this.props.questionTypeKey)
console.log("判断:", isJudge)
const isGapFilling = this.props.questionTypeKey === "GAP_FILLING";
const placehold = isGapFilling ? (
<span>
......@@ -1101,6 +1142,23 @@ class NewQuestionTab extends Component {
) : (
"必填(1000字以内,可粘贴小图)"
);
let acceptType = "";
let selectTypeList = [];
switch (mediaType) {
case "PICTURE":
acceptType = "image/png,image/jpg";
selectTypeList = ["JPG", "PNG"];
break;
case "VOICE":
acceptType = "audio/mp3";
selectTypeList = ["MP3"];
break;
case "VIDEO":
acceptType = "video/mp4";
selectTypeList = ["MP4"];
break;
}
return (
<div className="question-input-item_wrapper">
{/* 题干 */}
......@@ -1170,6 +1228,7 @@ class NewQuestionTab extends Component {
questionOptionContentList,
isCorrectAnswer,
} = optionItem;
console.log("questionOptionContentList:", questionOptionContentList)
optionItem.optionSort = optionIndex;
const mediaBtn = ["VOICE", "RECORD", "PICTURE"];
const placeHold =
......@@ -1192,30 +1251,30 @@ class NewQuestionTab extends Component {
{["SINGLE_CHOICE", "JUDGE"].includes(
this.props.questionTypeKey
) && (
<Radio
checked={isCorrectAnswer}
onClick={() => {
_.each(chooseOptions, (o) => {
o.isCorrectAnswer = 0;
});
optionItem.isCorrectAnswer = 1;
this._onSetState();
}}
/>
)}
<Radio
checked={isCorrectAnswer}
onClick={() => {
_.each(chooseOptions, (o) => {
o.isCorrectAnswer = 0;
});
optionItem.isCorrectAnswer = 1;
this._onSetState();
}}
/>
)}
{/* 多选 or 不定项 */}
{["INDEFINITE_CHOICE", "MULTI_CHOICE"].includes(
this.props.questionTypeKey
) && (
<Checkbox
checked={isCorrectAnswer === 1}
onChange={(e) => {
const checked = e.target.checked ? 1 : 0;
optionItem.isCorrectAnswer = checked;
this._onSetState();
}}
/>
)}
<Checkbox
checked={isCorrectAnswer === 1}
onChange={(e) => {
const checked = e.target.checked ? 1 : 0;
optionItem.isCorrectAnswer = checked;
this._onSetState();
}}
/>
)}
</Form.Item>
</div>
<div className="question-item_options__sort mr12">
......@@ -1231,12 +1290,12 @@ class NewQuestionTab extends Component {
{isJudge
? this.renderJudgeOption(questionOptionContentList)
: this.renderContent(
questionOptionContentList,
placeHold,
mediaBtn,
"QUESTION_OPTION",
this.state[`optionsValidate_${optionIndex}`]
)}
questionOptionContentList,
placeHold,
mediaBtn,
"QUESTION_OPTION",
this.state[`optionsValidate_${optionIndex}`]
)}
</Form.Item>
</div>
{[
......@@ -1244,41 +1303,41 @@ class NewQuestionTab extends Component {
"MULTI_CHOICE",
"SINGLE_CHOICE",
].includes(this.props.questionTypeKey) && (
<div className="question-item_options__extra">
<React.Fragment>
<span
className="option-operate_item__icon icon iconfont"
onClick={() => this.handleDelOption(optionIndex)}
>
&#xe81a;
</span>
{optionIndex > 0 && (
<div className="question-item_options__extra">
<React.Fragment>
<span
className="option-operate_item__icon icon iconfont"
onClick={() =>
this.handleMoveOption(optionIndex, -1)
}
onClick={() => this.handleDelOption(optionIndex)}
>
&#xe74a;
</span>
)}
{optionIndex < chooseOptions.length - 1 && (
<span
className="option-operate_item__icon icon iconfont"
style={{
transform: "rotate(180deg)",
display: "inline-block",
}}
onClick={() =>
this.handleMoveOption(optionIndex, 1)
}
>
&#xe74a;
</span>
)}
</React.Fragment>
</div>
)}
&#xe81a;
</span>
{optionIndex > 0 && (
<span
className="option-operate_item__icon icon iconfont"
onClick={() =>
this.handleMoveOption(optionIndex, -1)
}
>
&#xe74a;
</span>
)}
{optionIndex < chooseOptions.length - 1 && (
<span
className="option-operate_item__icon icon iconfont"
style={{
transform: "rotate(180deg)",
display: "inline-block",
}}
onClick={() =>
this.handleMoveOption(optionIndex, 1)
}
>
&#xe74a;
</span>
)}
</React.Fragment>
</div>
)}
</div>
);
})}
......@@ -1335,6 +1394,17 @@ class NewQuestionTab extends Component {
}}
/>
)}
<SelectPrepareFileModal
operateType="select"
accept={acceptType}
selectTypeList={selectTypeList}
isOpen={showSelectFileModal}
diskList={diskList}
onClose={() => {
this.setState({ showSelectFileModal: false });
}}
onSelect={this.handleSelectMedia}
/>
</div>
);
}
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-02-22 10:59:43
* @LastEditors: yuananting
* @LastEditTime: 2021-03-15 15:22:13
* @LastEditTime: 2021-03-18 09:33:50
* @Description: 助学工具-题库-题库主页面侧边栏
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......
......@@ -2,11 +2,11 @@
* @Author: yuananting
* @Date: 2021-02-22 12:02:34
* @LastEditors: yuananting
* @LastEditTime: 2021-03-11 20:29:44
* @LastEditTime: 2021-03-18 09:34:06
* @Description: 助学工具-题库-题库主页面侧边栏样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.question-bank-sider {
.question-bank-sider {
position: relative;
.sider-title {
height: 22px;
......
......@@ -49,9 +49,15 @@ class QuestionEditor extends Component {
this.renderEditor();
}
static getDerivedStateFromProps(nextProps, prevState) {
return {
detailInfo: nextProps.detailInfo
}
}
shouldComponentUpdate(nextProps, nextState) {
const { detailInfo } = nextProps;
if (this.props.detailInfo !== detailInfo) {
if (this.state.detailInfo !== detailInfo) {
this.setState({ detailInfo: nextProps.detailInfo }, () => {
this.renderEditor();
});
......@@ -127,7 +133,6 @@ class QuestionEditor extends Component {
};
editorRoot.customConfig.onchange = (html) => {
console.log("触发:+——+++++++", this.state.blanksList);
const { focusFlag } = this.state;
const textLength = editorRoot.txt.text().replace(/\&nbsp\;/gi, " ")
.length;
......@@ -174,27 +179,27 @@ class QuestionEditor extends Component {
editorRoot.create();
this.editorRoot = editorRoot;
if (detailInfo.content) {
const contentHtml = /^\<p/.test(detailInfo.content)
? detailInfo.content
: `<p>${detailInfo.content}</p>`;
editorRoot.txt.html(detailInfo.content);
const textLength = editorRoot.txt.text().replace(/\&nbsp\;/gi, " ")
.length;
const imgLength = contentHtml.match(/<img/g)
? contentHtml.match(/<img/g).length * 2
: 0;
const contentLength = imgLength + textLength;
this.setState(
{
contentLength,
visiblePlacehold: contentLength === 0 && !this.state.focusFlag,
},
() => {
onChange && onChange(contentHtml, this.state.contentLength);
}
);
}
// if (detailInfo && detailInfo.content) {
const contentHtml = /^\<p/.test(detailInfo.content)
? detailInfo.content
: `<p>${detailInfo.content}</p>`;
editorRoot.txt.html(detailInfo.content);
const textLength = editorRoot.txt.text().replace(/\&nbsp\;/gi, " ")
.length;
const imgLength = contentHtml.match(/<img/g)
? contentHtml.match(/<img/g).length * 2
: 0;
const contentLength = imgLength + textLength;
this.setState(
{
contentLength,
visiblePlacehold: contentLength === 0 && !this.state.focusFlag,
},
() => {
onChange && onChange(contentHtml, this.state.contentLength);
}
);
// }
bindChangeContent && bindChangeContent(this.handleChangeContent);
}
......@@ -207,6 +212,9 @@ class QuestionEditor extends Component {
var _blanksList = [];
_blanksList = document.getElementsByClassName("add-fill-line");
this.setState({ blanksList: _blanksList });
this.setState({
visiblePlacehold: false,
});
// this.props.changeBlankCount(_blanksList);
};
......@@ -288,9 +296,8 @@ class QuestionEditor extends Component {
</div>
)}
<div
className={`editor-limit-tip${
contentLength > limitLength ? " mt6" : ""
}`}
className={`editor-limit-tip${contentLength > limitLength ? " mt6" : ""
}`}
style={{ height: contentLength > limitLength ? 20 : 0 }}
>
最多只能输入1000字
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-02-25 11:23:47
* @LastEditors: yuananting
* @LastEditTime: 2021-03-17 12:43:53
* @LastEditTime: 2021-03-18 09:35:03
* @Description: 助学工具-题库-题目管理主页面列表数据
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -84,7 +84,7 @@ class QuestionManageContent extends Component {
};
}
componentDidMount() {}
componentDidMount() { }
shouldComponentUpdate(nextProps, nextState) {
const { selectedCategoryId } = nextProps;
......@@ -211,6 +211,8 @@ class QuestionManageContent extends Component {
// 表头设置
parseColumns = () => {
const isPermiss = ["CloudManager", "StoreManager"].includes(User.getUserRole());
console.log(isPermiss, User.getUserRole())
const columns = [
{
title: "题目",
......@@ -273,22 +275,22 @@ class QuestionManageContent extends Component {
>
预览
</div>
<span className="record-operate__item split"> | </span>
<div
{isPermiss && <span className="record-operate__item split"> | </span>}
{isPermiss && <div
className="record-operate__item"
onClick={() =>
this.toEditQuetion(record.id, record.questionTypeEnum)
}
>
编辑
</div>
<span className="record-operate__item split"> | </span>
<div
</div>}
{isPermiss && <span className="record-operate__item split"> | </span>}
{isPermiss && <div
className="record-operate__item"
onClick={() => this.delCategoryConfirm(record)}
>
删除
</div>
</div>}
</div>
);
},
......@@ -311,9 +313,7 @@ class QuestionManageContent extends Component {
<span
className="empty-list-tip"
onClick={() => {
window.RCHistory.push({
pathname: "/create-question-bank",
});
this.handleCreateQuestionBank()
}}
>
新建一个
......@@ -356,7 +356,7 @@ class QuestionManageContent extends Component {
const ImportQuestionModal = (
<BatchImportQuestionModal
close={() => {
this.setState({ ImportQuestionModal: null });
this.setState({ ImportQuestionModal: null }, () => { this.queryQuestionPageList(); this.props.updatedSiderTree(this.props.selectedCategoryId) });
}}
categoryId={categoryId}
/>
......@@ -430,7 +430,7 @@ class QuestionManageContent extends Component {
</div>
</Row>
</div>
{!["0", null].includes(categoryId) && (
{(["CloudManager", "StoreManager"].includes(User.getUserRole()) && !["0", null].includes(categoryId)) && (
<Space size="large">
<Button type="primary" onClick={this.handleCreateQuestionBank}>
新建题目
......
/*
* @Author: chenjianyu
* @Date: 2020-09-12 17:00:44
* @LastEditTime: 2021-03-15 13:00:10
* @LastEditTime: 2021-03-18 09:33:26
* @LastEditors: yuananting
* @Description: 答题模式模板
* @Copyright © 2020 杭州杰竞科技有限公司 版权所有
......@@ -54,7 +54,7 @@ export function defineQuestionInfo(questionType) {
type: "RICH_TEXT", // 内容项形式(0:富文本 1:文字 2:图片 3:语音 4:视频 5文件 6.课件)
}
], // 题干
optionList: [], // 非填空题选项
optionList: questionType === "JUDGE" ? [] : [], // 非填空题选项
gapFillingAnswerList: [
{
correctAnswerList: []
......@@ -84,6 +84,19 @@ export function defineOptionInfo() {
}
}
export function defineJudgeOptionInfo(content) {
return {
isCorrectAnswer: 0, // 是否为正确答案选项
questionOptionContentList: [ // 选项内容
{
contentType: "QUESTION_OPTION", // 内容类型(默认选项)
content, // 内容
type: "RICH_TEXT",
}
]
}
}
export function defineOptionData(content = '') {
return {
itemContentVOList: [{
......
......@@ -101,6 +101,7 @@ class BatchImportQuestionModal extends Component {
return (
<div>
<Modal
closable={status !== "uploading"}
className="import-score-modal"
title="导入题目信息"
visible={true}
......@@ -218,7 +219,11 @@ class BatchImportQuestionModal extends Component {
<Button
type="primary"
className="down-btn"
// onClick={}
onClick={() => {
this.setState({ status: "init" })
this.setState({ uploadFile: null })
this.setState({ showSelectFileModal: true })
}}
>
重新上传文件
</Button>
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-02-22 17:51:28
* @LastEditors: yuananting
* @LastEditTime: 2021-03-15 09:56:59
* @LastEditTime: 2021-03-18 09:32:59
* @Description: 助学工具-题库-题库新建或编辑题库分类模态框
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......
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