Commit 1d242593 by chenshu

Merge branch 'feature/yuananting/20210221/question-bank-tools' into 'master'

Feature/yuananting/20210221/question bank tools

See merge request !11
parents c8d4eb3b 8ef73141
......@@ -86,7 +86,7 @@
}
.iconfont {
color: #BFBFBF;
font-size:12px;
font-size: 16px;
}
}
}
......
/*
* @Author: 吴文洁
* @Date: 2020-03-19 16:45:42
* @LastEditors: houyan
* @LastEditTime: 2020-06-24 11:24:54
* @LastEditors: yuananting
* @LastEditTime: 2021-03-25 16:04:02
* @Description:
*/
import XMEnum from './XMEnum';
......@@ -29,12 +29,12 @@ export const RECORD_ERROR = new XMEnum([
{
name: 'NotAllowedError',
title: '请允许网页使用麦克风',
content: '麦克风已被禁用,请现在浏览器设置中允许当前望重使用你的麦克风'
content: '请检查开启麦克风权限后重试'
},
{
name: 'PermissionDeniedError',
title: '请允许浏览器使用麦克风',
content: '麦克风已被禁用,请现在浏览器设置中允许使用麦克风'
content: '请检查开启麦克风权限后重试'
},
{
name: 'NotFoundError',
......
/*
* @Author: 陈剑宇
* @Date: 2020-10-28 14:27:07
* @LastEditTime: 2020-11-02 20:25:15
* @LastEditors: 陈剑宇
* @LastEditTime: 2021-03-18 10:30:41
* @LastEditors: yuananting
* @Description:
* @FilePath: /xiaomai-web-b/app/common/constants/punchClock/punchClock.js
* @symbol_custom_string_obkoro1: Copyright © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from "react";
export const DEFAULT_IMG_URL = 'https://image.xiaomaiketang.com/xm/2MWCKBNiya.png';
export const DEFAULT_CALENDAR_TEXT = '<p>亲爱的同学:</p><p>欢迎参加《麦麦教育0基础21天英语打卡活动》,大家都知道21天养成一个好习惯。在未来的21天里,老师将和同学们一起完成21个打卡任务,0基础锻炼口语发音能力,让听英语并且跟读英语成为生活中的一部分。</p><p>打卡时间:2020年6月1日-6月23日(共21天)</p><p>其中休息日:6月6日、6月7日</p><p>打卡任务:坚持21天英语打卡学习,并且分享至朋友圈</p><p>分享格式:麦麦教育0基础21天英语打卡+学生名字 +第X天 +坚持就是胜利!</p><p>打卡奖励:</p><p>①坚持完成“21天打卡”的宝贝可以获得精美小礼品!</p><p>②每天分享打卡任务到朋友圈的小宝贝,还可以获得一份大礼包哦~</p><p>老师有话说:小宝贝每天只可完成1个打卡任务,做到真正吃透后再进行下一个任务哦。</p>';
export const DEFAULT_PASS_TEXT = '<p>亲爱的同学:</p><p>欢迎参加《麦麦芭蕾形体初级课打卡活动》,我们将通过芭蕾的几个特性如开、绷、直等,使身体各部位发展均衡,宝贝们每天需学完当前课时并完成打卡,才能解锁下一个课时内容</p><p>打卡时间:2020年6月1日-6月21日(共21天)</p><p>关卡数:共15关</p><p>每日可解锁上限:2关</p><p>打卡任务:坚持初级课闯关打卡课程,并且分享至朋友圈</p><p>分享格式:麦麦芭蕾0基础形体课打卡+学生名字 +第X关 +坚持就是胜利!</p><p>打卡奖励:</p><p>①坚持完成“闯关打卡”的宝贝可以获得精美小礼品!</p><p>②每关都分享打卡任务到朋友圈的小宝贝,还可以获得一份大礼包哦~</p><p>老师有话说:小宝贝每天最多完成2个任务,做到真正吃透后再进行下一个任务哦</p>';
......@@ -112,6 +113,12 @@ export const FILE_ACCEPT = {
VOICE: 'audio/x-mpeg,audio/mp3,audio/mpeg,audio/wav,audio/x-m4a'
}
export const MEDIA_FILE_ACCEPT = {
PICTURE: 'image/jpg,image/jpeg,image/png,image/bmp,image/gif,JPG,JPEG,PNG,BMP,GIF',
VIDEO: 'audio/mp4,video/mp4,MP4',
VOICE: 'audio/x-mpeg,audio/mp3,audio/mpeg,audio/wav,audio/x-m4a,MP3'
}
export const QUESTION_FILE_ACCEPT = {
PICTURE: 'image/jpg,image/jpeg,image/png,image/gif',
VIDEO: 'audio/mp4,video/mp4',
......
@font-face {
font-family: 'iconfont'; /* project id 2223403 */
src: url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.eot');
src: url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.eot?#iefix') format('embedded-opentype'),
url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.woff2') format('woff2'),
url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.woff') format('woff'),
url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.ttf') format('truetype'),
url('//at.alicdn.com/t/font_2223403_uxtdisq90ka.svg#iconfont') format('svg');
src: url('//at.alicdn.com/t/font_2223403_droqalespbg.eot');
src: url('//at.alicdn.com/t/font_2223403_droqalespbg.eot?#iefix') format('embedded-opentype'),
url('//at.alicdn.com/t/font_2223403_droqalespbg.woff2') format('woff2'),
url('//at.alicdn.com/t/font_2223403_droqalespbg.woff') format('woff'),
url('//at.alicdn.com/t/font_2223403_droqalespbg.ttf') format('truetype'),
url('//at.alicdn.com/t/font_2223403_droqalespbg.svg#iconfont') format('svg');
}
.iconfont{
font-family:"iconfont" !important;
......
/*
* @Author: yuananting
* @Date: 2021-03-03 15:13:12
* @LastEditors: yuananting
* @LastEditTime: 2021-03-17 11:40:41
* @Description: 助学工具接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import Service from "@/common/js/service";
export function queryCategoryTree(params: object) {
return Service.Hades("public/hades/queryCategoryTree", params);
}
export function addCategory(params: object) {
return Service.Hades("public/hades/addCategory", params);
}
export function delCategory(params: object) {
return Service.Hades("public/hades/delCategory", params);
}
export function editCategory(params: object) {
return Service.Hades("public/hades/editCategory", params);
}
export function editCategoryTree(params: object) {
return Service.Hades("public/hades/editCategoryTree", params);
}
export function queryQuestionCategoryTree(params: object) {
return Service.Hades("public/hades/queryQuestionCategoryTree", params);
}
export function queryQuestionPageList(params: object) {
return Service.Hades("public/hades/queryQuestionPageList", params);
}
export function addQuestion(params: object) {
return Service.Hades("public/hades/addQuestion", params);
}
export function deleteQuestion(params: object) {
return Service.Hades("public/hades/deleteQuestion", params);
}
export function queryQuestionDetails(params: object) {
return Service.Hades("public/hades/queryQuestionDetails", params);
}
export function editQuestion(params: object) {
return Service.Hades("public/hades/editQuestion", params);
}
export function batchImport(params: object) {
return Service.Hades("public/hades/batchImport", params);
}
\ No newline at end of file
/*
* @Author: 陈剑宇
* @Date: 2020-05-07 14:43:01
* @LastEditTime: 2021-03-01 10:09:42
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-19 10:25:06
* @LastEditors: yuananting
* @Description:
* @FilePath: /wheat-web-demo/src/domains/basic-domain/constants.ts
*/
import { MapInterface } from '@/domains/basic-domain/interface'
// 默认是 dev 环境
const ENV: string = process.env.DEPLOY_ENV || 'dev';
const ENV: string = process.env.DEPLOY_ENV || 'gray';
console.log("process.env.DEPLOY_ENV",process)
const BASIC_HOST_MAP: MapInterface = {
dev: 'https://dev-heimdall.xiaomai5.com/',
......
/*
* @Author: yuananting
* @Date: 2021-03-11 11:34:37
* @LastEditors: yuananting
* @LastEditTime: 2021-03-16 15:12:09
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import { queryCategoryTree, addCategory, delCategory, editCategory, editCategoryTree, queryQuestionCategoryTree, addQuestion, queryQuestionPageList, deleteQuestion, queryQuestionDetails, editQuestion, batchImport } 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);
}
// 预览题目
static queryQuestionDetails(params: any) {
return queryQuestionDetails(params);
}
// 编辑题目
static editQuestion(params: any) {
return editQuestion(params);
}
// 批量导入
static batchImport(params: any) {
return batchImport(params);
}
}
\ No newline at end of file
<!--
* @Author: 吴文洁
* @Date: 2020-08-24 12:20:57
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-17 19:42:47
* @LastEditors: yuananting
* @LastEditTime: 2021-03-24 19:34:13
* @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有
-->
......@@ -25,7 +25,7 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_uxtdisq90ka.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_325yz7wxu2d.css">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
......
<!--
* @Author: 吴文洁
* @Date: 2020-08-24 12:20:57
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-11 15:28:14
* @LastEditors: yuananting
* @LastEditTime: 2021-03-24 19:34:26
* @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有
-->
......@@ -25,7 +25,7 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_uxtdisq90ka.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_325yz7wxu2d.css">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
......
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:07:47
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-12 10:12:23
* @LastEditors: yuananting
* @LastEditTime: 2021-03-24 19:34:37
* @Description: 视频课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
......
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:12:45
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-16 15:20:35
* @LastEditors: yuananting
* @LastEditTime: 2021-03-24 19:34:48
* @Description: 视频课-列表模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
......
......@@ -526,6 +526,7 @@ class SelectPrepareFileModal extends React.Component {
footer={footer}
width={680}
maskClosable={false}
destroyOnClose={true}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
onCancel={this.handleClose}
className="select-prepare-file-modal"
......
/*
* @Author: wufan
* @Date: 2020-11-30 10:47:38
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-11 15:30:00
* @LastEditors: yuananting
* @LastEditTime: 2021-03-18 11:29:43
* @Description: web店铺banner页面
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......
/*
* @Author: yuananting
* @Date: 2021-02-25 13:52:01
* @LastEditors: yuananting
* @LastEditTime: 2021-03-18 09:32:11
* @Description: 助学工具-题库-题目管理-新增题目样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.add-new-question {
position: relative !important;
.box {
margin-bottom: 66px !important;
.ant-tabs {
color: #666666;
}
}
.footer {
position: fixed;
bottom: 0;
height: 58px;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 252px;
background: #fff;
border-top: 1px solid #e8e8e8;
z-index: 999;
.ant-btn {
margin-left: 10px;
}
}
}
/*
* @Author: yuananting
* @Date: 2021-02-21 17:51:01
* @LastEditors: yuananting
* @LastEditTime: 2021-03-24 13:55:56
* @Description: 助学工具-题库-题库主页面
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import "./QuestionBankIndex.less";
import QuestionBankSider from "./components/QuestionBankSider";
import QuestionManageContent from "./components/QuestionManageContent";
class QuestionBankIndex extends Component {
constructor(props) {
super(props);
this.state = {
selectedCategoryId: "",
};
}
componentDidMount() {}
getCategoryIdFromSider = (selectedCategoryId) => {
if (selectedCategoryId && selectedCategoryId.length > 0) {
this.setState({ selectedCategoryId: selectedCategoryId[0] });
}
};
updatedSiderTreeFromList = (currentTotal, updatedCategoryId) => {
this.setState({ currentTotal });
this.setState({ updatedCategoryId });
};
render() {
return (
<div className="question-bank-index page">
<div className="content-header">题目</div>
<div className="box content-body">
<div style={{borderRight: "0.5px solid #EEEEEE", paddingRight: "4px"}}>
<div className="sider">
<QuestionBankSider
getSelectedCategoryId={this.getCategoryIdFromSider.bind(this)}
currentTotal={this.state.currentTotal}
updatedCategoryId={this.state.updatedCategoryId}
/>
</div>
</div>
<div className="content">
<QuestionManageContent
updatedSiderTree={this.updatedSiderTreeFromList.bind(this)}
selectedCategoryId={this.state.selectedCategoryId}
/>
</div>
</div>
</div>
);
}
}
export default QuestionBankIndex;
/*
* @Author: yuananting
* @Date: 2021-02-21 18:27:43
* @LastEditors: yuananting
* @LastEditTime: 2021-03-24 16:15:03
* @Description: 助学工具-题库-题库主页面样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.question-bank-index {
.content-body {
display: flex;
.site-layout-background {
background: #fff;
}
.sider {
min-width: 244px;
}
.content {
width: 100%;
margin-left: 24px;
height: calc(100vh - 160px);
}
}
}
/*
* @Author: yuananting
* @Date: 2021-02-23 19:41:42
* @LastEditors: yuananting
* @LastEditTime: 2021-03-24 17:16:43
* @Description: 助学工具-题库-题目分类管理样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.question-category-manage {
position: relative;
.search-condition {
width: 30%;
margin-bottom: 16px;
.search-label {
vertical-align: middle;
display: inline-block;
height: 32px;
line-height: 32px;
}
}
.course-category-tree {
position: relative;
margin-top: 16px;
width: 900px;
border: 1px solid #E8E8E8;
.ant-tree.ant-tree-directory {
font-size: 14px;
font-weight: 400;
color: #666666;
.anticon {
color: #999999;
}
.ant-tree-treenode {
height: 44px;
padding: 0;
span {
line-height: 44px;
vertical-align: middle;
}
.ant-tree-node-content-wrapper.ant-tree-node-selected {
color: #666666;
}
.ant-tree-node-content-wrapper {
display: flex;
.ant-tree-title {
width: 100%;
display: flex;
*.node-title-div {
width: 100%;
display: flex;
justify-content: space-between;
.title-opts {
visibility: hidden;
margin-right: 16px;
}
}
}
}
}
.ant-tree-treenode-selected:hover::before,
.ant-tree-treenode-selected::before {
background: #fffbf1;
}
}
}
.xm-show-tip {
background: #f1f3f6 !important;
span.icon {
color: #bfbfbf !important;
}
}
.ant-tree .ant-tree-node-content-wrapper .ant-tree-iconEle {
line-height: 37px !important;
margin-right: 8px;
}
.ant-tree.ant-tree-directory .ant-tree-treenode:hover::before {
background-color: #F3F6FA;
}
}
.question-input-item_wrapper {
border-radius: 2px;
padding: 16px;
position: relative;
margin-bottom: 35px;
.editor-fill-box_single {
border-radius: 4px;
padding: 4px 10px;
border: 1px solid #e8e8e8;
.fill-line {
padding: 0 10px;
border-bottom: 1px solid;
}
}
.editor-fill-box_single:focus {
border: 1px solid #e8e8e8;
outline: none;
}
.fill-info {
height: 20px;
font-size: 14px;
line-height: 20px;
color: #999999;
margin-top: 8px;
.fill-info_icon {
color: #5289fa;
font-size: 14px;
padding-left: 9px;
cursor: pointer;
}
}
&__active {
border-color: #ff8534;
}
.ant-form-item-explain,
.ant-form-item-extra {
font-size: 12px;
width: 78px;
min-height: 0;
}
.question-option_control {
margin: 40px 0 !important;
}
.question-option_control > .ant-form-item-control {
margin-top: 27px;
}
.question-answer_control {
margin: 40px 0 24px !important;
.answer-tip {
font-size: 14px;
color: #cccccc;
}
}
.question-item_question-content {
position: relative;
margin: 12px 12px 12px 0;
.picture-box {
width: 88px;
height: 88px;
border-radius: 4px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #e8e8e8;
}
.audio-box {
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.1);
padding: 10px 20px;
}
.img-box {
max-width: 88px;
max-height: 88px;
border-radius: 4px;
}
.video-box {
width: 200px;
height: calc(200px * 9 / 16);
position: relative;
border-radius: 4px;
overflow: hidden;
background-color: #000;
&_content {
max-width: 100%;
max-height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
&_btn {
width: 32px;
height: 32px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -16px;
margin-left: -16px;
}
}
.icon_arrow {
position: absolute;
top: -12px;
right: -7px;
color: #bfbfbf;
cursor: pointer;
font-size: 16px;
}
.icon_sider {
color: #bfbfbf;
cursor: pointer;
font-size: 16px;
padding: 12px;
}
}
.question-item_control {
position: absolute;
top: 8px;
right: 0;
.icon {
font-size: 18px;
color: rgba(0, 0, 0, 0.4);
cursor: pointer;
&:hover {
color: #ff8534;
}
}
}
.question-item_label {
font-size: 14px;
color: #333333;
line-height: 33px;
flex-shrink: 0;
align-self: stretch;
}
.question-item_number {
display: inline-block;
background: rgba(216, 216, 216, 0.3);
border-radius: 2px;
font-size: 14px;
color: #333333;
line-height: 20px;
padding: 4px 8px;
}
.question-item_score {
font-size: 14px;
font-weight: 400;
color: #666666;
line-height: 20px;
}
.question-item_main {
display: flex;
align-items: center;
}
.question-item_main__content {
flex: 1;
margin-right: 187px;
}
// .question-item_options {
// display: flex;
// align-items: center;
// padding-bottom: 15px;
// }
.question-item_options__list {
flex: 1;
position: relative;
&::before {
content: attr(data-label);
position: absolute;
left: 15px;
transform: translateY(-100%);
font-size: 12px;
color: #666666;
line-height: 22px;
}
.ant-radio-wrapper {
margin-right: 0;
}
}
.question-item_true-false {
&::before {
transform: translateY(0);
top: 10px;
}
}
.question-item_options__content {
display: flex;
align-items: center;
}
.question-item_options__trur-false {
display: flex;
align-items: center;
height: 33px;
}
.question-item_options__sort {
flex-shrink: 0;
align-self: stretch;
line-height: 32px;
}
.question-item_options__input {
flex: 1;
}
.question-item_options__extra {
flex-shrink: 0;
padding: 0 20px 0 3px;
width: 108px;
line-height: 33px;
align-self: stretch;
.option-operate_item__icon:hover {
color: #ffb714;
}
.icon {
color: #bfbfbf;
margin-left: 10px;
cursor: pointer;
font-size: 16px;
}
}
.question-item_options__setting {
flex-shrink: 0;
width: 80px;
height: 32px;
text-align: center;
align-self: stretch;
}
.question-item_options__add {
height: 44px;
border-radius: 4px;
border: 1px dashed #e8e8e8;
font-size: 14px;
color: #5289fa;
line-height: 44px;
text-align: center;
cursor: pointer;
margin-right: 109px;
margin-left: 106px;
}
.question-item_other {
border-top: 1px solid #eeeeee;
padding-top: 15px;
}
.question-item_setting-score {
display: flex;
align-items: center;
}
.question-item_analysis {
display: flex;
align-items: center;
}
.question-item_analysis__content {
flex: 1;
.question-desc-box {
margin-top: 24px;
.desc-picture-box {
margin-bottom: 16px;
.picture-box {
position: relative;
display: inline-flex;
width: 88px;
height: 88px;
border: 1px solid #e8e8e8;
margin-right: 12px;
img {
max-width: 100%;
max-height: 100%;
border-radius: 4px;
vertical-align: middle;
width: auto;
height: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.icon_arrow {
position: absolute;
top: -12px;
right: -8px;
color: #bfbfbf;
cursor: pointer;
font-size: 16px;
}
}
}
.desc-audio-box {
margin-bottom: 28px;
.audio-box {
position: relative;
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.1);
padding: 10px 20px;
width: 320px;
margin-bottom: 12px;
.icon_sider {
color: #bfbfbf;
cursor: pointer;
font-size: 16px;
padding: 12px;
position: absolute;
top: 0px;
left: 320px;
}
}
}
.desc-video-box {
.video-box {
position: relative;
display: inline-block;
width: 208px;
height: calc(208px * 9 / 16);
position: relative;
background-color: #000;
margin: 0px 12px 12px 0;
&_content {
max-width: 100%;
max-height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
&_btn {
width: 32px;
height: 32px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -16px;
margin-left: -16px;
}
.icon_arrow {
position: absolute;
top: -12px;
right: -8px;
color: #bfbfbf;
cursor: pointer;
font-size: 16px;
}
}
}
}
}
.gap-answer-box {
display: inline-flex;
width: 100%;
padding: 6px 0;
.gap-answer-label {
margin-right: 12px;
white-space: nowrap;
}
.gap-answer-content {
background: #ffffff;
border-radius: 4px;
border: 1px solid #e8e8e8;
padding: 3px 12px;
width: calc(100% - 50px);
word-wrap: break-word;
word-break: break-all;
overflow: hidden;
margin-top: -5px;
.ant-tag {
border: none;
font-size: 14px;
line-height: 24px;
vertical-align: middle;
}
.gap-tag-input {
margin-right: 5px;
border: 1px solid rgb(165, 165, 165);
}
.ant-tag-close-icon {
font-size: 14px;
}
::-webkit-input-placeholder {
/* WebKit browsers */
color: #ff4f4f;
}
::-moz-placeholder {
/* Mozilla Firefox 19+ */
color: #ff4f4f;
}
:-ms-input-placeholder {
/* Internet Explorer 10+ */
color: #ff4f4f;
}
.ant-input-affix-wrapper {
border: none;
background: #f7f8f9;
:hover {
border-color: none;
}
width: 78px;
margin-right: 14px;
.ant-input {
background: #f7f8f9;
}
.ant-input:not(:last-child) {
padding-right: 0px !important;
}
.ant-input:focus {
border: none !important;
}
}
}
}
}
.question_skeleton {
background: linear-gradient(90deg, #f2f2f2 25%, #e6e6e6 37%, #f2f2f2 63%);
background-size: 400% 100%;
animation: question-editor_skeleton__loading 1.4s ease infinite;
}
.question_skeleton__editor {
min-height: 33px;
max-height: 140px;
overflow: hidden;
}
.question_skeleton__img {
width: 88px;
height: 88px;
}
.question_skeleton__voice {
height: 48px;
width: 280px;
}
.question_skeleton__video {
width: 100%;
height: 100%;
}
@keyframes question-editor_skeleton__loading {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}
/*
* @Author: yuananting
* @Date: 2021-02-22 10:59:43
* @LastEditors: yuananting
* @LastEditTime: 2021-03-24 14:50:08
* @Description: 助学工具-题库-题库主页面侧边栏
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import { Input, Button, Tree } from "antd";
import "./QuestionBankSider.less";
import User from "@/common/js/user";
import QuestionBankService from "@/domains/question-bank-domain/QuestionBankService";
const { Search } = Input;
const { DirectoryTree } = Tree;
class QuestionBankSider extends Component {
constructor(props) {
super(props);
const categoryId = getParameterByName("categoryId");
this.state = {
selectedKeys: categoryId
? categoryId === "null"
? ["0"]
: [categoryId]
: ["0"],
searchValue: null,
NewEditQuestionBankCategory: null, //新增或编辑分类模态框
ImportCourseCategory: null, // 引用课程分类模态框
treeData: this.props.treeData || [],
autoExpandParent: false,
};
}
componentDidMount() {
this.queryCategoryTree("change");
this.props.getSelectedCategoryId(
getParameterByName("categoryId")
? [getParameterByName("categoryId")]
: ["0"]
);
}
shouldComponentUpdate(nextProps, nextState) {
const { currentTotal, updatedCategoryId } = nextProps;
if (
this.props.currentTotal !== currentTotal &&
this.props.updatedCategoryId === updatedCategoryId
) {
this.queryCategoryTree("remain");
}
return true;
}
/** 获取树状第一级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 = (type, categoryName) => {
let query = {
source: 0,
categoryName,
userId: User.getStoreUserId(),
tenantId: User.getStoreId(),
};
QuestionBankService.queryQuestionCategoryTree(query).then((res) => {
const { categoryList = [], noCategoryCnt = 0 } = res.result;
let str = "未分类";
if (categoryName) {
this.setState({ autoExpandParent: true });
if (str.indexOf(categoryName) < 0) {
this.setState({
treeData: this.renderTreeNodes(categoryList, categoryName),
});
let nodeId = [];
Object.keys(this.state.treeMap).forEach((item) => {
nodeId.push(item);
});
if (type === "change") {
this.setState({ expandedKeys: nodeId });
}
} else {
const defaultNode = {
id: "0",
categoryName: "未分类",
categoryCount: noCategoryCnt,
parentId: "0",
categoryLevel: 0,
};
categoryList.unshift(defaultNode);
this.setState({
treeData: this.renderTreeNodes(categoryList, categoryName),
});
let nodeId = [];
Object.keys(this.state.treeMap).forEach((item) => {
nodeId.push(item);
});
if (type === "change") {
this.setState({ expandedKeys: nodeId });
}
}
} else {
this.setState({ autoExpandParent: false });
const defaultNode = {
id: "0",
categoryName: "未分类",
categoryCount: noCategoryCnt,
parentId: "0",
categoryLevel: 0,
};
categoryList.unshift(defaultNode);
this.setState({
treeData: this.renderTreeNodes(categoryList, categoryName),
});
if (type === "change") {
this.setState({ expandedKeys: [] });
}
}
});
};
getTreeMap = (data, map) => {
data.forEach((item) => {
map[item.id] = item;
if (item.sonCategoryList && item.sonCategoryList.length > 0) {
this.getTreeMap(item.sonCategoryList, map);
}
});
return map;
};
renderTreeNodes = (data, value) => {
let newTreeData = data.map((item) => {
item.title = item.categoryName;
item.key = item.id;
item.title =
!value || (value && item.categoryName.indexOf(value) > -1) ? (
<span>
{item.categoryName}{item.categoryCount}
</span>
) : (
<span style={{ opacity: 0.5 }}>
{item.categoryName}{item.categoryCount}
</span>
);
item.icon =
item.categoryName === "未分类" ? (
<img
style={{ width: "24px", height: "24px", opacity: !value || (value && item.categoryName.indexOf(value) > -1) ? 1 : 0.5 }}
src="https://image.xiaomaiketang.com/xm/defaultCategory.png"
alt=""
/>
) : (
<img
style={{ width: "24px", height: "24px", opacity: !value || (value && item.categoryName.indexOf(value) > -1) ? 1 : 0.5 }}
src="https://image.xiaomaiketang.com/xm/hasCategory.png"
alt=""
/>
);
if (item.sonCategoryList) {
item.children = this.renderTreeNodes(item.sonCategoryList, value);
}
return item;
});
let map = {};
this.setState({ treeMap: this.getTreeMap(data, map) });
return newTreeData;
};
render() {
const {
treeData,
expandedKeys,
selectedKeys,
autoExpandParent,
} = this.state;
return (
<div className="question-bank-sider">
<div className="sider-title">题目分类</div>
<Search
className="sider-search"
placeholder="搜索名称分类"
onSearch={(value) => {
this.queryCategoryTree("change", value);
}}
enterButton={<span className="icon iconfont">&#xe832;</span>}
/>
{User.getUserRole() !== "CloudLecturer" && (
<div className="sider-btn">
<Button
onClick={() => {
window.RCHistory.push({
pathname: "/question-category-manage?from=aid",
});
}}
>
分类管理
</Button>
</div>
)}
<div className="sider-tree">
<DirectoryTree
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onExpand={this.onExpand}
selectedKeys={selectedKeys}
onSelect={this.onSelect}
showIcon
treeData={treeData}
/>
</div>
{this.state.NewEditQuestionBankCategory}
{this.state.ImportCourseCategory}
</div>
);
}
}
export default QuestionBankSider;
/*
* @Author: yuananting
* @Date: 2021-02-22 12:02:34
* @LastEditors: yuananting
* @LastEditTime: 2021-03-24 15:57:12
* @Description: 助学工具-题库-题库主页面侧边栏样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.question-bank-sider {
position: relative;
.sider-title {
height: 22px;
font-size: 16px;
font-weight: 500;
color: #000000;
line-height: 22px;
margin-bottom: 16px;
}
.sider-search {
margin-bottom: 16px;
width: 230px;
}
.sider-btn {
margin-bottom: 16px;
}
.sider-tree {
width: 240px;
overflow: scroll;
height: calc(100vh - 300px);
.empty-tree-tip {
text-align: center;
margin-top: 100%;
.empty-tree-btn {
color: #ffb714;
cursor: pointer;
}
}
.ant-tree.ant-tree-directory {
font-size: 14px;
font-weight: 400;
color: #666666;
width: 234px;
.anticon {
color: #999999;
}
.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: #fffbf1;
}
}
}
.ant-tree .ant-tree-node-content-wrapper .ant-tree-iconEle {
line-height: 37px !important;
margin-right: 8px;
}
.ant-tree.ant-tree-directory .ant-tree-treenode:hover::before {
background-color: #F3F6FA;
}
}
.question-edtior_box {
position: relative;
z-index: 9;
background-color: #ffffff;
.add-fill-line {
padding: 0 10px;
border-bottom: 1px solid !important;
margin: 0 4px;
text-align: center;
border: none;
width: 54px;
background-color: transparent;
}
.editor-fill-info {
height: 20px;
font-size: 14px;
line-height: 20px;
color: #999999;
margin-top: 8px;
.ant-btn {
border: none;
.ant-input {
border-color: transparent;
}
}
.editor-fill-info_icon {
color: #5289fa !important;
font-size: 14px;
padding-left: 9px !important;
cursor: pointer;
}
}
.editor-box-single {
border-radius: 4px;
padding: 4px 0;
border: 1px solid #e8e8e8;
display: flex;
justify-content: space-between;
.editor-box_content {
width: calc(100% - 80px);
p {
display: inline-block;
}
}
.editor-limit {
padding-right: 12px;
line-height: 22px;
font-size: 12px;
color: #cccccc;
}
}
.editor-box-multiple {
border-radius: 4px;
padding: 4px 0;
border: 1px solid #e8e8e8;
.editor-box_content {
max-height: 110px;
overflow: auto;
p {
display: inline-block;
overflow-y: scroll;
}
}
.editor-limit {
text-align: right;
padding-right: 12px;
line-height: 22px;
font-size: 12px;
color: #cccccc;
padding-top: 3px;
}
}
.editor-limit-tip {
text-align: right;
color: #ec4b35;
transition: height 0.5s ease-in-out;
overflow: hidden;
}
.editor-box_content {
@media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) {
img {
zoom: calc(100% / 3);
}
}
@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) {
img {
zoom: calc(100% / 2);
}
}
}
.w-e-text p {
margin: 0;
line-height: inherit;
}
.editor-placehold {
position: absolute;
top: 0px;
left: 0;
right: 0;
font-size: 14px;
color: #cccccc;
line-height: 22px;
margin: 6px 12px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
z-index: 999;
pointer-events: none;
}
.edtior-media_box {
position: absolute;
left: 0;
padding-top: 8px;
&::before {
content: "";
border: 6px solid transparent;
border-bottom-color: #ffffff;
position: absolute;
top: -3px;
left: 50%;
margin-left: -3px;
filter: drop-shadow(-2px -2px 4px rgba(0, 0, 0, 0.06));
}
.edtior-media_list {
background: #ffffff;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.06);
border-radius: 4px;
display: flex;
align-items: center;
padding: 12px 24px;
}
.edtior-media_item {
margin-right: 24px;
cursor: pointer;
&:hover {
.edtior-media_item__icon {
color: #ffb714;
}
}
&:last-child {
margin-right: 0;
}
.edtior-media_item__icon {
color: #999999;
text-align: center;
font-size: 20px;
}
.edtior-media_item__name {
text-align: center;
font-size: 12px;
color: #999999;
line-height: 17px;
}
}
}
}
/*
* @Author: yuananting
* @Date: 2021-02-25 11:26:28
* @LastEditors: yuananting
* @LastEditTime: 2021-03-25 14:32:01
* @Description: 助学工具-题库-题目管理右侧内容样式
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
.question-manage-content {
.question-manage-filter {
position: relative;
.search-condition {
width: calc(100% - 80px);
display: flex;
align-items: center;
flex-wrap: wrap;
&__item {
width: 30%;
margin-right: 3%;
margin-bottom: 16px;
.search-label {
vertical-align: middle;
display: inline-block;
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;
}
.question-manage-list {
position: relative;
margin-top: 16px;
.empty-list-tip {
color: #ffb714;
cursor: pointer;
}
.record-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.record-operate {
display: flex;
&__item {
color: #5289fa;
cursor: pointer;
&.split {
margin: 0 8px;
color: #bfbfbf;
}
}
}
}
}
.tool-list {
.ant-tooltip-inner {
max-width: 700px !important;
}
}
.fill-line {
padding: 0 10px;
border-bottom: 1px solid;
}
/*
* @Author: sunbingqing
* @Date: 2019-07-26 14:04:00
* @Last Modified by: 吴文洁
* @Last Modified time: 2020-04-18 10:54:32
*/
import React, { useState, useEffect, useRef } from "react";
import "./XMAudio.less";
import VideoUpload from "@/core/upload";
import { string } from "prop-types";
// interface XMAudioProps {
// style?: any;
// index?: any;
// size?: number | string;
// url?: any;
// forbidParse?:boolean;
// getDuration?: (value: number) => void;
// }
let timerInterval;
const XMAudio = (props) => {
const ref = useRef();
const { style, size, getDuration } = props;
const [url, setUrl] = useState(props.url);
const [playing, setPlaying] = useState(false);
const [timer, setTimer] = useState(null);
const [playedTime, setPlayedTime] = useState(0);
const [leftTime, setLeftTime] = useState(Math.round(Number(size)));
const [totalTime, setTotalTime] = useState(Math.round(Number(size)));
const prevTimeRef = useRef();
useEffect(() => {
if(!props.forbidParse){
VideoUpload.getVideoParseRoute(props.url).then((newUrl) => {
setUrl(newUrl);
});
}
setLeftTime(Math.round(Number(size)));
setTotalTime(Math.round(Number(size)));
ref.current.addEventListener("pause", () => {
clearInterval(timer);
setPlaying(false);
setTimer(null);
if (ref.current && ref.current.ended) {
setLeftTime(totalTime);
setPlayedTime(0);
}
});
}, [props.url]);
useEffect(() => {
if (playing) {
timerInterval = setInterval(() => {
setPlayedTime((preTime) => {
prevTimeRef.current = preTime;
return prevTimeRef.current + 20;
});
if ((prevTimeRef.current + 20) % 1000 === 0) {
setLeftTime(totalTime - (prevTimeRef.current + 20));
}
if ((prevTimeRef.current + 30) >= totalTime) {
clearInterval(timerInterval);
setPlayedTime(0);
setLeftTime(totalTime);
setPlaying(false);
}
}, 20);
const audioDomList = document.querySelectorAll("audio");
for (let i = 0; i < Array.from(audioDomList).length; i++) {
if (audioDomList[i] === ref.current) {
ref.current.play();
setTimer(timerInterval)
} else {
audioDomList[i].pause();
}
}
} else {
ref.current.pause();
clearInterval(timer);
}
}, [playing]);
const audioImg = `https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/${
playing ? 1584514990496 : 1584514999661
}.png`;
function _togglePlay() {
playing ? pausePlay() : startPlay();
}
function pausePlay() {
setPlaying(false);
}
function startPlay() {
setPlaying(true);
}
function _changeTimeShow() {
let time = Math.floor(leftTime / 1000);
if (leftTime / 1000 > 60) {
const s = leftTime / 1000;
const second = Math.ceil(s % 60);
const minute = Math.floor(s / 60);
time = `${minute}'${second}`;
}
return time;
}
function _getDuration() {
const videoDiv = ref.current;
const videoSize = Math.round(videoDiv.duration) * 1000;
if (getDuration) {
setTotalTime(videoSize);
setLeftTime(videoSize);
getDuration(videoSize);
}
}
function _startTime() {
let time = Math.floor(playedTime / 1000);
let second = 0
let minute = 0;
let result = 0
if (time > 0) {
minute = Math.floor(time % 3600 / 60);
second = Math.floor((time - 60 * minute) % 60);
if (minute < 10) {
minute = "0" + minute;
}
if (second < 10) {
second = "0" + second;
}
result = minute + ':' + second
}else{
result = "00:00"
}
return result;
}
function _endTime() {
let time = Math.floor(totalTime / 1000);
let second = 0
let minute = 0;
let result = 0
if (time > 0) {
minute = Math.floor(time % 3600 / 60);
second = Math.floor((time - 60 * minute) % 60);
if (minute < 10) {
minute = "0" + minute;
}
if (second < 10) {
second = "0" + second;
}
result = minute + ':' + second
}
if(time === 0){
result = "00:00"
}
return result;
}
function putDownFlag(event) {
let dragDiv = event.target;
dragDiv.style.cursor = "pointer";
let offsetX = parseInt(dragDiv.style.left); // 获取当前的x轴距离
let innerX = event.clientX - offsetX; // 获取鼠标在方块内的x轴距
// 按住鼠标时为div修改样式
dragDiv.style.width = "16px";
dragDiv.style.height = "16px";
dragDiv.style.top = "-7px";
dragDiv.style.backGround = "linear-gradient(180deg, #FFB467 0%, #FF9143 100%)"
// 鼠标移动的时候不停的修改div的left和top值
document.onmousemove = function (event) {
dragDiv.style.left = event.clientX - innerX + "px";
// 边界判断
if (parseInt(dragDiv.style.left) <= 0) {
dragDiv.style.left = "0px";
}
if (parseInt(dragDiv.style.left) >= 154) {
dragDiv.style.left = "149px";
}
setPlayedTime(parseInt(dragDiv.style.left) / 150 * totalTime)
}
// 鼠标抬起时,清除绑定在文档上的mousemove和mouseup事件
// 否则鼠标抬起后还可以继续拖拽方块
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
// 清除border
dragDiv.style.top = "-4px";
dragDiv.style.width = "10px";
dragDiv.style.height = "10px";
}
}
function mouseOver(event){
let dragDiv = event.target;
dragDiv.style.cursor = "pointer";
dragDiv.style.width = "16px";
dragDiv.style.height = "16px";
dragDiv.style.top = "-7px";
dragDiv.style.backGround = "linear-gradient(180deg, #FFB467 0%, #FF9143 100%)"
}
function mouseLeave (event){
let dragDiv = event.target;
dragDiv.style.top = "-4px";
dragDiv.style.width = "10px";
dragDiv.style.height = "10px";
}
return (
<div className="xm-audio" style={style}>
<img src={audioImg} onClick={_togglePlay} className="audio-image" />
<div className="process-area">
<div
className="complete-area"
style={{ width: `${(playedTime / totalTime) * 150}px ` }}
/>
<div
className="flag"
style={{ left: `${(playedTime / totalTime) * 150}px ` }}
onMouseDown={(e) => putDownFlag(e)}
onMouseOver={(e)=> mouseOver(e)}
onMouseLeave={(e)=>mouseLeave(e)}
/>
</div>
<span className="sec-time">
{`${_startTime()}`}/{`${_endTime()}`}
</span>
<audio
src={url}
ref={ref}
autoPlay={false}
onCanPlayThrough={_getDuration}
/>
</div>
);
};
export default XMAudio;
/*
* @Author: Michael
* @Date: 2018-01-31 14:55:14
* @Last Modified by: mikey.zhaopeng
* @Last Modified time: 2020-03-24 10:18:43
*/
.xm-audio {
display: flex;
align-items: center;
width: 280px;
border-radius: 5px;
background-color: #ffffff;
.audio-image {
width: 28px !important;
height: 28px;
margin-left: 0px !important;
cursor: pointer;
}
.icon {
margin-left: 10px;
font-size: 25px;
margin-right: 15px;
cursor: pointer;
}
.play-icon {
color: #FC8540;
}
.sec-time{
white-space: nowrap;
color: #FF8534;
margin-left: 12px;
font-size: 13px;
}
.process-area {
height: 4px;
width: 154px;
border-radius: 3px;
margin-left: 12px;
position: relative;
background:rgba(255,133,52,0.2);
.complete-area {
height: 100%;
background-color: #FF8534;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
.flag {
position: absolute;
top: -4px;
width: 10px;
height: 10px;
border-radius: 50%;
background:linear-gradient(180deg,rgba(255,180,103,1) 0%,rgba(255,145,67,1) 100%);
}
}
}
/*
* @Author: 吴文洁
* @Date: 2020-03-18 10:01:28
* @LastEditors: yuananting
* @LastEditTime: 2021-03-22 17:24:38
* @Description: 录音组件
*/
import React, { Component } from "react";
import { Button, Modal } from "antd";
import UploadOss from "@/core/upload";
import { RECORD_ERROR } from "@/common/constants/academic";
import AudioRecorder from "./audioRecord";
import "./XMRecord.less";
class XMRecord extends Component {
constructor(props) {
super(props);
//从麦克风获取的音频流
this.mAudioContext = null;
this.mAudioFromMicrophone = null;
this.mMediaRecorder = null;
this.mChunks = [];
this.state = {
isFinished: true,
recordTime: 0,
};
}
componentDidMount() {
// 获取录音设备
this.getAudioRecorderDevice();
}
componentWillUnmount() {}
getAudioRecorderDevice = () => {
//仅用来进行录音
const constraints = { audio: true };
// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
// 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先,如果有getUserMedia的话,就获得它
var getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
// 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口
if (!getUserMedia) {
return Promise.reject(
new Error("getUserMedia is not implemented in this browser")
);
}
// 否则,为老的navigator.getUserMedia方法包裹一个Promise
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
};
}
};
openDeviceFailure = (reason) => {
const { title = "麦克风调用错误", content = "请检查麦克风是否可用" } =
RECORD_ERROR[reason.name] || {};
Modal.info({
title,
content,
okText: "我知道了",
});
};
handleCloseConfirm = () => {
this.$confirm = null;
this.forceUpdate();
};
handleStartRecord = () => {
navigator.mediaDevices
.getUserMedia({
audio: true,
})
.then((stream) => {
this.mMediaRecorder = new AudioRecorder(stream);
this.mMediaRecorder.start();
this.handleCountTime();
}, this.openDeviceFailure);
};
onProcessData = (audioData) => {
this.mChunks.push(audioData.data);
};
handleCountTime = () => {
const { maxTime = 180 } = this.props;
this.setState({ isFinished: false });
// 开始计时
this.timer = window.setInterval(() => {
const { recordTime } = this.state;
if (recordTime > maxTime - 1) {
window.clearInterval(this.timer);
this.handleFinishRecord();
return;
}
this.setState({
recordTime: this.state.recordTime + 1,
});
}, 1000);
};
handleFinishRecord = () => {
if (this.mMediaRecorder) {
this.mMediaRecorder.stop();
}
const blob = this.mMediaRecorder.upload();
UploadOss.uploadBlobToOSS(blob, window.random_string(16) + ".wav").then(
(mp3URL) => {
const { recordTime } = this.state;
this.props.onFinish(mp3URL, recordTime * 1000);
this.setState({
recordTime: 0,
isFinished: true,
});
window.clearInterval(this.timer);
}
);
};
// 格式化当前录音时长
formatRecordTime = (timeStamp) => {
let minutes = Math.floor(timeStamp / 60);
let seconds = timeStamp % 60;
minutes = minutes < 10 ? `0${minutes}` : minutes;
seconds = seconds < 10 ? `0${seconds}` : seconds;
return `${minutes}:${seconds}`;
};
handleCancel = () => {
if (this.mMediaRecorder) {
this.mMediaRecorder.stop();
}
window.clearInterval(this.timer);
this.setState(
{
recordTime: 0,
isFinished: true,
},
() => {
this.props.onCancel();
}
);
};
render() {
const { isFinished, recordTime } = this.state;
const { visible, maxTime = 180 } = this.props;
return (
<div className={`xm-record ${visible ? "visible" : "hidden"}`}>
<div className="record-left">
<div className="text">
{isFinished ? (
<img
style={{ width: 14, height: 14 }}
src="https://image.xiaomaiketang.com/xm/xCahGm2Q2J.png"
/>
) : (
<img src="https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1584607726775.png" />
)}
{isFinished ? <span>录音</span> : <span>录音中...</span>}
</div>
<div className="time">{`${this.formatRecordTime(
recordTime
)}/${this.formatRecordTime(maxTime)}`}</div>
</div>
<div className="btn-wrapper">
<Button onClick={this.handleCancel}>取消</Button>
{isFinished ? (
<Button type="primary" onClick={this.handleStartRecord}>
开始
</Button>
) : (
<Button
type="primary"
className="finish"
onClick={this.handleFinishRecord}
>
完成
</Button>
)}
</div>
</div>
);
}
}
export default XMRecord;
.xm-record {
position: fixed;
bottom: -100px;
left: 50%;
transform: translate(-50%, 0);
height: 56px;
width: 336px;
border-radius: 4px;
background: #FFF;
box-shadow: 0 2px 10px 0 rgba(0,0,0,.05);
margin-bottom: 32px;
transition: bottom 0.5s;
.record-left{
width: 100px;
height: 56px;
display: flex;
flex-direction: column;
margin-left: 24px;
align-items: flex-start;
justify-content: center;
}
&.visible {
animation: visibleRecord 0.5s;
bottom: 4px;
z-index: 10000;
}
&.hidden {
animation: hiddenRecord 0.5s;
bottom: -100px;
}
@keyframes visibleRecord {
from{
display: none;
}
to{
display: block;
}
}
@keyframes hiddenRecord {
from{
display: block;
}
to{
display: none;
}
}
.text {
height: 25px;
img {
margin-right: 3px;
}
}
.btn-wrapper {
position: absolute;
right: 24px;
top: 9px;
.ant-btn {
margin-left: 12px;
&.finish {
background-color: #FF483C !important;
}
}
}
}
\ No newline at end of file
export default function AudioRecorder(stream, config) {
config = config || {};
config.sampleBits = config.sampleBits || 16; //采样数位 8, 16
config.sampleRate = config.sampleRate || 16000; //采样率16khz
var context = new (window.webkitAudioContext || window.AudioContext)();
var audioInput = context.createMediaStreamSource(stream);
var createScript = context.createScriptProcessor || context.createJavaScriptNode;
var recorder = createScript.apply(context, [4096, 1, 1]);
var audioData = {
size: 0 //录音文件长度
, buffer: [] //录音缓存
, inputSampleRate: context.sampleRate //输入采样率
, inputSampleBits: 16 //输入采样数位 8, 16
, outputSampleRate: config.sampleRate //输出采样率
, oututSampleBits: config.sampleBits //输出采样数位 8, 16
, input: function (data) {
this.buffer.push(new Float32Array(data));
this.size += data.length;
}
, compress: function () { //合并压缩
//合并
var data = new Float32Array(this.size);
var offset = 0;
for (var i = 0; i < this.buffer.length; i++) {
data.set(this.buffer[i], offset);
offset += this.buffer[i].length;
}
//压缩
var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
var length = data.length / compression;
var result = new Float32Array(length);
var index = 0, j = 0;
while (index < length) {
result[index] = data[j];
j += compression;
index++;
}
return result;
}
, encodeWAV: function () {
var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
var bytes = this.compress();
var dataLength = bytes.length * (sampleBits / 8);
var buffer = new ArrayBuffer(44 + dataLength);
var data = new DataView(buffer);
var channelCount = 1;//单声道
var offset = 0;
var writeString = function (str) {
for (var i = 0; i < str.length; i++) {
data.setUint8(offset + i, str.charCodeAt(i));
}
}
// 资源交换文件标识符
writeString('RIFF'); offset += 4;
// 下个地址开始到文件尾总字节数,即文件大小-8
data.setUint32(offset, 36 + dataLength, true); offset += 4;
// WAV文件标志
writeString('WAVE'); offset += 4;
// 波形格式标志
writeString('fmt '); offset += 4;
// 过滤字节,一般为 0x10 = 16
data.setUint32(offset, 16, true); offset += 4;
// 格式类别 (PCM形式采样数据)
data.setUint16(offset, 1, true); offset += 2;
// 通道数
data.setUint16(offset, channelCount, true); offset += 2;
// 采样率,每秒样本数,表示每个通道的播放速度
data.setUint32(offset, sampleRate, true); offset += 4;
// 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8
data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;
// 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8
data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
// 每样本数据位数
data.setUint16(offset, sampleBits, true); offset += 2;
// 数据标识符
writeString('data'); offset += 4;
// 采样数据总数,即数据总大小-44
data.setUint32(offset, dataLength, true); offset += 4;
// 写入采样数据
if (sampleBits === 8) {
for (var i = 0; i < bytes.length; i++, offset++) {
var s = Math.max(-1, Math.min(1, bytes[i]));
var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
val = parseInt(255 / (65535 / (val + 32768)));
data.setInt8(offset, val, true);
}
} else {
for (var i = 0; i < bytes.length; i++, offset += 2) {
var s = Math.max(-1, Math.min(1, bytes[i]));
data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
}
return new Blob([data], { type: 'audio/wav' });
}
};
//开始录音
this.start = function () {
audioInput.connect(recorder);
recorder.connect(context.destination);
}
//停止
this.stop = function () {
stream.getTracks().forEach(function(track) {
track.stop();
});
recorder.disconnect();
}
//获取音频文件
this.getBlob = function () {
this.stop();
return audioData.encodeWAV();
}
//回放
this.play = function (audio) {
var blob=this.getBlob();
// saveAs(blob, "F:/3.wav");
audio.src = window.URL.createObjectURL(this.getBlob());
}
//上传
this.upload = function () {
return this.getBlob()
}
//音频采集
recorder.onaudioprocess = function (e) {
audioData.input(e.inputBuffer.getChannelData(0));
//record(e.inputBuffer.getChannelData(0));
}
}
\ No newline at end of file
/*
* @Author: chenjianyu
* @Date: 2020-09-12 17:00:44
* @LastEditTime: 2021-03-18 17:23:09
* @LastEditors: sunbingqing
* @Description: 答题模式模板
* @Copyright © 2020 杭州杰竞科技有限公司 版权所有
*/
export function defineModuleData(index) {
return {
questionInfoVOList: [], // 题目内容
taskModuleContentVOList: [], // 模块说明内容
taskModuleInfoVO: {
key: window.random_string(16),
name: `题目模块${index}`,
}
}
}
export function defineQuestionData(questionType) {
return {
taskModuleQuestionVO: { // 题目信息
key: window.random_string(16),
questionType, // 题目类型
itemTypeEnum: 'RIGHT_OR_WRONG',
questionLevel: 0,
questionTypeEnum: questionType,
// score: 1, // 本题分值
},
itemInfoVOList: [], // 选项
topicDescribeVOList: [{
content: '',
type: 'TEXT',
contentType: 'TEXT'
}], // 题干
parsingDescribeVOList: [{
content: '',
type: 'TEXT',
contentType: 'TEXT'
}], // 答案解析
lowerLevelQuestionInfoVOList: [],
showBox: true,
}
}
export function defineQuestionInfo(questionType) {
return {
questionTypeEnum: questionType, // 题目类型
questionStemList:[
{
contentType: "QUESTION_STEM", // 内容类型(默认题干)
content: "", // 内容
type: "RICH_TEXT", // 内容项形式(0:富文本 1:文字 2:图片 3:语音 4:视频 5文件 6.课件)
}
], // 题干
optionList: questionType === "JUDGE" ? [] : [], // 非填空题选项
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: 0, // 是否为正确答案选项
questionOptionContentList: [ // 选项内容
{
contentType: "QUESTION_OPTION", // 内容类型(默认选项)
content: "", // 内容
type: "RICH_TEXT", // 内容项形式(0:富文本 1:文字 2:图片 3:语音 4:视频 5文件 6.课件)
}
]
}
}
export function defineJudgeOptionInfo(content) {
return {
isCorrectAnswer: 0, // 是否为正确答案选项
questionOptionContentList: [ // 选项内容
{
contentType: "QUESTION_OPTION", // 内容类型(默认选项)
content, // 内容
type: "RICH_TEXT",
}
]
}
}
export function defineOptionData(content = '') {
return {
itemContentVOList: [{
content,
type: 'TEXT',
contentType: 'TEXT'
}], // 题目项内容
questionItemVO: { // 题目项信息
ifCorrectAnswerItem: false,
key: window.random_string(16),
}
}
}
\ No newline at end of file
/*
* @Author: zhangyi
* @Date: 2019-12-09 10:29:55
* @Last Modified by: mikey.wanghaofeng
* @Last Modified time: 2020-09-25 11:03:47
*/
import React, { Component } from "react";
import { Modal, Button, Upload, message, Spin, Progress } from "antd";
import "./BatchImportQuestionModal.less";
import SelectPrepareFileModal from "@/modules/prepare-lesson/modal/SelectPrepareFileModal";
import User from "@/common/js/user";
import QuestionBankService from "@/domains/question-bank-domain/QuestionBankService";
import { LoadingOutlined } from "@ant-design/icons";
class BatchImportQuestionModal extends Component {
constructor(props) {
super(props);
this.state = {
showSelectFileModal: false, // 云盘列表弹窗显隐
diskList: [], // 资料云盘文件列表
uploadFile: null, // 上传的文件
uploadResult: {
elapsedTime: 0,
failCnt: 0,
resourceUrl: null,
successCnt: 0,
}, // 上传返回结果
message: null,
status: "init",
};
}
// 下载题目模板
handleDownTemplate = () => {
const a = document.createElement("a");
a.href = "https://image.xiaomaiketang.com/xm/题目批量导入标准模版.xlsx";
a.click();
};
// 选择云盘资源
handleSelectExcel = (file) => {
this.setState({ uploadFile: file });
this.setState({
showSelectFileModal: false,
});
};
// 导入
handleImport = async () => {
const { uploadFile } = this.state;
if (!uploadFile) {
message.warning("请选择要导入的文件");
} else {
let flag = false;
setTimeout(() => {
if (!flag) {
this.setState({ status: "uploading" });
}
}, 1000);
let params = {
categoryId: this.props.categoryId,
resourceId: this.state.uploadFile.resourceId,
source: 0,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
};
const res = await QuestionBankService.batchImport(params);
const { result } = res;
const { bizSuccess, bizMessage } = result;
if (res) {
flag = true;
this.setState({ status: bizSuccess ? "success" : "fail" });
this.setState({ uploadResult: result });
this.setState({ message: bizMessage });
}
}
};
// 下载错误报告
downErrorReport = async () => {
const { uploadResult } = this.state;
const { resourceUrl } = uploadResult;
const a = document.createElement("a");
a.href = resourceUrl;
a.click();
};
render() {
const {
diskList,
showSelectFileModal,
uploadFile,
status,
uploadResult,
message,
} = this.state;
const { failCnt, resourceUrl, successCnt } = uploadResult;
const loadingIcon = <LoadingOutlined style={{ fontSize: 76 }} spin />;
return (
<div>
<Modal
closable={status !== "uploading"}
className="import-score-modal"
title="导入题目信息"
visible={true}
width={560}
maskClosable={false}
footer={
status === "init" && [
<Button onClick={this.props.close}>取消</Button>,
<Button type="primary" onClick={this.handleImport}>
导入
</Button>,
]
}
onCancel={() => {
this.props.close();
}}
>
{status === "init" && (
<div>
<div className="step-section">
<h4 className="step-title">1.下载导入模板,按要求填写信息</h4>
<div
className="down-btn"
style={{ fontSize: "14px" }}
onClick={this.handleDownTemplate}
>
下载题目导入模板
</div>
</div>
<div className="step-section">
<h4 className="step-title">2.选择需要导入的Excel文件</h4>
<p style={{ marginBottom: 16, color: "gray" }}>
导入限制:一次最多导入1000个题目
</p>
<Button
type="primary"
className="add-btn"
onClick={() => this.setState({ showSelectFileModal: true })}
>
{uploadFile ? "重新添加" : "添加文件"}
</Button>
{uploadFile && (
<div className="file-box">
<div>
<img
className="link-img"
src="https://image.xiaomaiketang.com/xm/link.png"
/>
<span>{uploadFile.folderName}</span>
<img
className="del-img"
src="https://image.xiaomaiketang.com/xm/del.png"
onClick={() => this.setState({ uploadFile: null })}
/>
</div>
</div>
)}
</div>
</div>
)}
{status === "uploading" && (
<div className="import-status-box">
<div className="status-content">
<Spin indicator={loadingIcon} />
<p className="status">题目导入中...</p>
<p className="status-tip">请勿关闭页面和此弹窗</p>
<p className="status-tip">
<span>以防题目导入失败</span>
</p>
</div>
</div>
)}
{status === "success" && (
<div className="import-status-box">
<div className="status-content">
<img
src="https://image.xiaomaiketang.com/xm/success.png"
alt=""
/>
<p className="status">导入完成</p>
<p className="status-tip">
<span style={{ color: successCnt === 0 ? "" : "#FF9D14" }}>
{successCnt}
</span>
个题目导入成功,
<span style={{ color: failCnt === 0 ? "" : "#FF9D14" }}>
{failCnt}
</span>
个题目导入失败。
</p>
{resourceUrl ? (
<Button
type="primary"
className="down-btn"
onClick={() => this.downErrorReport(resourceUrl)}
>
下载错误报告
</Button>
) : (
<Button
onClick={() => {
this.props.close();
}}
>
关闭
</Button>
)}
</div>
</div>
)}
{status === "fail" && (
<div className="import-status-box">
<div className="status-content">
<img src="https://image.xiaomaiketang.com/xm/fail.png" alt="" />
<p className="status">导入失败</p>
<p className="status-tip">{message}</p>
{message ? (
<Button
type="primary"
className="down-btn"
onClick={() => {
this.setState({ status: "init" });
this.setState({ uploadFile: null });
this.setState({ showSelectFileModal: true });
}}
>
重新上传文件
</Button>
) : (
<Button
onClick={() => {
this.props.close();
}}
>
关闭
</Button>
)}
</div>
</div>
)}
</Modal>
<SelectPrepareFileModal
operateType="select"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
selectTypeList={[
"vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"vnd.ms-excel",
]}
tooltip="支持文件类型:xls、xlsx"
isOpen={showSelectFileModal}
diskList={diskList}
onClose={() => {
this.setState({ showSelectFileModal: false });
}}
onSelect={this.handleSelectExcel}
/>
</div>
);
}
}
export default BatchImportQuestionModal;
@import '../../../core/mixins.less';
.import-score-modal {
.step-section {
margin-bottom: 24px;
.step-title {
font-size: 16px;
font-weight: 400;
color: #333;
}
.tip-box {
border: 1px dashed #e8e8e8;
padding: 8px;
margin-bottom: 16px;
.tip-title {
margin-bottom: 4px;
}
.tip-content {
font-size: 12px;
}
}
.add-btn {
border-radius: 2px;
font-size: 14px;
box-shadow: none;
text-shadow: none;
span {
font-weight: 400;
}
}
.file-box {
line-height: 20px;
color: #999999;
line-height: 20px;
margin-top: 10px;
span {
vertical-align: middle;
margin-right: 16px;
}
.link-img {
width: 14px;
vertical-align: middle;
margin-right: 4px;
}
.del-img {
width: 18px;
vertical-align: middle;
visibility: hidden;
}
}
.file-box :hover {
background-color: #FFF8E8;
.del-img {
visibility: visible !important;
}
}
.remark-input {
width: 304px;
margin-bottom: 8px;
}
.remark-tip {
color: #999;
font-size: 12px;
}
.down-btn {
text-align: left;
color: #5289FA;
font-size: 12px;
display: block;
margin-top: 8px;
cursor: pointer;
}
.upload-box {
width: 200px;
.ant-upload-list-item-name {
.text-overflow-ellipsis();
width:70%;
}
}
}
.import-status-box {
height:430px;
overflow: hidden;
.status-content {
margin:auto;
text-align: center;
margin-top:100px;
>img {
width: 76px;
}
.status {
font-size: 16px;
font-weight: 500;
margin: 18px 0 16px;
}
.status-tip {
line-height: 20px;
color: #666666;
margin-bottom: 16px;
.num {
color: #FC9C6B;
}
}
}
}
}
\ No newline at end of file
/*
* @Author: yuananting
* @Date: 2021-02-22 17:51:28
* @LastEditors: yuananting
* @LastEditTime: 2021-03-24 15:02:53
* @Description: 助学工具-题库-题库新建或编辑题库分类模态框
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import { Modal, Form, Input, message } from "antd";
import User from "@/common/js/user";
import QuestionBankService from "@/domains/question-bank-domain/QuestionBankService";
class NewEditQuestionBankCategory extends Component {
formRef = React.createRef();
constructor(props) {
super(props);
this.state = {
treeData: [],
categoryName:
this.props.type === "edit" ? this.props.node.categoryName : null,
};
}
componentDidMount() {
// document.getElementById("categoryName").setAttribute("style", "autocomplete","off")
this.queryCategoryTree();
}
// 查询分类树
queryCategoryTree = () => {
let query = {
source: 0,
userId: User.getStoreUserId(),
tenantId: User.getStoreId(),
};
QuestionBankService.queryCategoryTree(query).then((res) => {
const { result = [] } = res;
this.setState({ treeData: result });
});
};
// 确定新增或编辑
confirmOperate = async () => {
const { categoryName } = this.state;
const { node, addLevelType, type } = this.props;
let params = {
source: 0,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
};
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);
}
}
};
getEqualLevelNodes = (data, parentId) => {
let nodes = [];
data.forEach((item) => {
if (item.parentId === parentId) {
nodes.push(item);
}
if (item.children) {
nodes.push(...this.getEqualLevelNodes(item.children, parentId));
}
});
return nodes;
};
getChildLevelNodes = (data, id) => {
let nodes = [];
data.forEach((item) => {
if (item.id === id && item.children) {
nodes.push(...item.children);
}
if (item.children) {
nodes.push(...this.getChildLevelNodes(item.children, id));
}
});
return nodes;
};
getSameLevelNodes = (data, type) => {
let sameLevelNodes = [];
if (type === "equal") {
let parentId = this.props.node ? this.props.node.parentId : "0";
sameLevelNodes = this.getEqualLevelNodes(data, parentId);
} else {
sameLevelNodes = this.getChildLevelNodes(data, this.props.node.id);
}
return sameLevelNodes;
};
// 查询是否重名
checkExist = (sameLevelNodes, categoryName) => {
if ((sameLevelNodes.length > 0 && sameLevelNodes[0].parentId === "0")) {
if (categoryName === "未分类") {
return true;
}
}
var result = null;
sameLevelNodes.forEach((item) => {
if (result != null) {
return result;
}
if (item.categoryName === categoryName) {
result = item;
}
});
return result;
};
render() {
const { title, label, treeData, addLevelType } = this.props;
const { categoryName } = this.state;
const _that = this;
return (
<Modal
visible={true}
title={title}
onOk={this.confirmOperate}
onCancel={() => this.props.close()}
>
<Form ref={this.formRef}>
<Form.Item
name="categoryName"
label={label}
required
rules={[
{
required: true,
message: `请输入${label}`,
},
({ getFieldValue }) => ({
validator(_, value) {
let sameLevelNodes = _that.getSameLevelNodes(
treeData,
addLevelType
);
if (_that.checkExist(sameLevelNodes, value)) {
return Promise.reject("此分类名称已存在");
} else {
return Promise.resolve();
}
},
}),
]}
>
<Input
defaultValue={categoryName}
placeholder={`请输入${title},最多10个字`}
maxLength={10}
autoComplete="off"
onChange={(e) => {
this.setState({
categoryName: e.target.value,
});
}}
/>
</Form.Item>
</Form>
</Modal>
);
}
}
export default NewEditQuestionBankCategory;
.question-modal-content {
position: relative;
.question-type {
margin-bottom: 16px;
&__title {
height: 22px;
font-size: 16px;
color: #333333;
line-height: 22px;
margin-bottom: 8px;
}
&__content {
font-size: 14px;
font-weight: 400;
color: #666666;
}
}
.question-stem {
margin-bottom: 16px;
border-bottom: 1px solid #E8E8E8;
padding-bottom: 16px;
&__title {
height: 22px;
font-size: 16px;
color: #333333;
line-height: 22px;
margin-bottom: 8px;
}
&__content {
font-size: 14px;
font-weight: 400;
color: #666666;
.input-box {
margin-bottom: 8px;
* {
display: inline-block;
}
}
.picture-box {
width: 88px;
height: 88px;
border-radius: 4px;
overflow: hidden;
align-items: center;
justify-content: center;
margin-right: 12px;
position: relative;
display: inline-flex;
border: 1px solid #e8e8e8;
img {
max-width: 100%;
max-height: 100%;
border-radius: 4px;
vertical-align: middle;
width: auto;
height: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.voice-box {
margin-bottom: 12px;
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.1);
padding: 10px 20px;
width: 320px;
}
}
.add-fill-line {
padding: 0 10px;
border-bottom: 1px solid !important;
margin: 0 4px;
text-align: center;
border: none;
width: 54px;
}
}
.question-option {
margin-bottom: 16px;
&__title {
height: 22px;
font-size: 16px;
color: #333333;
line-height: 22px;
margin-bottom: 8px;
}
&__content {
font-size: 14px;
font-weight: 400;
color: #666666;
.option-box {
color: #666666;
margin-bottom: 8px;
.option-box-header {
margin-bottom: 8px;
.option-sort {
display: inline-block;
margin-right: 5px;
}
.input-box {
display: inline-block;
max-width: calc(100% - 20px);
vertical-align: top;
}
}
.picture-box {
width: 88px;
height: 88px;
border-radius: 4px;
overflow: hidden;
align-items: center;
justify-content: center;
margin-right: 12px;
position: relative;
display: inline-flex;
border: 1px solid #e8e8e8;
img {
max-width: 100%;
max-height: 100%;
border-radius: 4px;
vertical-align: middle;
width: auto;
height: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.voice-box {
margin-bottom: 12px;
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.1);
padding: 10px 20px;
width: 320px;
}
}
}
}
.question-answer {
margin-bottom: 16px;
border-bottom: 1px solid #E8E8E8;
padding-bottom: 16px;
img {
max-width: 88px;
max-height: 88px;
}
&__title {
height: 22px;
font-size: 16px;
color: #333333;
line-height: 22px;
margin-bottom: 8px;
}
&__content {
font-size: 14px;
font-weight: 400;
color: #666666;
.option-sort {
display: inline-block;
margin-right: 8px;
}
}
.question-gap-answer {
margin-bottom: 8px;
.gap-label {
display: inline-block;
width: 48px;
height: 20px;
color: #666666;
line-height: 20px;
}
.gap-content {
display: inline-block;
span {
background: #f7f8f9;
border-radius: 2px;
padding: 2px 12px;
margin-right: 8px;
}
}
}
}
.question-desc {
margin-bottom: 16px;
&__title {
height: 22px;
font-size: 16px;
color: #333333;
line-height: 22px;
margin-bottom: 8px;
}
&__content {
font-size: 14px;
font-weight: 400;
color: #666666;
.desc-input-box {
margin-bottom: 8px;
* {
display: inline-block;
}
}
.desc-picture-box {
display: inline-flex;
margin-bottom: 16px;
.picture-box {
width: 88px;
height: 88px;
border-radius: 4px;
overflow: hidden;
align-items: center;
justify-content: center;
margin-right: 12px;
position: relative;
display: inline-flex;
border: 1px solid #e8e8e8;
.img-box {
max-width: 100%;
max-height: 100%;
border-radius: 4px;
vertical-align: middle;
width: auto;
height: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
.desc-audio-box {
margin-bottom: 16px;
.audio-box {
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.1);
padding: 10px 20px;
width: 320px;
margin-bottom: 12px;
}
}
.desc-video-box {
.video-box {
position: relative;
display: inline-block;
width: 208px;
height: calc(208px * 9 / 16);
position: relative;
background-color: #000;
margin: 0px 12px 12px 0;
&_content {
max-width: 100%;
max-height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
&_btn {
width: 32px;
height: 32px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -16px;
margin-left: -16px;
}
.icon_arrow {
position: absolute;
top: -12px;
right: -8px;
color: #bfbfbf;
cursor: pointer;
font-size: 16px;
}
}
}
}
}
}
.question-preview-modal.ant-modal {
max-height: 60% !important;
}
/*
* @Author: 吴文洁
* @Date: 2020-04-29 10:26:32
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-02 15:56:22
* @LastEditors: yuananting
* @LastEditTime: 2021-03-18 11:30:15
* @Description: 内容线路由配置
*/
import Home from '@/modules/home/Home';
......@@ -25,6 +25,9 @@ import PlanPage from '@/modules/plan-manage/PlanPage';
import AddPlanPage from '@/modules/plan-manage/AddPlan';
import LearningDataPage from '@/modules/plan-manage/LearningData';
import StoreInfoPage from '@/modules/store-manage/StoreInfo';
import QuestionBankIndex from '@/modules/teach-tool/QuestionBankIndex';
import QuestionCategoryManage from '@/modules/teach-tool/QuestionCategoryManage';
import AddNewQuestion from '@/modules/teach-tool/AddNewQuestion';
const mainRoutes = [
{
......@@ -93,6 +96,21 @@ const mainRoutes = [
name: '资料云盘'
},
{
path: '/question-bank-index',
component:QuestionBankIndex,
name: '题库'
},
{
path: '/question-category-manage',
component:QuestionCategoryManage,
name: '分类管理'
},
{
path: '/create-new-question',
component:AddNewQuestion,
name: '新增题目'
},
{
path: '/switch-route',
component: SwitchRoute,
name: '登录后跳转承载页'
......
/*
* @Author: zhangleyuan
* @Date: 2021-01-19 11:27:56
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-02 15:18:12
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @Author: yuananting
* @Date: 2021-02-21 15:53:31
* @LastEditors: yuananting
* @LastEditTime: 2021-03-19 15:31:56
* @Description: 描述一下
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
export const menuList: any = [
{
......@@ -51,6 +51,18 @@ export const menuList: any = [
groupCode: "TrainPlan",
link: '/plan'
}
],
},
{
groupName: "助学工具",
groupCode: "AidTool",
icon: '&#xe828;',
children: [
{
groupName: "题库",
groupCode: "QuestionBank",
link: '/question-bank-index'
}
]
},
{
......@@ -73,10 +85,15 @@ export const menuList: any = [
groupCode: "ShopUser",
link: '/user-manage'
},
// {
// groupName: "课程分类",
// groupCode: "CourseCategory",
// link: '/course-catalog'
// },
{
groupName: "课程分类",
groupCode: "CourseCategory",
link: '/course-catalog'
groupName: "课程分类",
groupCode: "CourseCategory",
link: '/question-category-manage'
},
{
groupName: "店铺装修",
......
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