Commit 69411c9a by yuananting

feat:初步联调

parent fea42dc1
/*
* @Author: yuananting
* @Date: 2021-03-03 15:13:12
* @LastEditors: yuananting
* @LastEditTime: 2021-03-13 20:46:54
* @Description: 助学工具接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import Service from "@/common/js/service";
export function queryCategoryTree(params: object) {
return Service.Hades("anon/hades/category/queryCategoryTree", params);
}
export function addCategory(params: object) {
return Service.Hades("anon/hades/category/addCategory", params);
}
export function delCategory(params: object) {
return Service.Hades("anon/hades/category/delCategory", params);
}
export function editCategory(params: object) {
return Service.Hades("anon/hades/category/editCategory", params);
}
export function editCategoryTree(params: object) {
return Service.Hades("anon/hades/category/editCategoryTree", params);
}
export function queryQuestionCategoryTree(params: object) {
return Service.Hades("anon/hades/question/queryCategoryTree", params);
}
export function queryQuestionPageList(params: object) {
return Service.Hades("anon/hades/question/queryQuestionPageList", params);
}
export function addQuestion(params: object) {
return Service.Hades("anon/hades/question/addQuestion", params);
}
export function deleteQuestion(params: object) {
return Service.Hades("anon/hades/question/deleteQuestion", params);
}
\ No newline at end of file
/*
* @Author: yuananting
* @Date: 2021-03-03 15:13:12
* @LastEditors: yuananting
* @LastEditTime: 2021-03-03 15:18:30
* @Description: 助学工具接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import Service from "@/common/js/service";
export function queryCategoryTree(params: object) {
return Service.Hades("/private/lesson/queryCategoryTree", params);
}
/* /*
* @Author: 陈剑宇 * @Author: 陈剑宇
* @Date: 2020-05-07 14:43:01 * @Date: 2020-05-07 14:43:01
* @LastEditTime: 2021-02-21 17:24:08 * @LastEditTime: 2021-03-11 11:43:59
* @LastEditors: yuananting * @LastEditors: yuananting
* @Description: * @Description:
* @FilePath: /wheat-web-demo/src/domains/basic-domain/constants.ts * @FilePath: /wheat-web-demo/src/domains/basic-domain/constants.ts
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
import { MapInterface } from '@/domains/basic-domain/interface' import { MapInterface } from '@/domains/basic-domain/interface'
// 默认是 dev 环境 // 默认是 dev 环境
const ENV: string = process.env.DEPLOY_ENV || 'dev'; const ENV: string = process.env.DEPLOY_ENV || 'dev1';
console.log("process.env.DEPLOY_ENV",process) console.log("process.env.DEPLOY_ENV",process)
const BASIC_HOST_MAP: MapInterface = { const BASIC_HOST_MAP: MapInterface = {
dev: 'https://dev-heimdall.xiaomai5.com/', dev: 'https://dev-heimdall.xiaomai5.com/',
......
/*
* @Author: yuananting
* @Date: 2021-03-11 11:34:37
* @LastEditors: yuananting
* @LastEditTime: 2021-03-13 20:46:28
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import { queryCategoryTree, addCategory, delCategory, editCategory, editCategoryTree, queryQuestionCategoryTree, addQuestion, queryQuestionPageList, deleteQuestion } from '@/data-source/questionBank/request-apis';
export default class QuestionBankService {
// 获取题目分类树
static queryCategoryTree(params: any) {
return queryCategoryTree(params);
}
// 新增题目分类
static addCategory(params: any) {
return addCategory(params);
}
// 删除分类
static delCategory(params: any) {
return delCategory(params);
}
// 编辑分类
static editCategory(params: any) {
return editCategory(params);
}
// 编辑分类树(拖拽)
static editCategoryTree(params: any) {
return editCategoryTree(params);
}
// 查询分类树列表
static queryQuestionCategoryTree(params: any) {
return queryQuestionCategoryTree(params);
}
// 查询题目列表
static queryQuestionPageList(params: any) {
return queryQuestionPageList(params);
}
// 添加题目
static addQuestion(params: any) {
return addQuestion(params);
}
// 删除题目
static deleteQuestion(params: any) {
return deleteQuestion(params);
}
}
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-25 13:46:35 * @Date: 2021-02-25 13:46:35
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-03-09 11:19:01 * @LastEditTime: 2021-03-13 20:12:14
* @Description: 助学工具-题库-题目管理-新增题目 * @Description: 助学工具-题库-题目管理-新增题目
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -12,40 +12,79 @@ import Breadcrumbs from "@/components/Breadcrumbs"; ...@@ -12,40 +12,79 @@ import Breadcrumbs from "@/components/Breadcrumbs";
import ShowTips from "@/components/ShowTips"; import ShowTips from "@/components/ShowTips";
import "./AddNewQuestion.less"; import "./AddNewQuestion.less";
import NewQuestionTab from "./components/NewQuestionTab"; import NewQuestionTab from "./components/NewQuestionTab";
import { defineQuestionData } from "./components/model"; import { defineQuestionInfo } from "./components/model";
import QuestionBankService from "@/domains/question-bank-domain/QuestionBankService";
import User from "@/common/js/user";
const { TabPane } = Tabs; const { TabPane } = Tabs;
class AddNewQuestion extends Component { class AddNewQuestion extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
activeKey: "THE_RADIO", activeKey: "SINGLE_CHOICE",
singleContent: defineQuestionData("THE_RADIO"), // 单选题内容 // 构建题目基本结构
multipleContent: defineQuestionData("MULTI_SELECT"), // 多选题内容 singleChoiceContent: defineQuestionInfo("SINGLE_CHOICE"), // 单选题
judgeContent: defineQuestionData("JUDGE"), // 多选题内容 multiChoiceContent: defineQuestionInfo("MULTI_CHOICE"), // 多选题
completionContent: defineQuestionData("COMPLETION"), // 填空题内容 judgeContent: defineQuestionInfo("JUDGE"), // 判断题
indefiniteContent: defineQuestionData("INDEFINITE_SELECT"), // 不定项选择题内容 gapFillingContent: defineQuestionInfo("GAP_FILLING"), // 填空题
indefiniteChoiceContent: defineQuestionInfo("INDEFINITE_CHOICE"), // 不定项选择题
}; };
} }
componentDidMount() {} componentDidMount() {}
saveCurrentQuestion = () => { saveCurrentQuestion = (content) => {
content.questionStemList.map((item, index)=> {
item.sort = index;
return item;
})
content.optionList.map((item)=> {
item.questionOptionContentList.map((childItem, childIndex) => {
childItem.sort = childIndex;
return childItem;
})
return item;
})
content.questionAnswerDescList.map((item, index)=> {
item.sort = index;
return item;
})
let params = {
...content,
categoryId: getParameterByName("categoryId"),
source: 0,
tenantId: User.getStoreId(),
userId: User.getUserId(),
};
QuestionBankService.addQuestion(params).then((res) => {
console.log(res);
});
};
confirmSaveQuestion = () => {
const {
singleChoiceContent,
multiChoiceContent,
judgeContent,
gapFillingContent,
indefiniteChoiceContent,
} = this.state;
switch (this.state.activeKey) { switch (this.state.activeKey) {
case "THE_RADIO": case "SINGLE_CHOICE":
console.log(this.singleRef) if (this.singleRef.checkInput() === 0) {
this.singleRef.checkInput(); this.saveCurrentQuestion(singleChoiceContent);
}
break; break;
case "MULTI_SELECT": case "MULTI_CHOICE":
this.multipleRef.checkInput(); this.multipleRef.checkInput();
break; break;
case "JUDGE": case "JUDGE":
this.judgeRef.checkInput(); this.judgeRef.checkInput();
break; break;
case "COMPLETION": case "GAP_FILLING":
this.CompletionRef.checkInput(); this.CompletionRef.checkInput();
break; break;
case "INDEFINITE_SELECT": case "INDEFINITE_CHOICE":
this.indefiniteRef.checkInput(); this.indefiniteRef.checkInput();
break; break;
} }
...@@ -56,9 +95,14 @@ class AddNewQuestion extends Component { ...@@ -56,9 +95,14 @@ class AddNewQuestion extends Component {
activeKey, activeKey,
singleContent, singleContent,
multipleContent, multipleContent,
judgeContent, judgeContent1,
completionContent, completionContent,
indefiniteContent, indefiniteContent,
singleChoiceContent,
multiChoiceContent,
judgeContent,
gapFillingContent,
indefiniteChoiceContent,
} = this.state; } = this.state;
return ( return (
<div className="page add-new-question"> <div className="page add-new-question">
...@@ -76,31 +120,32 @@ class AddNewQuestion extends Component { ...@@ -76,31 +120,32 @@ class AddNewQuestion extends Component {
> >
<TabPane <TabPane
tab={<span className="icon iconfont">&#xe7fa; 单选题</span>} tab={<span className="icon iconfont">&#xe7fa; 单选题</span>}
key="THE_RADIO" key="SINGLE_CHOICE"
> >
<NewQuestionTab <NewQuestionTab
questionTypeKey="THE_RADIO" questionTypeKey="SINGLE_CHOICE"
onRef={(ref) => { onRef={(ref) => {
this.singleRef = ref; this.singleRef = ref;
}} }}
questionContent={singleContent} questionInfo={singleChoiceContent}
onSetState={(newContent) => { onSetState={(newContent) => {
Object.assign(singleContent, newContent); console.log(newContent);
Object.assign(singleChoiceContent, newContent);
}} }}
/> />
</TabPane> </TabPane>
<TabPane <TabPane
tab={<span className="icon iconfont">&#xe7fb; 多选题</span>} tab={<span className="icon iconfont">&#xe7fb; 多选题</span>}
key="MULTI_SELECT" key="MULTI_CHOICE"
> >
<NewQuestionTab <NewQuestionTab
questionTypeKey="MULTI_SELECT" questionTypeKey="MULTI_CHOICE"
onRef={(ref) => { onRef={(ref) => {
this.multipleRef = ref; this.multipleRef = ref;
}} }}
questionContent={multipleContent} questionInfo={multiChoiceContent}
onSetState={(newContent) => { onSetState={(newContent) => {
Object.assign(multipleContent, newContent); Object.assign(multiChoiceContent, newContent);
}} }}
/> />
</TabPane> </TabPane>
...@@ -113,7 +158,7 @@ class AddNewQuestion extends Component { ...@@ -113,7 +158,7 @@ class AddNewQuestion extends Component {
onRef={(ref) => { onRef={(ref) => {
this.judgeRef = ref; this.judgeRef = ref;
}} }}
questionContent={judgeContent} questionInfo={judgeContent}
onSetState={(newContent) => { onSetState={(newContent) => {
Object.assign(judgeContent, newContent); Object.assign(judgeContent, newContent);
}} }}
...@@ -121,16 +166,16 @@ class AddNewQuestion extends Component { ...@@ -121,16 +166,16 @@ class AddNewQuestion extends Component {
</TabPane> </TabPane>
<TabPane <TabPane
tab={<span className="icon iconfont">&#xe7fd; 填空题</span>} tab={<span className="icon iconfont">&#xe7fd; 填空题</span>}
key="COMPLETION" key="GAP_FILLING"
> >
<NewQuestionTab <NewQuestionTab
questionTypeKey="COMPLETION" questionTypeKey="GAP_FILLING"
onRef={(ref) => { onRef={(ref) => {
this.CompletionRef = ref; this.CompletionRef = ref;
}} }}
questionContent={completionContent} questionInfo={gapFillingContent}
onSetState={(newContent) => { onSetState={(newContent) => {
Object.assign(completionContent, newContent); Object.assign(gapFillingContent, newContent);
}} }}
/> />
</TabPane> </TabPane>
...@@ -143,16 +188,16 @@ class AddNewQuestion extends Component { ...@@ -143,16 +188,16 @@ class AddNewQuestion extends Component {
</Tooltip> </Tooltip>
</span> </span>
} }
key="INDEFINITE_SELECT" key="INDEFINITE_CHOICE"
> >
<NewQuestionTab <NewQuestionTab
questionTypeKey="INDEFINITE_SELECT" questionTypeKey="INDEFINITE_CHOICE"
onRef={(ref) => { onRef={(ref) => {
this.indefiniteRef = ref; this.indefiniteRef = ref;
}} }}
questionContent={indefiniteContent} questionInfo={indefiniteChoiceContent}
onSetState={(newContent) => { onSetState={(newContent) => {
Object.assign(indefiniteContent, newContent); Object.assign(indefiniteChoiceContent, newContent);
}} }}
/> />
</TabPane> </TabPane>
...@@ -164,7 +209,7 @@ class AddNewQuestion extends Component { ...@@ -164,7 +209,7 @@ class AddNewQuestion extends Component {
<Button <Button
type="primary" type="primary"
onClick={() => { onClick={() => {
this.saveCurrentQuestion(); this.confirmSaveQuestion();
}} }}
> >
保存 保存
......
...@@ -2,27 +2,23 @@ ...@@ -2,27 +2,23 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-21 17:51:01 * @Date: 2021-02-21 17:51:01
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-03-05 13:41:32 * @LastEditTime: 2021-03-13 16:48:23
* @Description: 助学工具-题库-题库主页面 * @Description: 助学工具-题库-题库主页面
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React, { Component } from "react"; import React, { Component } from "react";
import { Layout } from "antd";
import "./QuestionBankIndex.less"; import "./QuestionBankIndex.less";
import QuestionBankSider from "./components/QuestionBankSider"; import QuestionBankSider from "./components/QuestionBankSider";
import QuestionManageFilter from "./components/QuestionManageFilter"; import QuestionManageList from "./components/QuestionManageContent";
import QuestionManageOpt from "./components/QuestionManageOpt"; import User from "@/common/js/user";
import QuestionManageList from "./components/QuestionManageList"; import QuestionBankService from "@/domains/question-bank-domain/QuestionBankService";
class QuestionBankIndex extends Component { class QuestionBankIndex extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
query: { selectedCategoryId: "",
current: 1,
size: 10,
},
loading: true, loading: true,
dataSource: [], // 题库列表数据 dataSource: [], // 题库列表数据
totalCount: 1, // 题库数据总条数 totalCount: 1, // 题库数据总条数
...@@ -32,46 +28,26 @@ class QuestionBankIndex extends Component { ...@@ -32,46 +28,26 @@ class QuestionBankIndex extends Component {
componentDidMount() { componentDidMount() {
// TODO // TODO
// 接口请求 初始化数据 // 接口请求 初始化数据
this.getQuestionBankList();
} }
getQuestionBankList = (_query = {}) => { getCategoryIdFromSider = (selectedCategoryId) => {
const query = { if (selectedCategoryId && selectedCategoryId.length > 0) {
...this.state.query, this.setState({ selectedCategoryId: selectedCategoryId[0] });
..._query, }
};
// 更新请求参数
this.setState({ query });
// CourseService.videoSchedulePage(query).then((res) => {
// const { result = {} } = res || {};
// const { records = [], total = 0 } = result;
// this.setState({
// dataSource: records,
// totalCount: Number(total)
// });
// });
}; };
render() { render() {
const { query, dataSource, totalCount } = this.state;
return ( return (
<div className="question-bank-index page"> <div className="question-bank-index page">
<div className="content-header">题目</div> <div className="content-header">题目</div>
<div className="box content-body"> <div className="box content-body">
<div className="sider"> <div className="sider">
<QuestionBankSider /> <QuestionBankSider
getSelectedCategoryId={this.getCategoryIdFromSider.bind(this)}
/>
</div> </div>
<div className="content"> <div className="content">
<QuestionManageFilter /> <QuestionManageList selectedCategoryId={this.state.selectedCategoryId} />
<QuestionManageOpt />
<QuestionManageList
query={query}
dataSource={dataSource}
totalCount={totalCount}
onChange={this.getQuestionList}
/>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-21 18:27:43 * @Date: 2021-02-21 18:27:43
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-03-02 17:26:30 * @LastEditTime: 2021-03-11 19:10:44
* @Description: 助学工具-题库-题库主页面样式 * @Description: 助学工具-题库-题库主页面样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
.content { .content {
width: 100%; width: 100%;
margin-left: 24px; margin-left: 24px;
height: calc(100vh - 160px);
} }
} }
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-23 18:28:50 * @Date: 2021-02-23 18:28:50
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-03-05 10:19:34 * @LastEditTime: 2021-03-13 19:53:48
* @Description: 助学工具-题库-主页面分类管理 * @Description: 助学工具-题库-主页面分类管理
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -10,153 +10,88 @@ import React, { Component } from "react"; ...@@ -10,153 +10,88 @@ import React, { Component } from "react";
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from "@/components/Breadcrumbs";
import "./QuestionCategoryManage.less"; import "./QuestionCategoryManage.less";
import NewEditQuestionBankCategory from "./modal/NewEditQuestionBankCategory"; import NewEditQuestionBankCategory from "./modal/NewEditQuestionBankCategory";
import { Tree, Input, Space, Button, Menu, Dropdown, message } from "antd"; import QuestionBankService from "@/domains/question-bank-domain/QuestionBankService";
import User from "@/common/js/user";
import {
Tree,
Input,
Space,
Button,
Menu,
Dropdown,
message,
Modal,
} from "antd";
import ShowTips from "@/components/ShowTips"; import ShowTips from "@/components/ShowTips";
const { DirectoryTree, Popconfirm } = Tree; import user from "@/common/js/user";
const { DirectoryTree } = Tree;
const { Search } = Input; const { Search } = Input;
const { confirm } = Modal;
class QuestionCategoryManage extends Component { class QuestionCategoryManage extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
NewEditQuestionBankCategory: null, //新增或编辑分类模态框 NewEditQuestionBankCategory: null, //新增或编辑分类模态框
treeData: [ treeData: [],
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367023655839768500",
parentId: "0",
rootId: "1367023655839768500",
categoryLevel: 0,
categoryName: "未分类",
sort: 0,
},
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367023655839768577",
parentId: "0",
rootId: "1367023655839768577",
categoryLevel: 0,
categoryName: "分类1",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367023709942095873",
parentId: "1367023655839768577",
rootId: "1367023655839768577",
categoryLevel: 1,
categoryName: "test",
sort: 0,
},
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367023725184196609",
parentId: "1367023655839768577",
rootId: "1367023655839768577",
categoryLevel: 1,
categoryName: "分类2",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367023805563838466",
parentId: "1367023725184196609",
rootId: "1367023655839768577",
categoryLevel: 2,
categoryName: "test",
sort: 0,
},
],
sort: 1,
},
],
sort: 1,
},
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367049864988516353",
parentId: "0",
rootId: "1367049864988516353",
categoryLevel: 0,
categoryName: "分类2",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367049931656978433",
parentId: "1367049864988516353",
rootId: "1367049864988516353",
categoryLevel: 1,
categoryName: "分类2",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367049973746819074",
parentId: "1367049931656978433",
rootId: "1367049864988516353",
categoryLevel: 2,
categoryName: "分类3",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367050021431861249",
parentId: "1367049973746819074",
rootId: "1367049864988516353",
categoryLevel: 3,
categoryName: "分类4",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367050063035162626",
parentId: "1367050021431861249",
rootId: "1367049864988516353",
categoryLevel: 4,
categoryName: "分类5",
sort: 0,
},
],
sort: 0,
},
],
sort: 0,
},
],
sort: 0,
},
],
sort: 2,
},
],
treeMap: {}, treeMap: {},
selectedKeys: ["0"]
}; };
} }
componentDidMount() { componentDidMount() {
this.setState({ treeData: this.renderTreeNodes(this.state.treeData) }); this.queryCategoryTree();
this.setState({
expandedKeys: this.getFirstLevelKeys(this.state.treeData),
});
} }
// 新增分类 // 查询分类树
newEditQuestionCategory = (categoryType, id, parentId, addLevelType) => { queryCategoryTree = (categoryName) => {
let query = {
source: 0,
categoryName,
userId: User.getUserId(),
tenantId: User.getStoreId(),
};
QuestionBankService.queryCategoryTree(query).then((res) => {
const { result = [] } = res;
const defaultNode = { id: "0", categoryName:"未分类", categoryCount: 0}
result.unshift(defaultNode);
this.setState({ treeData: this.renderTreeNodes(result, categoryName) });
this.setState({
expandedKeys: this.getFirstLevelKeys(result),
});
});
};
// 删除分类
delCategory = (item) => {
return confirm({
title: "确认删除该分类吗?",
content:
"此分类下存在关联项目,如删除,此分类下包含的所有内容将自动转入“未分类”中。",
icon: (
<span className="icon iconfont default-confirm-icon">&#xe839; </span>
),
okText: "删除",
okType: "danger",
cancelText: "取消",
onOk: () => {
let params = {
categoryId: item.id,
source: 0,
tenantId: User.getStoreId(),
userId: User.getUserId(),
};
QuestionBankService.delCategory(params).then((res) => {
if (res.success) {
message.success("删除分类成功");
this.queryCategoryTree();
}
});
},
});
};
// 新增或编辑分类
newEditQuestionCategory = (categoryType, addLevelType, type, node) => {
let title = ""; let title = "";
let label = ""; let label = "";
switch (categoryType) { switch (categoryType) {
...@@ -179,13 +114,14 @@ class QuestionCategoryManage extends Component { ...@@ -179,13 +114,14 @@ class QuestionCategoryManage extends Component {
} }
const m = ( const m = (
<NewEditQuestionBankCategory <NewEditQuestionBankCategory
id={id} node={node}
parentId={parentId}
addLevelType={addLevelType} addLevelType={addLevelType}
type={type}
treeData={this.state.treeData} treeData={this.state.treeData}
title={title} title={title}
label={label} label={label}
close={() => { close={() => {
this.queryCategoryTree();
this.setState({ this.setState({
NewEditQuestionBankCategory: null, NewEditQuestionBankCategory: null,
}); });
...@@ -205,38 +141,37 @@ class QuestionCategoryManage extends Component { ...@@ -205,38 +141,37 @@ class QuestionCategoryManage extends Component {
item.categoryLevel === 0 item.categoryLevel === 0
? "editEqualLevelCategory" ? "editEqualLevelCategory"
: "editChildLevelCategory"; : "editChildLevelCategory";
this.newEditQuestionCategory( this.newEditQuestionCategory(categoryType, "equal", "edit", item);
categoryType,
item.id,
item.parentId,
"equal"
);
}} }}
> >
重命名 重命名
</span> </span>
</Menu.Item> </Menu.Item>
<Menu.Item key="1"> <Menu.Item key="1">
<span>删除</span> <span
onClick={() => {
this.delCategory(item);
}}
>
删除
</span>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
); );
}; };
getRelatedNodes = (parentId) => { getRelatedNodes = (parentId) => {
console.log("parentId:", parentId); return this.state.treeMap[parentId]
return this.state.treeMap[parentId].subList ? this.state.treeMap[parentId].sonCategoryList
? this.state.treeMap[parentId].subList
: []; : [];
}; };
onDrop = (info) => { onDrop = (info) => {
console.log("info:", info);
// 未分类不可以拖拽 // 未分类不可以拖拽
if (info.dragNode.id === "1367023655839768500") return; if (info.dragNode.categoryName === "未分类" || info.node.categoryName === "未分类") return;
// 不允许其他节点拖拽到未分类之前 // 不允许其他节点拖拽到未分类之前
if ( if (
info.node.id === "1367023655839768500" && info.node.categoryName === "未分类" &&
info.dropToGap && info.dropToGap &&
info.dropPosition === -1 info.dropPosition === -1
) )
...@@ -245,14 +180,6 @@ class QuestionCategoryManage extends Component { ...@@ -245,14 +180,6 @@ class QuestionCategoryManage extends Component {
let targetParentId = info.dropToGap ? info.node.parentId : info.node.id; let targetParentId = info.dropToGap ? info.node.parentId : info.node.id;
let relatedNodes = this.getRelatedNodes(targetParentId); let relatedNodes = this.getRelatedNodes(targetParentId);
console.log(
"relatedNodes:",
relatedNodes.length,
relatedNodes,
targetParentId
);
const dropKey = info.node.key; const dropKey = info.node.key;
const dragKey = info.dragNode.key; const dragKey = info.dragNode.key;
const dropPos = info.node.pos.split("-"); const dropPos = info.node.pos.split("-");
...@@ -264,27 +191,25 @@ class QuestionCategoryManage extends Component { ...@@ -264,27 +191,25 @@ class QuestionCategoryManage extends Component {
if (data[i].key === key) { if (data[i].key === key) {
return callback(data[i], i, data); return callback(data[i], i, data);
} }
if (data[i].subList) { if (data[i].sonCategoryList) {
loop(data[i].subList, key, callback); loop(data[i].sonCategoryList, key, callback);
} }
} }
}; };
const data = [...this.state.treeData]; const data = [...this.state.treeData];
let getSuf = function (name, sufIndex) { let getSuf = function (name, originCategoryName, sufIndex) {
if (relatedNodes.length > 0) { if (relatedNodes && relatedNodes.length > 0) {
let sameNameNodes = []; let sameNameNodes = [];
relatedNodes.forEach((item) => { relatedNodes.forEach((item) => {
if (item.id === info.dragNode.id) return true; if (item.id === info.dragNode.id) return true;
if (item.categoryName === name) { if (item.categoryName === name) {
sameNameNodes.push(item); sameNameNodes.push(item);
console.log(item, sameNameNodes);
} }
}); });
if (sameNameNodes.length > 0) { if (sameNameNodes.length > 0) {
sufIndex++; sufIndex++;
return getSuf(name + `(${sufIndex})`, sufIndex); return getSuf(originCategoryName + `(${sufIndex})`, originCategoryName, sufIndex);
} }
} }
return sufIndex; return sufIndex;
...@@ -300,13 +225,9 @@ class QuestionCategoryManage extends Component { ...@@ -300,13 +225,9 @@ class QuestionCategoryManage extends Component {
item.originCategoryName = item.categoryName; item.originCategoryName = item.categoryName;
} }
info.dragNode.categoryName = item.originCategoryName; info.dragNode.categoryName = item.originCategoryName;
let sufIndex = getSuf(info.dragNode.categoryName, item.originCategoryName, 0);
console.log("info.dragNode.categoryName:", info.dragNode.categoryName); item.categoryName =
let sufIndex = getSuf(info.dragNode.categoryName, 0); item.categoryName + (sufIndex ? `(${sufIndex})` : "");
console.log("sufIndex1:", sufIndex);
console.log("sufIndex2:", sufIndex > 0 ? `${sufIndex}` : "");
item.categoryName = item.categoryName =
item.originCategoryName + (sufIndex ? `(${sufIndex})` : ""); item.originCategoryName + (sufIndex ? `(${sufIndex})` : "");
dragObj = item; dragObj = item;
...@@ -314,17 +235,17 @@ class QuestionCategoryManage extends Component { ...@@ -314,17 +235,17 @@ class QuestionCategoryManage extends Component {
if (!info.dropToGap) { if (!info.dropToGap) {
loop(data, dropKey, (item) => { loop(data, dropKey, (item) => {
item.subList = item.subList || []; item.sonCategoryList = item.sonCategoryList || [];
item.subList.push(dragObj); item.sonCategoryList.unshift(dragObj);
}); });
} else if ( } else if (
(info.node.props.subList || []).length > 0 && (info.node.props.sonCategoryList || []).length > 0 &&
info.node.props.expanded && info.node.props.expanded &&
dropPosition === 1 dropPosition === 1
) { ) {
loop(data, dropKey, (item) => { loop(data, dropKey, (item) => {
item.subList = item.children || []; item.sonCategoryList = item.children || [];
item.subList.push(dragObj); item.sonCategoryList.unshift(dragObj);
}); });
} else { } else {
let ar; let ar;
...@@ -339,17 +260,27 @@ class QuestionCategoryManage extends Component { ...@@ -339,17 +260,27 @@ class QuestionCategoryManage extends Component {
ar.splice(i + 1, 0, dragObj); ar.splice(i + 1, 0, dragObj);
} }
} }
data.shift();
console.log("data:", data) let newTreeData = this.renderTreeNodes(this.handleLoop(data, 0));
this.setState({ treeData: this.renderTreeNodes(this.handleLoop(data, 0)) }); this.setState({ treeData: newTreeData });
let params = {
categoryList: newTreeData,
source: 0,
tenantId: User.getStoreId(),
userId: User.getUserId(),
};
QuestionBankService.editCategoryTree(params).then((res) => {
this.queryCategoryTree();
});
}; };
handleLoop = (data, level) => { handleLoop = (data, level) => {
data.map((item, index) => { data.map((item, index) => {
item.categoryLevel = level;
item.sort = index; item.sort = index;
if (item.children) { item.categoryLevel = level;
this.handleLoop(item.children, level + 1); if (item.sonCategoryList) {
item.children = this.handleLoop(item.sonCategoryList, level + 1);
item.sonCategoryList = this.handleLoop(item.sonCategoryList, level + 1);
} }
return item; return item;
}); });
...@@ -387,15 +318,19 @@ class QuestionCategoryManage extends Component { ...@@ -387,15 +318,19 @@ class QuestionCategoryManage extends Component {
className="node-title-div" className="node-title-div"
onMouseOver={(e) => { onMouseOver={(e) => {
let mouseNodeOpts = e.currentTarget.getElementsByTagName("div")[0]; let mouseNodeOpts = e.currentTarget.getElementsByTagName("div")[0];
mouseNodeOpts.style.visibility = "visible"; if (mouseNodeOpts) {
mouseNodeOpts.style.visibility = "visible";
}
}} }}
onMouseOut={(e) => { onMouseOut={(e) => {
let mouseNodeOpts = e.currentTarget.getElementsByTagName("div")[0]; let mouseNodeOpts = e.currentTarget.getElementsByTagName("div")[0];
mouseNodeOpts.style.visibility = "hidden"; if (mouseNodeOpts) {
mouseNodeOpts.style.visibility = "hidden";
}
}} }}
> >
<span>{item.categoryName}</span> <span>{item.categoryName}</span>
{item.id !== "1367023655839768500" && ( {item.categoryName !== "未分类" && (
<Space className="title-opts" size={50}> <Space className="title-opts" size={50}>
<span <span
className="icon iconfont" className="icon iconfont"
...@@ -407,10 +342,14 @@ class QuestionCategoryManage extends Component { ...@@ -407,10 +342,14 @@ class QuestionCategoryManage extends Component {
nodesCount = treeData.length; nodesCount = treeData.length;
} else { } else {
let parentNodes = this.getRelatedNodes(item.parentId); let parentNodes = this.getRelatedNodes(item.parentId);
nodesCount = if (
parentNodes.length > 0 parentNodes.length > 0 &&
? parentNodes[0].subList.length parentNodes[0].sonCategoryList
: 0; ) {
nodesCount = parentNodes[0].sonCategoryList.length;
} else {
nodesCount = 0;
}
} }
if (nodesCount >= 30) { if (nodesCount >= 30) {
message.info("最多只能添加30个分类"); message.info("最多只能添加30个分类");
...@@ -418,9 +357,9 @@ class QuestionCategoryManage extends Component { ...@@ -418,9 +357,9 @@ class QuestionCategoryManage extends Component {
} }
this.newEditQuestionCategory( this.newEditQuestionCategory(
"newEqualLevelCategory", "newEqualLevelCategory",
item.id, "equal",
item.parentId, "new",
"equal" item
); );
}} }}
> >
...@@ -430,15 +369,18 @@ class QuestionCategoryManage extends Component { ...@@ -430,15 +369,18 @@ class QuestionCategoryManage extends Component {
<span <span
className="icon iconfont" className="icon iconfont"
onClick={() => { onClick={() => {
if (item.subList && item.subList.length >= 30) { if (
item.sonCategoryList &&
item.sonCategoryList.length >= 30
) {
message.info("最多只能添加30个子分类"); message.info("最多只能添加30个子分类");
return; return;
} }
this.newEditQuestionCategory( this.newEditQuestionCategory(
"newChildLevelCategory", "newChildLevelCategory",
item.id, "child",
item.parentId, "new",
"child" item
); );
}} }}
> >
...@@ -463,32 +405,36 @@ class QuestionCategoryManage extends Component { ...@@ -463,32 +405,36 @@ class QuestionCategoryManage extends Component {
</span> </span>
); );
if (item.subList) { if (item.sonCategoryList) {
item.children = this.renderTreeNodes(item.subList, value); item.children = this.renderTreeNodes(item.sonCategoryList, value);
} }
return item; return item;
}); });
this.getFirstLevelKeys(newTreeData); // this.getFirstLevelKeys(newTreeData);
let map = {}; let map = {};
let getChildren = function (data) { let getChildren = function (data) {
data.forEach((item) => { data.forEach((item) => {
map[item.id] = item; map[item.id] = item;
if (item.subList && item.subList.length > 0) { if (item.sonCategoryList && item.sonCategoryList.length > 0) {
getChildren(item.subList); getChildren(item.sonCategoryList);
} }
}); });
}; };
getChildren(data); getChildren(data);
console.log("map:", map);
this.setState({ treeMap: map }); this.setState({ treeMap: map });
return newTreeData; return newTreeData;
}; };
/** 树状选中事件 */
onSelect = (selectedKeys) => {
this.setState({ selectedKeys });
// TODO调用查询题目接口
};
render() { render() {
const { treeData, expandedKeys } = this.state; const { treeData, expandedKeys, selectedKeys } = this.state;
return ( return (
<div className="page question-category-manage"> <div className="page question-category-manage">
<Breadcrumbs navList="课程分类" goBack={() => RCHistory.goBack()} /> <Breadcrumbs navList="课程分类" goBack={() => RCHistory.goBack()} />
...@@ -498,12 +444,7 @@ class QuestionCategoryManage extends Component { ...@@ -498,12 +444,7 @@ class QuestionCategoryManage extends Component {
<Search <Search
placeholder="请输入名称" placeholder="请输入名称"
style={{ width: "calc(100% - 84px)" }} style={{ width: "calc(100% - 84px)" }}
onSearch={(value) => onSearch={(value) => this.queryCategoryTree(value)}
// TODO 调用查询分类接口
this.setState({
treeData: this.renderTreeNodes(treeData, value),
})
}
/> />
</div> </div>
<Button <Button
...@@ -515,9 +456,8 @@ class QuestionCategoryManage extends Component { ...@@ -515,9 +456,8 @@ class QuestionCategoryManage extends Component {
} }
this.newEditQuestionCategory( this.newEditQuestionCategory(
"newEqualLevelCategory", "newEqualLevelCategory",
"1367023655839768500", "equal",
"0", "new"
"equal"
); );
}} }}
> >
...@@ -530,7 +470,8 @@ class QuestionCategoryManage extends Component { ...@@ -530,7 +470,8 @@ class QuestionCategoryManage extends Component {
<DirectoryTree <DirectoryTree
expandedKeys={expandedKeys} expandedKeys={expandedKeys}
onExpand={this.onExpand} onExpand={this.onExpand}
defaultSelectedKeys={["1367023655839768500"]} selectedKeys={selectedKeys}
onSelect={this.onSelect}
draggable draggable
blockNode blockNode
onDrop={this.onDrop} onDrop={this.onDrop}
......
/*
* @Author: yuananting
* @Date: 2021-02-25 09:45:11
* @LastEditors: yuananting
* @LastEditTime: 2021-02-25 11:35:10
* @Description: 助学工具-题库-题目管理主页面
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import Breadcrumbs from "@/components/Breadcrumbs";
import { Space } from "antd";
import QuestionManageFilter from "./components/QuestionManageFilter";
import QuestionManageOpt from "./components/QuestionManageOpt";
import QuestionManageList from "./components/QuestionManageList";
import "./QuestionManageIndex.less";
class QuestionManageIndex extends Component {
constructor(props) {
super(props);
this.state = {
query: {
current: 1,
size: 10,
},
loading: true,
dataSource: [], // 题目列表数据
totalCount: 1, // 题目数据总条数
};
}
componentDidMount() {
// TODO
// 接口请求 初始化数据
this.getQuestionList();
}
getQuestionList = (_query = {}) => {
const query = {
...this.state.query,
..._query,
};
// 更新请求参数
this.setState({ query });
};
render() {
const { query, dataSource, totalCount } = this.state;
return (
<div className="question-manage-index page">
<Breadcrumbs navList="课程分类" goBack={() => RCHistory.goBack()} />
<div className="box">
<Space className="question-basic-info" size={100}>
<span>题库名称:{"业务培训"}</span>
<span>题目数量:{100}</span>
</Space>
<QuestionManageFilter />
<QuestionManageOpt />
<QuestionManageList
query={query}
dataSource={dataSource}
totalCount={totalCount}
onChange={this.getQuestionList}
/>
</div>
</div>
);
}
}
export default QuestionManageIndex;
/*
* @Author: yuananting
* @Date: 2021-02-25 10:49:16
* @LastEditors: yuananting
* @LastEditTime: 2021-02-25 10:53:58
* @Description: 助学工具-题库-题目管理主页面样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.question-manage-index {
position: relative;
.question-basic-info {
margin-bottom: 16px;
}
}
...@@ -45,61 +45,41 @@ class CompletionStem extends Component { ...@@ -45,61 +45,41 @@ class CompletionStem extends Component {
} }
watchStemContent = () => { watchStemContent = () => {
var currentBlanksList = this.saveCurrentBlanks(); // 获取当前填空列表 var _blanksList = [];
console.log("originBlanksList:", this.state.originBlanksList); const stemInput = document.getElementById("editor-box_content");
console.log("currentBlanksList:", currentBlanksList);
// console.log(JSON.stringify(currentBlanksList)===JSON.stringify(this.state.originBlanksList))
if (
JSON.stringify(currentBlanksList) !==
JSON.stringify(this.state.originBlanksList)
) {
console.log("修改")
this.setState({ originBlanksList: this.saveCurrentBlanks() }); // 保存修改前的填空列表
this.sortBlanks();
}
this.handleStemStyle();
this.props.changeBlankCount(currentBlanksList);
};
sortBlanks = () => {
const _blanksList = JSON.parse(JSON.stringify(this.saveCurrentBlanks()));
_blanksList.map((item, index) => {
item.innerHTML = "填空" + (index + 1);
return item;
});
this.setState({ blanksList: _blanksList });
};
saveCurrentBlanks = () => {
var currentBlanksList = [];
var stemInput = document.getElementById("editor-box_content");
stemInput.childNodes.forEach((item) => { stemInput.childNodes.forEach((item) => {
if (item.nodeName === "SPAN") { if (item.nodeName === "SPAN") {
currentBlanksList.push(item); _blanksList.push(item);
} }
}); });
return currentBlanksList; this.setState({blanksList:_blanksList})
this.handleStemStyle();
this.props.changeBlankCount(this.state.blanksList);
}; };
handleInsertBlanks = (id) => { /**
* 插入占位符
*
* @memberof QuestionInputItem
*/
insertBlanks = () => {
document.getElementById("editor-box_content").focus(); document.getElementById("editor-box_content").focus();
const { blanksList } = this.state; const _blanksList = this.state.blanksList;
blanksList.map((item, index) => { var blanks = document.createElement("span");
item.innerHTML = "填空" + (index + 1); blanks.className = "fill-line";
}); blanks.innerHTML = "填空";
var templateBlanks = blanksList.filter((item) => item.id === id); blanks.id = window.random_string(16); // 填空id
var insertBlanks = document.createElement("span"); blanks.contentEditable = false;
insertBlanks.className = "fill-line"; _blanksList.push(blanks);
insertBlanks.innerHTML = templateBlanks[0].innerHTML; this.setState({ blanksList: _blanksList });
insertBlanks.id = templateBlanks[0].id; // 填空id;
insertBlanks.contentEditable = false;
var sel, range; var sel, range;
sel = window.getSelection(); sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) { if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0); range = sel.getRangeAt(0);
range.deleteContents(); range.deleteContents();
var el = document.createElement("div"); var el = document.createElement("div");
el.appendChild(insertBlanks); el.appendChild(blanks);
var frag = document.createDocumentFragment(), var frag = document.createDocumentFragment(),
node, node,
lastNode; lastNode;
...@@ -117,24 +97,6 @@ class CompletionStem extends Component { ...@@ -117,24 +97,6 @@ class CompletionStem extends Component {
} }
}; };
/**
* 插入占位符
*
* @memberof QuestionInputItem
*/
insertBlanks = () => {
const _blanksList = this.state.blanksList;
var blanks = document.createElement("span");
blanks.className = "fill-line";
blanks.innerHTML = "填空";
blanks.id = window.random_string(16); // 填空id
blanks.contentEditable = false;
_blanksList.push(blanks);
this.setState({ blanksList: _blanksList });
this.handleInsertBlanks(blanks.id);
};
// 输入框样式 // 输入框样式
handleStemStyle = () => { handleStemStyle = () => {
const stemInput = document.getElementById("editor-box_content"); const stemInput = document.getElementById("editor-box_content");
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-25 14:34:29 * @Date: 2021-02-25 14:34:29
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-03-11 09:37:11 * @LastEditTime: 2021-03-13 20:09:55
* @Description: 助学工具-题库-题目管理-新建题目Tab * @Description: 助学工具-题库-题目管理-新建题目Tab
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -17,7 +17,7 @@ import { ...@@ -17,7 +17,7 @@ import {
NUM_TO_WORD_MAP, NUM_TO_WORD_MAP,
QUESTION_FILE_ACCEPT, QUESTION_FILE_ACCEPT,
} from "@/common/constants/punchClock/punchClock"; } from "@/common/constants/punchClock/punchClock";
import { defineOptionData } from "./model"; import { defineOptionInfo } from "./model";
import UploadingProgress from "./UploadingProgress"; import UploadingProgress from "./UploadingProgress";
import XMAudio from "./XMAudio"; import XMAudio from "./XMAudio";
import XMRecord from "./XMRecord"; import XMRecord from "./XMRecord";
...@@ -26,19 +26,17 @@ import ScanFileModal from "@/modules/resource-disk/modal/ScanFileModal"; ...@@ -26,19 +26,17 @@ import ScanFileModal from "@/modules/resource-disk/modal/ScanFileModal";
class NewQuestionTab extends Component { class NewQuestionTab extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
const { questionContent = {} } = props; const { questionInfo = {} } = props;
const { const {
topicDescribeVOList, questionStemList,
itemInfoVOList, optionList,
parsingDescribeVOList, questionAnswerDescList,
taskModuleQuestionVO,
showBox, showBox,
} = questionContent; } = questionInfo;
this.state = { this.state = {
questionDetail: JSON.parse(JSON.stringify(taskModuleQuestionVO)), // 题目其他信息 stemContent: JSON.parse(JSON.stringify(questionStemList)), // 题干内容
stemContent: JSON.parse(JSON.stringify(topicDescribeVOList)), // 题干内容 chooseOptions: JSON.parse(JSON.stringify(optionList)), // 单选多选不定项-选项列表
chooseOptions: JSON.parse(JSON.stringify(itemInfoVOList)), // 单选多选不定项-选项列表 questionAnswerDesc: JSON.parse(JSON.stringify(questionAnswerDescList)), // 答案解析
analysisContent: JSON.parse(JSON.stringify(parsingDescribeVOList)), // 答案解析
accept: QUESTION_FILE_ACCEPT["PICTURE"], // 上传媒体类型 accept: QUESTION_FILE_ACCEPT["PICTURE"], // 上传媒体类型
fileType: "PICTURE", // 媒体枚举 fileType: "PICTURE", // 媒体枚举
showRecord: false, // 录音弹窗 showRecord: false, // 录音弹窗
...@@ -50,12 +48,12 @@ class NewQuestionTab extends Component { ...@@ -50,12 +48,12 @@ class NewQuestionTab extends Component {
// 判断-选项列表 // 判断-选项列表
label: "正确", label: "正确",
value: true, value: true,
ifCorrectAnswerItem: null, isCorrectAnswer: null,
}, },
{ {
label: "错误", label: "错误",
value: false, value: false,
ifCorrectAnswerItem: null, isCorrectAnswer: null,
}, },
], ],
}; };
...@@ -76,16 +74,56 @@ class NewQuestionTab extends Component { ...@@ -76,16 +74,56 @@ class NewQuestionTab extends Component {
this.props.onRef(this); this.props.onRef(this);
} }
handleLogger = (en, cn) => {
const { onLogger } = this.props;
onLogger && onLogger(en, cn);
};
static getDerivedStateFromProps(nextProps, prevState) {
// 控制录音组件展示
if (nextProps.showBox && !prevState.showBox) {
return {
showRecord: false,
showBox: nextProps.showBox,
};
}
return {
showBox: nextProps.showBox,
};
}
shouldComponentUpdate(nextProps, nextState) {
return (
JSON.stringify(nextProps) !== JSON.stringify(this.props) ||
JSON.stringify(nextState) !== JSON.stringify(this.state)
);
}
_onSetState = (params = {}) => {
this.setState({ ...params, updateKey: window.random_string(16) }, () => {
this.props.onSetState({
questionStemList: JSON.parse(JSON.stringify(this.state.stemContent)),
optionList: JSON.parse(JSON.stringify(this.state.chooseOptions)),
questionAnswerDescList: JSON.parse(
JSON.stringify(this.state.questionAnswerDesc)
),
});
});
};
// 保存校验
checkInput = () => { checkInput = () => {
let validateError = 0;
// 题干校验 // 题干校验
const stemContent = _.find( const stemContent = _.find(
this.state.stemContent, this.state.stemContent,
(contentItem) => contentItem.contentType === "TEXT" (contentItem) => contentItem.type === "RICH_TEXT"
); );
const stem = stemContent.content.replace(/<[^>]+>/g, ""); const stem = stemContent.content.replace(/<[^>]+>/g, "");
if (stem.length === 0) { if (stem.length === 0) {
this.setState({ stemValidate: "error" }); this.setState({ stemValidate: "error" });
this.setState({ stemText: "请输入题干" }); this.setState({ stemText: "请输入题干" });
validateError++;
} else { } else {
this.setState({ stemValidate: "success" }); this.setState({ stemValidate: "success" });
this.setState({ stemText: "" }); this.setState({ stemText: "" });
...@@ -97,28 +135,30 @@ class NewQuestionTab extends Component { ...@@ -97,28 +135,30 @@ class NewQuestionTab extends Component {
const { questionTypeKey } = this.props; const { questionTypeKey } = this.props;
if (questionTypeKey === "JUDGE") { if (questionTypeKey === "JUDGE") {
optionUnChecked = judgeOptions.filter( optionUnChecked = judgeOptions.filter(
(item) => item.ifCorrectAnswerItem === null (item) => item.isCorrectAnswer === null
).length; ).length;
if (optionUnChecked === judgeOptions.length) { if (optionUnChecked === judgeOptions.length) {
this.setState({ radioValidate: "error" }); this.setState({ radioValidate: "error" });
this.setState({ radioText: "请选择正确答案" }); this.setState({ radioText: "请选择正确答案" });
validateError++;
} else { } else {
this.setState({ radioValidate: "success" }); this.setState({ radioValidate: "success" });
this.setState({ radioText: "" }); this.setState({ radioText: "" });
} }
} else { } else {
chooseOptions.forEach((item, index) => { chooseOptions.forEach((item, index) => {
const optionContent = item.itemContentVOList; const optionContent = item.questionOptionContentList;
optionUnChecked = item.questionItemVO.ifCorrectAnswerItem optionUnChecked = item.isCorrectAnswer
? optionUnChecked ? optionUnChecked
: optionUnChecked + 1; : optionUnChecked + 1;
if ( if (
optionContent.length === 1 && optionContent.length === 1 &&
optionContent[0].contentType === "TEXT" && optionContent[0].type === "RICH_TEXT" &&
optionContent[0].content.length === 0 optionContent[0].content.length === 0
) { ) {
this.setState({ [`optionsValidate_${index}`]: "error" }); this.setState({ [`optionsValidate_${index}`]: "error" });
this.setState({ [`optionsText_${index}`]: "请输入选项" }); this.setState({ [`optionsText_${index}`]: "请输入选项" });
validateError++;
} else { } else {
this.setState({ [`optionsValidate_${index}`]: "success" }); this.setState({ [`optionsValidate_${index}`]: "success" });
this.setState({ [`optionsText_${index}`]: "" }); this.setState({ [`optionsText_${index}`]: "" });
...@@ -127,46 +167,21 @@ class NewQuestionTab extends Component { ...@@ -127,46 +167,21 @@ class NewQuestionTab extends Component {
if (optionUnChecked === chooseOptions.length) { if (optionUnChecked === chooseOptions.length) {
this.setState({ radioValidate: "error" }); this.setState({ radioValidate: "error" });
this.setState({ radioText: "请选择正确答案" }); this.setState({ radioText: "请选择正确答案" });
validateError++;
} else { } else {
this.setState({ radioValidate: "success" }); this.setState({ radioValidate: "success" });
this.setState({ radioText: "" }); this.setState({ radioText: "" });
} }
if ( if (
this.props.questionTypeKey === "MULTI_SELECT" && this.props.questionTypeKey === "MULTI_CHOICE" &&
this.state.chooseOptions.length - optionUnChecked === 1 this.state.chooseOptions.length - optionUnChecked === 1
) { ) {
this.setState({ radioValidate: "error" }); this.setState({ radioValidate: "error" });
this.setState({ radioText: "最少选两个" }); this.setState({ radioText: "最少选两个" });
validateError++;
} }
} }
}; return validateError;
static getDerivedStateFromProps(nextProps, prevState) {
// 控制录音组件展示
if (nextProps.showBox && !prevState.showBox) {
return {
showRecord: false,
showBox: nextProps.showBox,
};
}
return {
showBox: nextProps.showBox,
};
}
_onSetState = (params = {}) => {
this.setState({ ...params, updateKey: window.random_string(16) }, () => {
this.props.onSetState({
topicDescribeVOList: JSON.parse(JSON.stringify(this.state.stemContent)),
itemInfoVOList: JSON.parse(JSON.stringify(this.state.chooseOptions)),
parsingDescribeVOList: JSON.parse(
JSON.stringify(this.state.analysisContent)
),
taskModuleQuestionVO: JSON.parse(
JSON.stringify(this.state.questionDetail)
),
});
});
}; };
/** /**
...@@ -192,11 +207,40 @@ class NewQuestionTab extends Component { ...@@ -192,11 +207,40 @@ class NewQuestionTab extends Component {
if (chooseOptions.length >= 20) { if (chooseOptions.length >= 20) {
message.warning("最多添加20个选项"); message.warning("最多添加20个选项");
} else { } else {
chooseOptions.push(defineOptionData(content)); chooseOptions.push(defineOptionInfo(content));
this._onSetState(); this._onSetState();
} }
}; };
/**
* 删除选项
*
* @memberof QuestionInputItem
*/
handleDelOption = (optionIndex) => {
const { chooseOptions } = this.state;
this.handleLogger("delete_option", "删除选项");
if (chooseOptions.length < 3) {
message.warning("至少保留2个选项");
} else {
chooseOptions.splice(optionIndex, 1);
this._onSetState();
}
};
/**
* 移动选项
*
* @memberof QuestionInputItem
*/
handleMoveOption = (optionIndex, moveLength) => {
const { chooseOptions } = this.state;
const optionItem = chooseOptions.splice(optionIndex + moveLength, 1);
this.handleLogger("sort_option", "选项排序");
chooseOptions.splice(optionIndex, 0, optionItem[0]);
this._onSetState();
};
/** /**
* 选择上传文件类型 * 选择上传文件类型
* *
...@@ -205,7 +249,7 @@ class NewQuestionTab extends Component { ...@@ -205,7 +249,7 @@ class NewQuestionTab extends Component {
handleChangeMedia = (key, uploadItemTarget, inputType) => { handleChangeMedia = (key, uploadItemTarget, inputType) => {
const { mediaFirstType } = this.state; const { mediaFirstType } = this.state;
const mediaArr = _.filter(uploadItemTarget, (mediaItem) => { const mediaArr = _.filter(uploadItemTarget, (mediaItem) => {
return mediaItem.type !== "TEXT"; return mediaItem.type !== "RICH_TEXT";
}); });
if (mediaArr.length === 0) { if (mediaArr.length === 0) {
this.setState({ mediaFirstType: key }); this.setState({ mediaFirstType: key });
...@@ -277,14 +321,14 @@ class NewQuestionTab extends Component { ...@@ -277,14 +321,14 @@ class NewQuestionTab extends Component {
async uploadFile(event) { async uploadFile(event) {
const { fileType, uploadItemTarget } = this.state; const { fileType, uploadItemTarget } = this.state;
const imageFile = event.target.files[0]; const mediaFile = event.target.files[0];
if (!imageFile) return; if (!mediaFile) return;
if (fileType === "VOICE") { if (fileType === "VOICE") {
if (!QUESTION_FILE_ACCEPT.VOICE.split(",").includes(imageFile.type)) { if (!QUESTION_FILE_ACCEPT.VOICE.split(",").includes(mediaFile.type)) {
message.warning("文件格式不正确"); message.warning("文件格式不正确");
return; return;
} }
if (imageFile.size > 30 * 1024 * 1024) { if (mediaFile.size > 30 * 1024 * 1024) {
Modal.warning({ Modal.warning({
title: "音频过大", title: "音频过大",
content: "音频大小超过30M,请压缩后上传", content: "音频大小超过30M,请压缩后上传",
...@@ -294,11 +338,11 @@ class NewQuestionTab extends Component { ...@@ -294,11 +338,11 @@ class NewQuestionTab extends Component {
} }
if (fileType === "PICTURE") { if (fileType === "PICTURE") {
if (!QUESTION_FILE_ACCEPT.PICTURE.split(",").includes(imageFile.type)) { if (!QUESTION_FILE_ACCEPT.PICTURE.split(",").includes(mediaFile.type)) {
message.warning("文件格式不正确"); message.warning("文件格式不正确");
return; return;
} }
if (imageFile.size > 8 * 1024 * 1024) { if (mediaFile.size > 8 * 1024 * 1024) {
Modal.warning({ Modal.warning({
title: "图片过大", title: "图片过大",
content: "图片大小超过8M,请压缩后上传", content: "图片大小超过8M,请压缩后上传",
...@@ -308,11 +352,11 @@ class NewQuestionTab extends Component { ...@@ -308,11 +352,11 @@ class NewQuestionTab extends Component {
} }
if (fileType === "VIDEO") { if (fileType === "VIDEO") {
if (!QUESTION_FILE_ACCEPT.VIDEO.split(",").includes(imageFile.type)) { if (!QUESTION_FILE_ACCEPT.VIDEO.split(",").includes(mediaFile.type)) {
message.warning("文件格式不正确"); message.warning("文件格式不正确");
return; return;
} }
if (imageFile.size > 1024 * 1024 * 1024) { if (mediaFile.size > 1024 * 1024 * 1024) {
Modal.warning({ Modal.warning({
title: "视频过大", title: "视频过大",
content: "视频大小超过1G,请压缩后上传", content: "视频大小超过1G,请压缩后上传",
...@@ -320,21 +364,20 @@ class NewQuestionTab extends Component { ...@@ -320,21 +364,20 @@ class NewQuestionTab extends Component {
return; return;
} }
} }
const originArr = imageFile.name.split("."); const originArr = mediaFile.name.split(".");
const originType = originArr[originArr.length - 1]; const originType = originArr[originArr.length - 1];
const uploadObj = { const uploadObj = {
contentType: fileType, contentType: "QUESTION_STEM",
type: fileType, type: fileType,
contentName: `${window.random_string(16)}.${originType}`, contentName: `${window.random_string(16)}.${originType}`, // 文件名
status: "init", fileType: originType, // 文件后缀
key: window.random_string(16), mediaFile, // url
imageFile,
}; };
if (["VIDEO", "VOICE"].includes(fileType)) { if (["VIDEO", "VOICE"].includes(fileType)) {
try { try {
await new Promise((resolve) => { await new Promise((resolve) => {
const fileurl = URL.createObjectURL(imageFile); const fileurl = URL.createObjectURL(mediaFile);
const audioElement = new Audio(fileurl); const audioElement = new Audio(fileurl);
audioElement.addEventListener("loadedmetadata", (_event) => { audioElement.addEventListener("loadedmetadata", (_event) => {
const duration = audioElement.duration; const duration = audioElement.duration;
...@@ -345,6 +388,8 @@ class NewQuestionTab extends Component { ...@@ -345,6 +388,8 @@ class NewQuestionTab extends Component {
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
} else if (fileType === "PICTURE") {
uploadObj.size = mediaFile.size;
} }
uploadItemTarget.push(uploadObj); uploadItemTarget.push(uploadObj);
this.setState({}, () => { this.setState({}, () => {
...@@ -405,15 +450,19 @@ class NewQuestionTab extends Component { ...@@ -405,15 +450,19 @@ class NewQuestionTab extends Component {
const _blanksList = this.state.blanksList; const _blanksList = this.state.blanksList;
_blanksList.forEach((item) => { _blanksList.forEach((item) => {
if (item.id === optionItem.id) { if (item.id === optionItem.id) {
item.answerTagList.map() item.answerTagList.map();
} }
}); });
this.setState({ blanksList: _blanksList }); this.setState({ blanksList: _blanksList });
} };
renderCompletionAnswer = (optionItem) => {
renderCompletionAnswer = (optionItem, optionIndex) => {
return ( return (
<div className="completion-answer-box"> <div className="completion-answer-box">
<span className="completion-answer-label">{optionItem.innerHTML}.</span> <span className="completion-answer-label">
{optionItem.innerHTML}
{optionIndex + 1}.
</span>
<div className="completion-answer-content"> <div className="completion-answer-content">
{optionItem.answerTagList.map((tag, index) => { {optionItem.answerTagList.map((tag, index) => {
return optionItem.editInput ? ( return optionItem.editInput ? (
...@@ -488,9 +537,9 @@ class NewQuestionTab extends Component { ...@@ -488,9 +537,9 @@ class NewQuestionTab extends Component {
validateStatus validateStatus
) => { ) => {
const isCompletion = this.props.questionTypeKey === "COMPLETION"; const isCompletion = this.props.questionTypeKey === "COMPLETION";
const textContent = _.find( const editorContent = _.find(
contentList, contentList,
(contentItem) => contentItem.contentType === "TEXT" (contentItem) => contentItem.type === "RICH_TEXT"
); );
return ( return (
<React.Fragment> <React.Fragment>
...@@ -511,15 +560,15 @@ class NewQuestionTab extends Component { ...@@ -511,15 +560,15 @@ class NewQuestionTab extends Component {
markKey={this.markKey} markKey={this.markKey}
placehold={placehold} placehold={placehold}
validateStatus={validateStatus} validateStatus={validateStatus}
detail={contentList} detailInfo={contentList}
mediaBtn={mediaBtn} mediaBtn={mediaBtn}
bindChangeContent={(cb, textElemId) => { bindChangeContent={(cb, textElemId) => {
this.setState({ textElemId }); this.setState({ textElemId });
textContent.handleChangeContent = cb; editorContent.handleChangeContent = cb;
}} }}
onChange={(content, textLength) => { onChange={(content, textLength) => {
textContent.content = content; editorContent.content = content;
textContent.textLength = textLength; editorContent.textLength = textLength;
this._onSetState(); this._onSetState();
}} }}
onUploadMedia={(key) => { onUploadMedia={(key) => {
...@@ -529,7 +578,7 @@ class NewQuestionTab extends Component { ...@@ -529,7 +578,7 @@ class NewQuestionTab extends Component {
)} )}
</div> </div>
{_.map(contentList, (contentItem, index) => { {_.map(contentList, (contentItem, index) => {
const { contentType, content, status } = contentItem; const { type, content, status } = contentItem;
let dom = ""; let dom = "";
if (["init", "fail"].includes(status)) { if (["init", "fail"].includes(status)) {
return ( return (
...@@ -543,7 +592,7 @@ class NewQuestionTab extends Component { ...@@ -543,7 +592,7 @@ class NewQuestionTab extends Component {
</div> </div>
); );
} }
switch (contentType) { switch (type) {
case "PICTURE": case "PICTURE":
dom = ( dom = (
<div className="picture-box"> <div className="picture-box">
...@@ -593,7 +642,7 @@ class NewQuestionTab extends Component { ...@@ -593,7 +642,7 @@ class NewQuestionTab extends Component {
<div <div
className="question-item_question-content" className="question-item_question-content"
style={{ style={{
display: ["PICTURE", "VIDEO"].includes(contentType) display: ["PICTURE", "VIDEO"].includes(type)
? "inline-flex" ? "inline-flex"
: "flex", : "flex",
}} }}
...@@ -602,7 +651,7 @@ class NewQuestionTab extends Component { ...@@ -602,7 +651,7 @@ class NewQuestionTab extends Component {
{dom} {dom}
<span <span
className={ className={
["PICTURE", "VIDEO"].includes(contentType) ["PICTURE", "VIDEO"].includes(type)
? "icon_arrow iconfont" ? "icon_arrow iconfont"
: "icon_sider iconfont" : "icon_sider iconfont"
} }
...@@ -620,40 +669,6 @@ class NewQuestionTab extends Component { ...@@ -620,40 +669,6 @@ class NewQuestionTab extends Component {
); );
}; };
handleLogger = (en, cn) => {
const { onLogger } = this.props;
onLogger && onLogger(en, cn);
};
/**
* 删除选项
*
* @memberof QuestionInputItem
*/
handleDelOption = (optionIndex) => {
const { chooseOptions } = this.state;
this.handleLogger("delete_option", "删除选项");
if (chooseOptions.length < 3) {
message.warning("至少保留2个选项");
} else {
chooseOptions.splice(optionIndex, 1);
this.setState({ chooseOptions });
}
};
/**
* 移动选项
*
* @memberof QuestionInputItem
*/
handleMoveOption = (optionIndex, moveLength) => {
const { chooseOptions } = this.state;
const optionItem = chooseOptions.splice(optionIndex + moveLength, 1);
this.handleLogger("sort_option", "选项排序");
chooseOptions.splice(optionIndex, 0, optionItem[0]);
this.setState({ chooseOptions });
};
/** /**
* 重新上传 * 重新上传
* *
...@@ -662,7 +677,7 @@ class NewQuestionTab extends Component { ...@@ -662,7 +677,7 @@ class NewQuestionTab extends Component {
handleReupload = (uploadItem) => { handleReupload = (uploadItem) => {
uploadItem.status = "init"; uploadItem.status = "init";
Upload.uploadToOSSEvent( Upload.uploadToOSSEvent(
uploadItem.imageFile, uploadItem.mediaFile,
uploadItem.contentName, uploadItem.contentName,
(url, xhr) => { (url, xhr) => {
uploadItem.content = url; uploadItem.content = url;
...@@ -677,7 +692,7 @@ class NewQuestionTab extends Component { ...@@ -677,7 +692,7 @@ class NewQuestionTab extends Component {
uploadItem.status = "success"; uploadItem.status = "success";
delete uploadItem.xhr; delete uploadItem.xhr;
delete uploadItem.progress; delete uploadItem.progress;
delete uploadItem.imageFile; delete uploadItem.mediaFile;
this._onSetState(); this._onSetState();
}, },
() => { () => {
...@@ -727,48 +742,12 @@ class NewQuestionTab extends Component { ...@@ -727,48 +742,12 @@ class NewQuestionTab extends Component {
this.setState({ showRecord: false }); this.setState({ showRecord: false });
}; };
/**
* 插入占位符
*
* @memberof QuestionInputItem
*/
insertHTML = () => {
document.getElementById("completionStem").focus();
var blanks = document.createElement("span");
blanks.className = "fill-line";
blanks.innerHTML = `&nbsp;&nbsp;填空${1}&nbsp;&nbsp;`;
blanks.contentEditable = false;
var sel, range;
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
var el = document.createElement("div");
el.appendChild(blanks);
var frag = document.createDocumentFragment(),
node,
lastNode;
while ((node = el.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
};
render() { render() {
const { const {
stemContent, stemContent,
chooseOptions, chooseOptions,
judgeOptions, judgeOptions,
analysisContent, questionAnswerDesc,
accept, accept,
showRecord, showRecord,
showScanFile, showScanFile,
...@@ -832,7 +811,7 @@ class NewQuestionTab extends Component { ...@@ -832,7 +811,7 @@ class NewQuestionTab extends Component {
<span className="answer-tip">请在题干中插入答案占位符</span> <span className="answer-tip">请在题干中插入答案占位符</span>
) : ( ) : (
_.map(blanksList, (item, index) => { _.map(blanksList, (item, index) => {
return this.renderCompletionAnswer(item); return this.renderCompletionAnswer(item, index);
}) })
)} )}
</Form.Item> </Form.Item>
...@@ -862,12 +841,12 @@ class NewQuestionTab extends Component { ...@@ -862,12 +841,12 @@ class NewQuestionTab extends Component {
> >
{/* 判断 */} {/* 判断 */}
<Radio <Radio
checked={optionItem.ifCorrectAnswerItem} checked={optionItem.isCorrectAnswer === 1}
onClick={() => { onClick={() => {
_.each(judgeOptions, (item) => { _.each(judgeOptions, (item) => {
item.ifCorrectAnswerItem = false; item.isCorrectAnswer = 0;
}); });
optionItem.ifCorrectAnswerItem = true; optionItem.isCorrectAnswer = 1;
this._onSetState(); this._onSetState();
}} }}
/> />
...@@ -883,12 +862,16 @@ class NewQuestionTab extends Component { ...@@ -883,12 +862,16 @@ class NewQuestionTab extends Component {
); );
}) })
: _.map(chooseOptions, (optionItem, optionIndex) => { : _.map(chooseOptions, (optionItem, optionIndex) => {
const { itemContentVOList, questionItemVO } = optionItem; const {
questionOptionContentList,
isCorrectAnswer,
} = optionItem;
optionItem.optionSort = optionIndex;
const mediaBtn = ["VOICE", "RECORD", "PICTURE"]; const mediaBtn = ["VOICE", "RECORD", "PICTURE"];
const placeHold = const placeHold =
"必填(1000字以内,可粘贴小图;可以不输入文字,只添加音频或图片)"; "必填(1000字以内,可粘贴小图;可以不输入文字,只添加音频或图片)";
return ( return (
<div className="question-item_options__content"> <div className="question-item_options__content" key={optionIndex}>
<div className="question-item_options__setting"> <div className="question-item_options__setting">
<Form.Item <Form.Item
validateStatus={radioValidate} validateStatus={radioValidate}
...@@ -899,27 +882,28 @@ class NewQuestionTab extends Component { ...@@ -899,27 +882,28 @@ class NewQuestionTab extends Component {
} }
> >
{/* 单选 */} {/* 单选 */}
{this.props.questionTypeKey === "THE_RADIO" && ( {this.props.questionTypeKey ===
"SINGLE_CHOICE" && (
<Radio <Radio
checked={questionItemVO.ifCorrectAnswerItem} checked={isCorrectAnswer}
onClick={() => { onClick={() => {
_.each(chooseOptions, (o) => { _.each(chooseOptions, (o) => {
o.questionItemVO.ifCorrectAnswerItem = false; o.isCorrectAnswer = 0;
}); });
questionItemVO.ifCorrectAnswerItem = true; optionItem.isCorrectAnswer = 1;
this._onSetState(); this._onSetState();
}} }}
/> />
)} )}
{/* 多选 or 不定项 */} {/* 多选 or 不定项 */}
{["INDEFINITE_SELECT", "MULTI_SELECT"].includes( {["INDEFINITE_SELECT", "MULTI_CHOICE"].includes(
this.props.questionTypeKey this.props.questionTypeKey
) && ( ) && (
<Checkbox <Checkbox
checked={questionItemVO.ifCorrectAnswerItem} checked={isCorrectAnswer === 1}
onChange={(e) => { onChange={(e) => {
const checked = e.target.checked; const checked = e.target.checked ? 1 : 0;
questionItemVO.ifCorrectAnswerItem = checked; optionItem.isCorrectAnswer = checked;
this._onSetState(); this._onSetState();
}} }}
/> />
...@@ -937,7 +921,7 @@ class NewQuestionTab extends Component { ...@@ -937,7 +921,7 @@ class NewQuestionTab extends Component {
help={this.state[`optionsText_${optionIndex}`]} help={this.state[`optionsText_${optionIndex}`]}
> >
{this.renderContent( {this.renderContent(
itemContentVOList, questionOptionContentList,
placeHold, placeHold,
mediaBtn, mediaBtn,
"options", "options",
...@@ -1000,7 +984,7 @@ class NewQuestionTab extends Component { ...@@ -1000,7 +984,7 @@ class NewQuestionTab extends Component {
<Form.Item name="analysis" label="答案解析"> <Form.Item name="analysis" label="答案解析">
<div className="question-item_analysis__content"> <div className="question-item_analysis__content">
{this.renderContent( {this.renderContent(
analysisContent, questionAnswerDesc,
"1000字以内,可粘贴小图", "1000字以内,可粘贴小图",
["VOICE", "RECORD", "PICTURE", "VIDEO"], ["VOICE", "RECORD", "PICTURE", "VIDEO"],
"analysis" "analysis"
......
...@@ -2,13 +2,15 @@ ...@@ -2,13 +2,15 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-22 10:59:43 * @Date: 2021-02-22 10:59:43
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-03-05 09:39:47 * @LastEditTime: 2021-03-13 20:29:29
* @Description: 助学工具-题库-题库主页面侧边栏 * @Description: 助学工具-题库-题库主页面侧边栏
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React, { Component } from "react"; import React, { Component } from "react";
import { Input, Button, Tree } from "antd"; import { Input, Button, Tree } from "antd";
import "./QuestionBankSider.less"; import "./QuestionBankSider.less";
import User from "@/common/js/user";
import QuestionBankService from "@/domains/question-bank-domain/QuestionBankService";
const { Search } = Input; const { Search } = Input;
const { DirectoryTree } = Tree; const { DirectoryTree } = Tree;
...@@ -17,140 +19,59 @@ class QuestionBankSider extends Component { ...@@ -17,140 +19,59 @@ class QuestionBankSider extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
selectedKeys: ["0"],
searchValue: null, searchValue: null,
NewEditQuestionBankCategory: null, //新增或编辑分类模态框 NewEditQuestionBankCategory: null, //新增或编辑分类模态框
ImportCourseCategory: null, // 引用课程分类模态框 ImportCourseCategory: null, // 引用课程分类模态框
treeData: [ treeData: this.props.treeData || [],
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367023655839768500",
parentId: "0",
rootId: "1367023655839768500",
categoryLevel: 0,
categoryName: "未分类",
sort: 0,
},
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367023655839768577",
parentId: "0",
rootId: "1367023655839768577",
categoryLevel: 0,
categoryName: "分类1",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367023709942095873",
parentId: "1367023655839768577",
rootId: "1367023655839768577",
categoryLevel: 1,
categoryName: "test",
sort: 0,
},
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367023725184196609",
parentId: "1367023655839768577",
rootId: "1367023655839768577",
categoryLevel: 1,
categoryName: "分类2",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367023805563838466",
parentId: "1367023725184196609",
rootId: "1367023655839768577",
categoryLevel: 2,
categoryName: "test",
sort: 0,
},
],
sort: 1,
},
],
sort: 1,
},
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367049864988516353",
parentId: "0",
rootId: "1367049864988516353",
categoryLevel: 0,
categoryName: "分类2",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367049931656978433",
parentId: "1367049864988516353",
rootId: "1367049864988516353",
categoryLevel: 1,
categoryName: "分类2",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367049973746819074",
parentId: "1367049931656978433",
rootId: "1367049864988516353",
categoryLevel: 2,
categoryName: "分类3",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367050021431861249",
parentId: "1367049973746819074",
rootId: "1367049864988516353",
categoryLevel: 3,
categoryName: "分类4",
subList: [
{
tenantId: "1122334455667788",
userId: "12345678",
source: 0,
id: "1367050063035162626",
parentId: "1367050021431861249",
rootId: "1367049864988516353",
categoryLevel: 4,
categoryName: "分类5",
sort: 0,
},
],
sort: 0,
},
],
sort: 0,
},
],
sort: 0,
},
],
sort: 2,
},
],
}; };
} }
componentDidMount() { componentDidMount() {
this.setState({ treeData: this.renderTreeNodes(this.state.treeData) }); this.queryCategoryTree();
} }
/** 获取树状第一级key 设置默认展开第一项 */
getFirstLevelKeys = (data) => {
let firstLevelKeys = [];
data.forEach((item) => {
if (item.categoryLevel === 0) {
firstLevelKeys.push(item.key);
}
});
return firstLevelKeys;
};
/** 树状展开事件 */
onExpand = (expandedKeys) => {
this.setState({ expandedKeys });
};
/** 树状选中事件 */
onSelect = (selectedKeys) => {
this.setState({ selectedKeys });
this.props.getSelectedCategoryId(selectedKeys)
};
// 查询分类树
queryCategoryTree = (categoryName) => {
let query = {
source: 0,
categoryName,
userId: User.getUserId(),
tenantId: User.getStoreId(),
};
QuestionBankService.queryQuestionCategoryTree(query).then((res) => {
const { categoryList = [] } = res.result;
const defaultNode = { id: "0", categoryName:"未分类", categoryCount: 0}
categoryList.unshift(defaultNode);
this.setState({ treeData: this.renderTreeNodes(categoryList, categoryName) });
this.setState({
expandedKeys: this.getFirstLevelKeys(categoryList),
});
});
};
renderTreeNodes = (data, value) => { renderTreeNodes = (data, value) => {
return data.map((item) => { return data.map((item) => {
item.title = item.categoryName; item.title = item.categoryName;
...@@ -166,7 +87,7 @@ class QuestionBankSider extends Component { ...@@ -166,7 +87,7 @@ class QuestionBankSider extends Component {
</span> </span>
); );
item.icon = item.icon =
item.id === "default" ? ( item.categoryName === "未分类" ? (
<span className="icon iconfont" style={{ color: "#FBD140" }}> <span className="icon iconfont" style={{ color: "#FBD140" }}>
&#xe7f6; &#xe7f6;
</span> </span>
...@@ -175,15 +96,15 @@ class QuestionBankSider extends Component { ...@@ -175,15 +96,15 @@ class QuestionBankSider extends Component {
&#xe7f1; &#xe7f1;
</span> </span>
); );
if (item.subList) { if (item.sonCategoryList) {
item.children = this.renderTreeNodes(item.subList, value); item.children = this.renderTreeNodes(item.sonCategoryList, value);
} }
return item; return item;
}); });
}; };
render() { render() {
const { treeData } = this.state; const { treeData, expandedKeys, selectedKeys } = this.state;
return ( return (
<div className="question-bank-sider"> <div className="question-bank-sider">
<div className="sider-title">题目分类</div> <div className="sider-title">题目分类</div>
...@@ -192,9 +113,7 @@ class QuestionBankSider extends Component { ...@@ -192,9 +113,7 @@ class QuestionBankSider extends Component {
placeholder="搜索名称分类" placeholder="搜索名称分类"
onSearch={(value) => { onSearch={(value) => {
// TODO 调用查询分类接口 // TODO 调用查询分类接口
this.setState({ this.queryCategoryTree(value);
treeData: this.renderTreeNodes(treeData, value),
});
}} }}
/> />
<div className="sider-btn"> <div className="sider-btn">
...@@ -208,17 +127,16 @@ class QuestionBankSider extends Component { ...@@ -208,17 +127,16 @@ class QuestionBankSider extends Component {
分类管理 分类管理
</Button> </Button>
</div> </div>
<DirectoryTree <div className="sider-tree">
defaultSelectedKeys={["1367023655839768500"]} <DirectoryTree
showIcon expandedKeys={expandedKeys}
treeData={treeData} onExpand={this.onExpand}
/> selectedKeys={selectedKeys}
{treeData.length === 1 && ( onSelect={this.onSelect}
<div className="empty-tree-tip"> showIcon
还没有分类,需<span className="empty-tree-btn">引用课程分类</span> treeData={treeData}
哦~ />
</div> </div>
)}
{this.state.NewEditQuestionBankCategory} {this.state.NewEditQuestionBankCategory}
{this.state.ImportCourseCategory} {this.state.ImportCourseCategory}
</div> </div>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-22 12:02:34 * @Date: 2021-02-22 12:02:34
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-03-04 16:59:07 * @LastEditTime: 2021-03-11 20:29:44
* @Description: 助学工具-题库-题库主页面侧边栏样式 * @Description: 助学工具-题库-题库主页面侧边栏样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -22,34 +22,38 @@ ...@@ -22,34 +22,38 @@
.sider-btn { .sider-btn {
margin-bottom: 16px; margin-bottom: 16px;
} }
.empty-tree-tip {
text-align: center; .sider-tree {
margin-top: 100%; width: 266px;
.empty-tree-btn { overflow: scroll;
color: #ffb714; height: calc(100vh - 300px);
cursor: pointer; .empty-tree-tip {
text-align: center;
margin-top: 100%;
.empty-tree-btn {
color: #ffb714;
cursor: pointer;
}
} }
} .ant-tree.ant-tree-directory {
.ant-tree.ant-tree-directory { font-size: 14px;
font-size: 14px; font-weight: 400;
font-weight: 400;
color: #666666;
.anticon {
color: #666666; color: #666666;
} width: 260px;
.ant-tree-treenode { .anticon {
height: 44px;
padding: 0;
span {
line-height: 44px;
}
.ant-tree-node-content-wrapper.ant-tree-node-selected {
color: #666666; color: #666666;
} }
.ant-tree-treenode {
height: 44px;
padding: 0;
span {
line-height: 44px;
white-space: nowrap;
}
.ant-tree-node-content-wrapper.ant-tree-node-selected {
color: #666666;
}
}
} }
// .ant-tree-treenode-selected:hover::before,
// .ant-tree-treenode-selected::before {
// background: rgb(255 251 240);
// }
} }
} }
...@@ -73,7 +73,7 @@ class QuestionEditor extends Component { ...@@ -73,7 +73,7 @@ class QuestionEditor extends Component {
renderEditor() { renderEditor() {
const { editorId } = this.state; const { editorId } = this.state;
const { detail = {}, onChange, bindChangeContent } = this.props; const { detail = {}, detailInfo = {}, onChange, bindChangeContent } = this.props;
const editorRoot = new E( const editorRoot = new E(
`#editor${editorId}_tabbar`, `#editor${editorId}_tabbar`,
`#editor${editorId}_content` `#editor${editorId}_content`
...@@ -103,15 +103,15 @@ class QuestionEditor extends Component { ...@@ -103,15 +103,15 @@ class QuestionEditor extends Component {
str = str.replace(/<\/?[^>]*>/g, ""); str = str.replace(/<\/?[^>]*>/g, "");
str = str.replace(/[ | ]*\n/g, "\n"); str = str.replace(/[ | ]*\n/g, "\n");
str = str.replace(/\&nbsp\;/gi, " "); str = str.replace(/\&nbsp\;/gi, " ");
str = str.replace(/[\r\n]/g,"");
if (str.length > 1000) { if (str.length > 1000) {
str = str.substring(0, 1000); str = str.substring(0, 1000);
message.error("内容过长,不能超过1000字"); message.error("内容过长,不能超过1000字");
} }
return str; return str
}; };
editorRoot.customConfig.onchange = (html) => { editorRoot.customConfig.onchange = (html) => {
console.log(html)
const { focusFlag } = this.state; const { focusFlag } = this.state;
const textLength = editorRoot.txt.text().replace(/\&nbsp\;/gi, " ") const textLength = editorRoot.txt.text().replace(/\&nbsp\;/gi, " ")
.length; .length;
...@@ -119,15 +119,14 @@ class QuestionEditor extends Component { ...@@ -119,15 +119,14 @@ class QuestionEditor extends Component {
? html.match(/<img/g).length * 2 ? html.match(/<img/g).length * 2
: 0; : 0;
const contentLength = imgLength + textLength; const contentLength = imgLength + textLength;
const pHeight = document.getElementById(`editor${editorId}_content`) const divHeight = document.getElementById(`editor${editorId}_content`)
.firstChild.firstChild.offsetHeight; .firstChild.offsetHeight;
if (pHeight > 30 || imgLength > 0) { if (divHeight > 30 || imgLength > 0) {
this.setState({ isShowSingleInput: false }); this.setState({ isShowSingleInput: false });
} else { } else {
this.setState({ isShowSingleInput: true }); this.setState({ isShowSingleInput: true });
} }
this.setState({ errorInput: contentLength > 1000 });
this.setState( this.setState(
{ contentLength, visiblePlacehold: contentLength === 0 && !focusFlag }, { contentLength, visiblePlacehold: contentLength === 0 && !focusFlag },
() => { () => {
...@@ -152,16 +151,16 @@ class QuestionEditor extends Component { ...@@ -152,16 +151,16 @@ class QuestionEditor extends Component {
visibleMediaBox: true, visibleMediaBox: true,
visiblePlacehold: false, visiblePlacehold: false,
}); });
bindChangeContent && bindChangeContent(this.handleChangeContent, editorRoot.textElemId)
}; };
editorRoot.create(); editorRoot.create();
this.editorRoot = editorRoot; this.editorRoot = editorRoot;
// 初始化设置内容
if (detail.content) { if (detail.content) {
const contentHtml = /^\<p/.test(detail.content) const contentHtml = /^\<p/.test(detail.content)
? detail.content ? detail.content
: `<p>${detail.content}</p>`; : `<p>${detail.content}</p>`;
editorRoot.txt.html(contentHtml); editorRoot.txt.html(detail.content);
const textLength = editorRoot.txt.text().replace(/\&nbsp\;/gi, " ") const textLength = editorRoot.txt.text().replace(/\&nbsp\;/gi, " ")
.length; .length;
const imgLength = contentHtml.match(/<img/g) const imgLength = contentHtml.match(/<img/g)
...@@ -178,7 +177,28 @@ class QuestionEditor extends Component { ...@@ -178,7 +177,28 @@ class QuestionEditor extends Component {
} }
); );
} }
bindChangeContent && bindChangeContent(this.handleChangeContent, editorRoot.textElemId); 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);
}
);
}
bindChangeContent && bindChangeContent(this.handleChangeContent);
} }
render() { render() {
...@@ -219,12 +239,13 @@ class QuestionEditor extends Component { ...@@ -219,12 +239,13 @@ class QuestionEditor extends Component {
}); });
}} }}
> >
<div <div
className="editor-box" className="editor-box"
id={`editor${editorId}_tabbar`} id={`editor${editorId}_tabbar`}
style={{ display: "none" }} style={{ display: "none" }}
></div> ></div>
<div <div
className={ className={
isShowSingleInput ? "editor-box-single " : "editor-box-multiple" isShowSingleInput ? "editor-box-single " : "editor-box-multiple"
} }
...@@ -287,4 +308,5 @@ class QuestionEditor extends Component { ...@@ -287,4 +308,5 @@ class QuestionEditor extends Component {
); );
} }
} }
export default QuestionEditor; export default QuestionEditor;
/*
* @Author: yuananting
* @Date: 2021-02-25 11:23:47
* @LastEditors: yuananting
* @LastEditTime: 2021-03-13 21:04:36
* @Description: 助学工具-题库-题目管理主页面列表数据
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import {
Table,
Switch,
ConfigProvider,
Empty,
Row,
Input,
Select,
Tooltip,
Space,
Button,
Modal,
} from "antd";
import { PageControl } from "@/components";
import "./QuestionManageContent.less";
import User from "@/common/js/user";
import QuestionBankService from "@/domains/question-bank-domain/QuestionBankService";
import _ from "underscore";
const { Search } = Input;
const questionTypeList = [
{
label: "单选题",
value: "SINGLE_CHOICE",
},
{
label: "多选题",
value: "MULTI_CHOICE",
},
{
label: "判断题",
value: "JUDGE",
},
{
label: "填空题",
value: "GAP_FILLING",
},
{
label: "不定项选择题",
value: "INDEFINITE_CHOICE",
},
];
class QuestionManageContent extends Component {
constructor(props) {
super(props);
this.state = {
query: {
current: 1,
size: 10,
order: "ACCURACY_DESC", // 排序规则[ ACCURACY_DESC, ACCURACY_ASC, CREATED_DESC, CREATED_ASC, UPDATED_DESC, UPDATED_ASC ]
categoryId: null, // 当前题库分类Id
questionName: null, // 题目名称
questionType: null, // 题目类型
source: 0,
tenantId: User.getStoreId(),
userId: User.getUserId(),
},
questionTypeList: [], // 题型列表
dataSource: [],
totalCount: 0,
};
}
componentDidMount() {
this.queryQuestionPageList();
}
shouldComponentUpdate(nextProps, nextState) {
const { selectedCategoryId } = nextProps;
const _query = this.state.query;
if (this.props.selectedCategoryId !== selectedCategoryId) {
_query.categoryId = selectedCategoryId;
this.setState({ query: _query }, () => this.queryQuestionPageList());
}
return true;
}
queryQuestionPageList = () => {
QuestionBankService.queryQuestionPageList(this.state.query).then((res) => {
const { records = [], total = 0 } = res.result;
this.setState({ dataSource: records });
});
};
handleCreateQuestionBank = () => {
window.RCHistory.push({
pathname: `/create-new-question?categoryId=${this.state.query.categoryId}`,
});
};
delCategoryConfirm(record) {
return Modal.confirm({
title: "提示",
content: "确定要删除此题目吗?",
icon: (
<span className="icon iconfont default-confirm-icon">&#xe839; </span>
),
okText: "删除",
cancelText: "取消",
onOk: () => {
this.deleteQuestion(record);
},
});
}
deleteQuestion = (record) => {
let params = {
id: record.id,
categoryId: record.categoryId,
source: 0,
tenantId: User.getStoreId(),
userId: User.getUserId(),
}
console.log(params)
QuestionBankService.delCategory(params).then(res=>{
console.log(res)
})
}
// 表头设置
parseColumns = () => {
const columns = [
{
title: "题目",
key: "questionStem",
dataIndex: "questionStem",
width: 300,
render: (val, record) => {
return <div className="record-name">{val}</div>;
},
},
{
title: "题型",
key: "questionTypeEnum",
dataIndex: "questionTypeEnum",
},
{
title: "正确率",
key: "accuracy",
dataIndex: "accuracy",
sorter: true,
},
{
title: "更新时间",
key: "updateTime",
dataIndex: "updateTime",
sorter: true,
render: (val) => {
return formatDate("YYYY-MM-DD H:i:s", val);
},
},
{
title: "操作",
key: "operate",
dataIndex: "operate",
fixed: "right",
render: (val, record) => {
return (
<div className="record-operate">
<div
className="record-operate__item"
onClick={() => console.log("预览")}
>
预览
</div>
<span className="record-operate__item split"> | </span>
<div
className="record-operate__item"
onClick={() => console.log("预览")}
>
编辑
</div>
<span className="record-operate__item split"> | </span>
<div
className="record-operate__item"
onClick={() => this.delCategoryConfirm(record)}
>
删除
</div>
</div>
);
},
},
];
return columns;
};
// 自定义表格空状态
customizeRenderEmpty = () => {
return (
<Empty
image="https://image.xiaomaiketang.com/xm/emptyTable.png"
imageStyle={{
height: 100,
}}
description={
<div>
还没有题目,快去
<span
className="empty-list-tip"
onClick={() => {
window.RCHistory.push({
pathname: "/create-question-bank",
});
}}
>
新建一个
</span>
吧!
</div>
}
></Empty>
);
};
onShowSizeChange = (current, size) => {
if (current == size) {
return;
}
let _query = this.props.query;
_query.size = size;
this.props.onChange(_query);
};
// 改变搜索条件
handleChangeQuery = (searchType, value) => {
this.setState(
{
query: {
...this.state.query,
[searchType]: value || null,
current: 1,
},
},
() => {
if (searchType === "questionName") return;
this.queryQuestionPageList();
}
);
};
render() {
const { dataSource = [], totalCount, query } = this.state;
const { current, size, categoryId } = query;
return (
<div className="question-manage-content">
<div className="question-manage-filter">
<Row type="flex" justify="space-between" align="top">
<div className="search-condition">
<div className="search-condition__item">
<span className="search-label">题目:</span>
<Search
placeholder="搜索题目名称"
style={{ width: "calc(100% - 84px)" }}
onChange={(e) => {
this.handleChangeQuery("questionName", e.target.value);
}}
onSearch={() => {
this.queryQuestionPageList();
}}
/>
</div>
<div className="search-condition__item">
<span className="search-label">题型:</span>
<Select
placeholder="请选择题目类型"
style={{ width: "calc(100% - 70px)" }}
showSearch
allowClear
filterOption={(inputVal, option) =>
option.props.children.includes(inputVal)
}
onChange={(value) => {
if(_.isEmpty(value)) {
this.handleChangeQuery("questionType", value);
}
}}
onSelect={(value) => {
this.handleChangeQuery("questionType", value);
}}
>
{_.map(questionTypeList, (item, index) => {
return (
<Select.Option value={item.value} key={item.key}>
{item.label}
</Select.Option>
);
})}
</Select>
</div>
</div>
<div className="reset-fold-area">
<Tooltip title="清空筛选">
<span
className="resetBtn iconfont icon"
onClick={this.handleReset}
>
&#xe61b;{" "}
</span>
</Tooltip>
</div>
</Row>
</div>
{!["0", null].includes(categoryId) && (
<Space size="large">
<Button type="primary" onClick={this.handleCreateQuestionBank}>
新建题目
</Button>
<Button
onClick={() => {
console.log("批量导入");
}}
>
批量导入
</Button>
</Space>
)}
<div className="question-manage-list">
<ConfigProvider renderEmpty={this.customizeRenderEmpty}>
<Table
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
onChange={this.handleChangeTable}
pagination={false}
bordered
/>
</ConfigProvider>
{totalCount > 0 && (
<div className="box-footer">
<PageControl
current={current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = { ...query, current: page + 1 };
this.props.onChange(_query);
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
)}
</div>
</div>
);
}
}
export default QuestionManageContent;
/* /*
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-25 10:42:51 * @Date: 2021-02-25 11:26:28
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-02-25 11:11:10 * @LastEditTime: 2021-03-13 16:42:41
* @Description: 助学工具-题库-题目管理主页面头部搜索样式 * @Description: 助学工具-题库-题目管理右侧内容样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
.question-manage-filter { .question-manage-content {
position: relative; .question-manage-filter {
.search-condition { position: relative;
width: calc(100% - 80px); .search-condition {
display: flex; width: calc(100% - 80px);
align-items: center; display: flex;
flex-wrap: wrap; align-items: center;
flex-wrap: wrap;
&__item { &__item {
width: 30%; width: 30%;
margin-right: 3%; margin-right: 3%;
margin-bottom: 12px; margin-bottom: 12px;
.search-label { .search-label {
vertical-align: middle; vertical-align: middle;
display: inline-block; display: inline-block;
height: 32px; height: 32px;
line-height: 32px; line-height: 32px;
}
} }
} }
.reset-fold-area {
position: absolute;
right: 12px;
.resetBtn {
color: #999999;
font-size: 18px;
margin-right: 8px;
}
.fold-btn {
font-size: 14px;
color: #666666;
line-height: 20px;
.fold-icon {
font-size: 12px;
margin-left: 4px;
}
}
}
}
.data-icon {
cursor: pointer;
} }
.reset-fold-area { .question-manage-list {
position: absolute; position: relative;
right: 12px; margin-top: 16px;
.resetBtn { .empty-list-tip {
color: #999999; color: #ffb714;
font-size: 18px; cursor: pointer;
margin-right: 8px;
} }
.fold-btn { .record-name {
font-size: 14px; word-break: break-all;
color: #666666; overflow: hidden;
line-height: 20px; text-overflow: ellipsis;
.fold-icon { display: -webkit-box;
font-size: 12px; -webkit-line-clamp: 5;
margin-left: 4px; -webkit-box-orient: vertical;
}
.record-operate {
display: flex;
&__item {
color: #5289fa;
cursor: pointer;
&.split {
margin: 0 8px;
color: #bfbfbf;
}
} }
} }
} }
} }
.data-icon {
cursor: pointer;
}
/*
* @Author: yuananting
* @Date: 2021-02-25 10:30:52
* @LastEditors: yuananting
* @LastEditTime: 2021-02-25 10:44:46
* @Description: 助学工具-题库-题目管理主页面头部搜索
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import "./QuestionManageFilter.less";
import { Row, Input, Select, Tooltip } from "antd";
const { Search } = Input;
const DEFAULT_QUERY = {
questionName: null, // 题目名称
questionType: null, // 题型
};
class QuestionManageFilter extends Component {
constructor(props) {
super(props);
this.state = {
query: { ...DEFAULT_QUERY }, // 查询条件
questionTypeList: [], // 题型列表
};
}
render() {
const {
query: { questionName, questionType },
questionTypeList,
} = this.state;
return (
<div className="question-manage-filter">
<Row type="flex" justify="space-between" align="top">
<div className="search-condition">
<div className="search-condition__item">
<span className="search-label">题目:</span>
<Search
value={questionName}
placeholder="搜索题目名称"
style={{ width: "calc(100% - 84px)" }}
onChange={(value) => {
console.log(value);
}}
/>
</div>
<div className="search-condition__item">
<span className="search-label">题型:</span>
<Select
value={questionType}
placeholder="请选择题目类型"
style={{ width: "calc(100% - 70px)" }}
showSearch
allowClear
filterOption={(input, option) => option}
onChange={(value) => {
console.log(value);
}}
>
{_.map(questionTypeList, (item, index) => {
return (
<Select.Option value={item.id} key={item.id}>
{item}
</Select.Option>
);
})}
</Select>
</div>
</div>
<div className="reset-fold-area">
<Tooltip title="清空筛选">
<span
className="resetBtn iconfont icon"
onClick={this.handleReset}
>
&#xe61b;{" "}
</span>
</Tooltip>
</div>
</Row>
</div>
);
}
}
export default QuestionManageFilter;
/*
* @Author: yuananting
* @Date: 2021-02-25 11:23:47
* @LastEditors: yuananting
* @LastEditTime: 2021-03-04 16:55:55
* @Description: 助学工具-题库-题目管理主页面列表数据
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import { Table, Switch, ConfigProvider, Empty } from "antd";
import { PageControl } from "@/components";
import "./QuestionManageList.less";
class QuestionManageList extends Component {
// 表头设置
parseColumns = () => {
const columns = [
{
title: "题目",
key: "questionName",
dataIndex: "questionName",
width: 300,
render: (val, record) => {
return <div className="record-name">{val}</div>;
},
},
{
title: "题型",
key: "questionType",
dataIndex: "questionType",
},
{
title: "正确率",
key: "accuracy",
dataIndex: "accuracy",
sorter: true,
},
{
title: "更新时间",
key: "updated",
dataIndex: "updated",
sorter: true,
render: (val) => {
return formatDate("YYYY-MM-DD H:i:s", val);
},
},
{
title: "操作",
key: "operate",
dataIndex: "operate",
fixed: "right",
render: (val, record) => {
return (
<div className="record-operate">
<div
className="operate__item"
onClick={() => console.log("预览")}
>
预览
</div>
<span className="operate__item split"> | </span>
<div
className="operate__item"
onClick={() => console.log("编辑")}
>
编辑
</div>
<span className="operate__item split"> | </span>
<div
className="operate__item"
onClick={() => console.log("删除")}
>
删除
</div>
</div>
);
},
},
];
return columns;
};
// 自定义表格空状态
customizeRenderEmpty = () => {
return (
<Empty
image="https://image.xiaomaiketang.com/xm/emptyTable.png"
imageStyle={{
height: 100,
}}
description={
<div>
还没有题目,快去
<span
className="empty-list-tip"
onClick={() => {
window.RCHistory.push({
pathname: "/create-question-bank",
});
}}
>
新建一个
</span>
吧!
</div>
}
></Empty>
);
};
onShowSizeChange = (current, size) => {
if (current == size) {
return;
}
let _query = this.props.query;
_query.size = size;
this.props.onChange(_query);
};
render() {
const { dataSource = [], totalCount, query } = this.props;
const { current, size } = query;
return (
<div className="question-manage-list">
<ConfigProvider renderEmpty={this.customizeRenderEmpty}>
<Table
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
onChange={this.handleChangeTable}
pagination={false}
bordered
/>
</ConfigProvider>
{totalCount > 0 && (
<div className="box-footer">
<PageControl
current={current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = { ...query, current: page + 1 };
this.props.onChange(_query);
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
)}
</div>
);
}
}
export default QuestionManageList;
/*
* @Author: yuananting
* @Date: 2021-02-25 11:26:28
* @LastEditors: yuananting
* @LastEditTime: 2021-02-25 11:39:08
* @Description: 助学工具-题库-题目管理主页面列表数据样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.question-manage-list {
position: relative;
margin-top: 16px;
.empty-list-tip {
color: #ffb714;
cursor: pointer;
}
.record-name {
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;
}
.record_operate {
display: flex;
&__item {
color: #5289fa;
cursor: pointer;
&.split {
margin: 0 8px;
color: #bfbfbf;
}
}
}
}
\ No newline at end of file
/*
* @Author: yuananting
* @Date: 2021-02-25 11:01:06
* @LastEditors: yuananting
* @LastEditTime: 2021-03-04 17:31:45
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import { Space, Button } from "antd";
class QuestionManageOpt extends Component {
handleCreateQuestionBank = () => {
window.RCHistory.push({
pathname: "/create-new-question",
});
};
render() {
return (
<Space size="large">
<Button type="primary" onClick={this.handleCreateQuestionBank}>
新建题目
</Button>
<Button
onClick={() => {
console.log("批量导入");
}}
>
批量导入
</Button>
</Space>
);
}
}
export default QuestionManageOpt;
...@@ -226,11 +226,11 @@ const XMAudio = (props) => { ...@@ -226,11 +226,11 @@ const XMAudio = (props) => {
<div className="process-area"> <div className="process-area">
<div <div
className="complete-area" className="complete-area"
style={{ width: `${(playedTime / totalTime) * 180}px ` }} style={{ width: `${(playedTime / totalTime) * 150}px ` }}
/> />
<div <div
className="flag" className="flag"
style={{ left: `${(playedTime / totalTime) * 180}px ` }} style={{ left: `${(playedTime / totalTime) * 150}px ` }}
onMouseDown={(e) => putDownFlag(e)} onMouseDown={(e) => putDownFlag(e)}
onMouseOver={(e)=> mouseOver(e)} onMouseOver={(e)=> mouseOver(e)}
onMouseLeave={(e)=>mouseLeave(e)} onMouseLeave={(e)=>mouseLeave(e)}
......
/* /*
* @Author: chenjianyu * @Author: chenjianyu
* @Date: 2020-09-12 17:00:44 * @Date: 2020-09-12 17:00:44
* @LastEditTime: 2021-03-01 17:25:37 * @LastEditTime: 2021-03-13 11:19:38
* @LastEditors: yuananting * @LastEditors: yuananting
* @Description: 答题模式模板 * @Description: 答题模式模板
* @Copyright © 2020 杭州杰竞科技有限公司 版权所有 * @Copyright © 2020 杭州杰竞科技有限公司 版权所有
...@@ -44,6 +44,46 @@ export function defineQuestionData(questionType) { ...@@ -44,6 +44,46 @@ export function defineQuestionData(questionType) {
} }
} }
export function defineQuestionInfo(questionType) {
return {
questionTypeEnum: questionType, // 题目类型
questionStemList:[
{
contentType: "QUESTION_STEM", // 内容类型(默认题干)
content: "", // 内容
type: "RICH_TEXT", // 内容项形式(0:富文本 1:文字 2:图片 3:语音 4:视频 5文件 6.课件)
}
], // 题干
optionList: [], // 非填空题选项
gapFillingAnswerList: [
{
correctAnswerList: []
}
], //填空题填空项
questionAnswerDescList: [
{
contentType: "QUESTION_ANSWER_DESC", // 内容类型(默认解析)
content: "", // 内容
type: "RICH_TEXT", // 内容项形式(0:富文本 1:文字 2:图片 3:语音 4:视频 5文件 6.课件)
}
], //答案解析
showBox: true, // 显示录音弹窗
}
}
export function defineOptionInfo() {
return {
isCorrectAnswer: false, // 是否为正确答案选项
questionOptionContentList: [ // 选项内容
{
contentType: "QUESTION_OPTION", // 内容类型(默认选项)
content: "", // 内容
type: "RICH_TEXT", // 内容项形式(0:富文本 1:文字 2:图片 3:语音 4:视频 5文件 6.课件)
}
]
}
}
export function defineOptionData(content = '') { export function defineOptionData(content = '') {
return { return {
itemContentVOList: [{ itemContentVOList: [{
......
/*
* @Author: yuananting
* @Date: 2021-02-23 11:43:43
* @LastEditors: yuananting
* @LastEditTime: 2021-02-25 10:32:53
* @Description: 助学工具-题库-题库引用课程分类模态框
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import { Modal, Tree, Card } from "antd";
import { PageControl } from "@/components";
import "./ImportCourseCategory.less";
class ImportCourseCategory extends Component {
constructor(props) {
super(props);
this.state = {
query: {
current: 1,
size: 1,
},
totalCount: 0,
};
}
onSelect = (selectedKeys, info) => {
console.log("selected", selectedKeys, info);
};
onCheck = (checkedKeys, info) => {
console.log("onCheck", checkedKeys, info);
};
onShowSizeChange = (current, size) => {
if (current == size) {
return;
}
const _query = { ...this.props.query };
_query.size = size;
// 更新请求参数
this.setState({ _query });
// TODO请求
};
confirmImport = () => {
// 判断是否已经选择
// TODO save
this.props.close();
};
render() {
const treeData = [
{
title: "parent 1",
key: "0-0",
children: [
{
title: "parent 1-0",
key: "0-0-0",
disabled: true,
children: [
{
title: "leaf",
key: "0-0-0-0",
disableCheckbox: true,
},
{
title: "leaf",
key: "0-0-0-1",
},
],
},
{
title: "parent 1-1",
key: "0-0-1",
children: [
{
title: <span style={{ color: "#1890ff" }}>sss</span>,
key: "0-0-1-0",
},
],
},
],
},
];
const { query, totalCount } = this.state;
const { current, size } = query;
return (
<Modal
title="引用课程分类"
visible={true}
onOk={this.confirmImport}
onCancel={() => this.props.close()}
>
<div className="import-course-title">请选择需要的分类</div>
<Card size="small" title="分类名称">
<Tree
checkable
blockNode
height={200}
defaultExpandedKeys={["0-0-0", "0-0-1"]}
defaultSelectedKeys={["0-0-0", "0-0-1"]}
defaultCheckedKeys={["0-0-0", "0-0-1"]}
onSelect={this.onSelect}
onCheck={this.onCheck}
treeData={treeData}
/>
</Card>
<div className="box-footer">
<PageControl
current={current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = { ...query, current: page + 1 };
this.props.onChange(_query);
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
</Modal>
);
}
}
export default ImportCourseCategory;
/*
* @Author: yuananting
* @Date: 2021-02-23 14:15:07
* @LastEditors: yuananting
* @LastEditTime: 2021-02-23 14:23:19
* @Description: 助学工具-题库-题库引用课程分类模态框样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.ant-modal .ant-modal-content .ant-modal-body {
padding-top: 16px !important;
}
.import-course-title {
margin-bottom: 16px;
}
...@@ -2,34 +2,89 @@ ...@@ -2,34 +2,89 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-22 17:51:28 * @Date: 2021-02-22 17:51:28
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-03-04 11:47:20 * @LastEditTime: 2021-03-13 14:26:31
* @Description: 助学工具-题库-题库新建或编辑题库分类模态框 * @Description: 助学工具-题库-题库新建或编辑题库分类模态框
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React, { Component } from "react"; import React, { Component } from "react";
import { Modal, Form, Input, message } from "antd"; import { Modal, Form, Input, message } from "antd";
import User from "@/common/js/user";
import QuestionBankService from "@/domains/question-bank-domain/QuestionBankService";
import { node } from "prop-types";
class NewEditQuestionBankCategory extends Component { class NewEditQuestionBankCategory extends Component {
formRef = React.createRef(); formRef = React.createRef();
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
categoryName: null, // 分类名称 treeData: [],
categoryName:
this.props.type === "edit" ? this.props.node.categoryName : null,
}; };
} }
componentDidMount() { componentDidMount() {
this.queryCategoryTree();
// this.getSameLevelNodes(this.props.treeData); // this.getSameLevelNodes(this.props.treeData);
} }
// 查询分类树
queryCategoryTree = () => {
let query = {
source: 0,
userId: User.getUserId(),
tenantId: User.getStoreId(),
};
QuestionBankService.queryCategoryTree(query).then((res) => {
const { result = [] } = res;
this.setState({ treeData: result });
});
};
// 确定新增或编辑 // 确定新增或编辑
confirmOperate = async () => { confirmOperate = async () => {
try { const { categoryName } = this.state;
await this.formRef.current.validateFields(); const { node, addLevelType, type } = this.props;
// TODO save let params = {
this.props.close(); source: 0,
} catch (e) { tenantId: User.getStoreId(),
console.log(e); userId: User.getUserId(),
};
if (type === "new") {
//新增
params.categoryName = categoryName;
if (addLevelType === "equal") {
params.parentId = node ? node.parentId : 0
params.categoryLevel = node ? node.categoryLevel : 0;
} else {
params.parentId = node.id;
params.categoryLevel = node.categoryLevel + 1;
}
try {
await this.formRef.current.validateFields();
QuestionBankService.addCategory(params).then((res) => {
if (res.success) {
this.props.close();
}
});
} catch (e) {
console.log(e);
}
} else {
// 编辑
params.categoryId = node.id;
params.parentId = node.parentId;
params.categoryLevel = node.categoryLevel;
params.categoryName = categoryName;
try {
await this.formRef.current.validateFields();
QuestionBankService.editCategory(params).then((res) => {
if (res.success) {
this.props.close();
}
});
} catch (e) {
console.log(e);
}
} }
}; };
...@@ -40,32 +95,32 @@ class NewEditQuestionBankCategory extends Component { ...@@ -40,32 +95,32 @@ class NewEditQuestionBankCategory extends Component {
nodes.push(item); nodes.push(item);
} }
if (item.children) { if (item.children) {
nodes.push(...this.getEqualLevelNodes(item.children, parentId)) nodes.push(...this.getEqualLevelNodes(item.children, parentId));
} }
}) });
return nodes; return nodes;
} };
getChildLevelNodes = (data, id) => { getChildLevelNodes = (data, id) => {
let nodes = []; let nodes = [];
data.forEach((item) => { data.forEach((item) => {
if(item.id === id && item.children) { if (item.id === id && item.children) {
nodes.push(...item.children); nodes.push(...item.children);
} }
if (item.children) { if (item.children) {
nodes.push(...this.getChildLevelNodes(item.children, id)) nodes.push(...this.getChildLevelNodes(item.children, id));
} }
}) });
return nodes; return nodes;
} };
getSameLevelNodes = (data, type) => { getSameLevelNodes = (data, type) => {
let sameLevelNodes = []; let sameLevelNodes = [];
const { id, parentId } = this.props;
if (type === "equal") { if (type === "equal") {
let parentId = this.props.node ? this.props.node.parentId : "0";
sameLevelNodes = this.getEqualLevelNodes(data, parentId); sameLevelNodes = this.getEqualLevelNodes(data, parentId);
} else { } else {
sameLevelNodes = this.getChildLevelNodes(data, id); sameLevelNodes = this.getChildLevelNodes(data, this.props.node.id);
} }
return sameLevelNodes; return sameLevelNodes;
}; };
...@@ -107,7 +162,10 @@ class NewEditQuestionBankCategory extends Component { ...@@ -107,7 +162,10 @@ class NewEditQuestionBankCategory extends Component {
}, },
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
validator(_, value) { validator(_, value) {
let sameLevelNodes = _that.getSameLevelNodes(treeData, addLevelType); let sameLevelNodes = _that.getSameLevelNodes(
treeData,
addLevelType
);
if (_that.checkExist(sameLevelNodes, value)) { if (_that.checkExist(sameLevelNodes, value)) {
return Promise.reject("此分类名称已存在"); return Promise.reject("此分类名称已存在");
} else { } else {
...@@ -118,7 +176,7 @@ class NewEditQuestionBankCategory extends Component { ...@@ -118,7 +176,7 @@ class NewEditQuestionBankCategory extends Component {
]} ]}
> >
<Input <Input
value={categoryName} defaultValue={categoryName}
placeholder={`请输入${title},最多10个字`} placeholder={`请输入${title},最多10个字`}
maxLength={10} maxLength={10}
onChange={(e) => { onChange={(e) => {
......
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