Commit 6871c062 by yuananting

feat:新增培训任务模块开发

parent a7e01f18
/*
* @Author: yuananting
* @Date: 2021-08-02 11:50:37
* @LastEditors: yuananting
* @LastEditTime: 2021-08-02 13:56:52
* @Description: 任务中心
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
const LearningContentIcon = {
LIVE: 'https://image.xiaomaiketang.com/xm/jyFhCtaKfi.png',
VOICE: 'https://image.xiaomaiketang.com/xm/2T2k5Tbmpy.png',
PICTURE: 'https://image.xiaomaiketang.com/xm/yzjNwGX6TY.png',
EAXM: 'https://image.xiaomaiketang.com/xm/fCDPp2Eenc.png',
HOMEWORK: 'https://image.xiaomaiketang.com/xm/hShsAzzppZ.png',
};
export { LearningContentIcon };
@font-face { @font-face {
font-family: 'iconfont'; /* Project id 2223403 */ font-family: 'iconfont'; /* Project id 2223403 */
src: url('//at.alicdn.com/t/font_2223403_e1xgcyaur7.woff2?t=1627810786858') format('woff2'), src: url('//at.alicdn.com/t/font_2223403_sv3ehwf2rr.woff2?t=1627903518018') format('woff2'),
url('//at.alicdn.com/t/font_2223403_e1xgcyaur7.woff?t=1627810786858') format('woff'), url('//at.alicdn.com/t/font_2223403_sv3ehwf2rr.woff?t=1627903518018') format('woff'),
url('//at.alicdn.com/t/font_2223403_e1xgcyaur7.ttf?t=1627810786858') format('truetype'); url('//at.alicdn.com/t/font_2223403_sv3ehwf2rr.ttf?t=1627903518018') format('truetype');
} }
.iconfont { .iconfont {
font-family: 'iconfont' !important; font-family: 'iconfont' !important;
......
<!-- <!--
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-24 12:20:57 * @Date: 2020-08-24 12:20:57
* @LastEditors: Please set LastEditors * @LastEditors: yuananting
* @LastEditTime: 2021-07-08 19:38:52 * @LastEditTime: 2021-08-03 14:08:12
* @Description: * @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
--> -->
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ 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="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_e1xgcyaur7.css" /> <link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_sv3ehwf2rr.css" />
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-07-05 10:47:19 * @Date: 2021-07-05 10:47:19
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-07-12 17:13:38 * @LastEditTime: 2021-08-02 17:54:13
* @Description: 描述一下咯 * @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
...@@ -49,7 +49,7 @@ class GraphicsEditor extends React.Component { ...@@ -49,7 +49,7 @@ class GraphicsEditor extends React.Component {
renderEditor() { renderEditor() {
const { editorId } = this.state; const { editorId } = this.state;
const { detail, onChange, isIntro, maxLimit, editorType } = this.props; const { detail, onChange, isIntro, maxLimit, editorType, placeholder = '请输入正文' } = this.props;
class ImageMenu extends BtnMenu { class ImageMenu extends BtnMenu {
constructor(editor) { constructor(editor) {
// data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述 // data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
...@@ -89,6 +89,7 @@ class GraphicsEditor extends React.Component { ...@@ -89,6 +89,7 @@ class GraphicsEditor extends React.Component {
this.editorInt = new E(`#editor${editorId}`); this.editorInt = new E(`#editor${editorId}`);
this.editorInt.config.focus = false; this.editorInt.config.focus = false;
this.editorInt.config.showFullScreen = !isIntro; this.editorInt.config.showFullScreen = !isIntro;
this.editorInt.config.placeholder = placeholder;
this.editorInt.menus.extend('xmimage', ImageMenu); this.editorInt.menus.extend('xmimage', ImageMenu);
!isIntro && this.editorInt.menus.extend('xmvideo', VideoMenu); !isIntro && this.editorInt.menus.extend('xmvideo', VideoMenu);
this.editorInt.config.menus = isIntro this.editorInt.config.menus = isIntro
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-07-29 13:57:03 * @Date: 2021-07-29 13:57:03
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-08-02 10:42:28 * @LastEditTime: 2021-08-02 17:25:17
* @Description: 任务中心-培训任务-新建页面 * @Description: 任务中心-培训任务-新建页面
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Button, Tabs, message, Modal } from 'antd'; import { Button, Tabs, message, Modal, Space } from 'antd';
import ShowTips from '@/components/ShowTips'; import ShowTips from '@/components/ShowTips';
import Breadcrumbs from '@/components/Breadcrumbs'; import Breadcrumbs from '@/components/Breadcrumbs';
import BasicInfo from './components/BasicInfo'; import BasicInfo from './components/BasicInfo';
...@@ -78,6 +78,7 @@ function AddTrainTask() { ...@@ -78,6 +78,7 @@ function AddTrainTask() {
</Choose> </Choose>
); );
} }
function handleGoBack() { function handleGoBack() {
window.RCHistory.goBack(); window.RCHistory.goBack();
} }
...@@ -100,14 +101,25 @@ function AddTrainTask() { ...@@ -100,14 +101,25 @@ function AddTrainTask() {
<div className='show-tips'> <div className='show-tips'>
<ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' /> <ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
</div> </div>
<Tabs centered={true} onChange={(key) => setActiveStep(key)}> <div className='header-tab'>
<TabPane tab='1 基本信息' key='BASIC_INFO'> <span className='tab-title' onClick={() => setActiveStep('BASIC_INFO')}>
<BasicInfo data={basicInfo} onChange={handleChangeBasicInfo} /> <span className={`step-icon ${activeStep === 'BASIC_INFO' ? 'active-icon' : 'default-icon'}`}>1</span>
</TabPane> <span style={{ position: 'relative' }}>
<TabPane tab='2 培训内容' key='TRAIN_CONTENT'> <span className={`${activeStep === 'BASIC_INFO' && 'active-text'}`}>基本信息</span>
<TrainContent data={taskList} onChange={handleChangeTaskInfo} /> {activeStep === 'BASIC_INFO' && <span className='active-line'></span>}
</TabPane> </span>
</Tabs> </span>
<span className='next-arrow'></span>
<span className='tab-title' onClick={() => setActiveStep('TRAIN_CONTENT')}>
<span className={`step-icon ${activeStep === 'TRAIN_CONTENT' ? 'active-icon' : 'default-icon'}`}>2</span>
<span style={{ position: 'relative' }}>
<span className={`${activeStep === 'TRAIN_CONTENT' && 'active-text'}`}>培训内容</span>
{activeStep === 'TRAIN_CONTENT' && <span className='active-line'></span>}
</span>
</span>
</div>
{activeStep === 'BASIC_INFO' && <BasicInfo data={basicInfo} onChange={handleChangeBasicInfo} />}
{activeStep === 'TRAIN_CONTENT' && <TrainContent data={taskList} onChange={handleChangeTaskInfo} />}
</div> </div>
{renderFooter()} {renderFooter()}
</div> </div>
......
.add-train-task { .add-train-task {
.header-tab {
position: relative;
padding: 20px 0 12px 0;
text-align: center;
font-size: 16px;
color: #666666;
line-height: 22px;
border-bottom: 1px solid #e8e8e8;
margin-bottom: 20px;
.tab-title {
position: relative;
cursor: pointer;
&:first-child {
margin-right: 44px;
}
.step-icon {
display: inline-block;
width: 16px;
height: 16px;
border-radius: 50%;
font-size: 12px;
text-align: center;
line-height: 14px;
margin-right: 8px;
&.default-icon {
border: 1px solid #999999;
color: #999999;
}
&.active-icon {
background-color: #2966ff;
border: 1px solid #2966ff;
color: #ffffff;
}
}
.active-text {
color: #2966ff;
}
.active-line {
position: absolute;
width: 32px;
height: 2px;
background: #2966ff;
top: 34px;
left: 16px;
z-index: 999;
}
}
&::after {
content: ' ';
height: 10px;
width: 10px;
border-width: 2px 2px 0 0;
border-color: #999999;
border-style: solid;
transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0);
position: absolute;
top: 50%;
left: calc(50% - 5px);
}
}
.footer { .footer {
position: fixed; position: fixed;
left: 196px; left: 196px;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-07-29 14:32:24 * @Date: 2021-07-29 14:32:24
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-07-30 16:23:11 * @LastEditTime: 2021-08-02 17:50:51
* @Description: 任务中心-培训任务-新建-基本信息 * @Description: 任务中心-培训任务-新建-基本信息
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
...@@ -41,80 +41,7 @@ function BasicInfo(props) { ...@@ -41,80 +41,7 @@ function BasicInfo(props) {
percentCompletePicture, percentCompletePicture,
} = data; } = data;
const isDefaultCover = true; const isDefaultCover = true;
const helpStoreUsers = [ const helpStoreUsers = [];
{
id: 1,
name: 'y阿萨德at',
},
{
id: 2,
name: 'ASFSDFAtgdf',
},
{
id: 3,
name: 'sfasfasdffff',
},
{
id: 4,
name: 'fsaf',
},
{
id: 5,
name: '示范法as',
},
{
id: 6,
name: '会尽快个何况',
},
{
id: 7,
name: '千多',
},
{
id: 8,
name: '我二合一',
},
{
id: 9,
name: '发顺丰',
},
{
id: 10,
name: '时高时低',
},
{
id: 11,
name: 'ASFSDFAtgdf',
},
{
id: 12,
name: '撒的A',
},
{
id: 13,
name: '山东高速广东省广东省怪怪的',
},
{
id: 14,
name: '而言二',
},
{
id: 15,
name: '而已',
},
{
id: 16,
name: '一条鱼',
},
{
id: 17,
name: '水电费干啥',
},
{
id: 18,
name: 'AddTrainTask',
},
];
const assignList1 = [ const assignList1 = [
{ {
...@@ -171,38 +98,6 @@ function BasicInfo(props) { ...@@ -171,38 +98,6 @@ function BasicInfo(props) {
id: 10, id: 10,
name: 'iuyti第三方感受到', name: 'iuyti第三方感受到',
}, },
{
id: 11,
name: 'ASFSDFAtgdf',
},
{
id: 12,
name: '撒的A',
},
{
id: 13,
name: '耳热耳热让他一人',
},
{
id: 14,
name: '而言二',
},
{
id: 15,
name: '而已',
},
{
id: 16,
name: '一条鱼',
},
{
id: 17,
name: '水电费干啥',
},
{
id: 18,
name: 'AddTrainTask',
},
]; ];
// 使用默认封面图 // 使用默认封面图
...@@ -312,9 +207,9 @@ function BasicInfo(props) { ...@@ -312,9 +207,9 @@ function BasicInfo(props) {
</div> </div>
</FormItem> </FormItem>
<FormItem label='指派学员' required> <FormItem label='指派学员' required>
<div className='item-btn'>添加指派对象</div> <Button style={{ display: 'block' }}>添加指派对象</Button>
{assignList1.length + assignList2.length > 0 && ( {assignList1.length + assignList2.length > 0 && (
<Space size={'12'} direction={'vertical'} className='item-obj'> <Space size={'12'} direction={'vertical'} className='select-obj'>
{assignList1.length > 0 && ( {assignList1.length > 0 && (
<div className='obj-type-container'> <div className='obj-type-container'>
<div className='type-title'>已选组织:</div> <div className='type-title'>已选组织:</div>
...@@ -339,9 +234,9 @@ function BasicInfo(props) { ...@@ -339,9 +234,9 @@ function BasicInfo(props) {
)} )}
</FormItem> </FormItem>
<FormItem label='协同人员'> <FormItem label='协同人员'>
<div className='item-btn'>添加协同者</div> <Button style={{ display: 'block' }}>添加协同者</Button>
{helpStoreUsers.length > 0 && ( {helpStoreUsers.length > 0 && (
<div className='item-obj'> <div className='select-obj'>
{_.map(helpStoreUsers, (item) => { {_.map(helpStoreUsers, (item) => {
return <Tag closable>{item.name}</Tag>; return <Tag closable>{item.name}</Tag>;
})} })}
...@@ -353,6 +248,7 @@ function BasicInfo(props) { ...@@ -353,6 +248,7 @@ function BasicInfo(props) {
maxLimit={1000} maxLimit={1000}
id='intro' id='intro'
isIntro={true} isIntro={true}
placeholder='请输入培训目的'
detail={{ detail={{
content: instro, content: instro,
}} }}
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
} }
} }
} }
.duration { .duration {
&__wrap { &__wrap {
.tips { .tips {
...@@ -59,13 +60,32 @@ ...@@ -59,13 +60,32 @@
} }
} }
} }
.ant-form-item-extra {
.select-obj {
width: 600px;
max-height: 90px;
overflow-y: scroll;
padding: 12px;
border-radius: 4px;
margin-top: 10px;
border: 1px solid #e8e8e8;
color: #666666;
.obj-type-container {
display: flex;
overflow-wrap: normal;
.type-title {
width: 70px;
flex-shrink: 0;
}
}
}
.learning-model-tips { .learning-model-tips {
margin-top: 8px; margin-top: 8px;
color: #999999; color: #999999;
line-height: 20px; line-height: 20px;
} }
}
.ant-form-item { .ant-form-item {
margin-bottom: 24px !important; margin-bottom: 24px !important;
.ant-form-item-label > label { .ant-form-item-label > label {
...@@ -85,26 +105,4 @@ ...@@ -85,26 +105,4 @@
font-size: 14px; font-size: 14px;
} }
} }
.item-btn {
color: #2966ff;
}
.item-obj {
width: 600px;
max-height: 90px;
overflow-y: scroll;
padding: 12px;
border-radius: 4px;
margin-top: 10px;
border: 1px solid #e8e8e8;
color: #666666;
.obj-type-container {
display: flex;
overflow-wrap: normal;
.type-title {
width: 70px;
flex-shrink: 0;
}
}
}
} }
/*
* @Author: yuananting
* @Date: 2021-08-03 10:47:59
* @LastEditors: yuananting
* @LastEditTime: 2021-08-03 14:08:56
* @Description: 编辑培训任务-失效课程
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { List } from 'antd';
import './ExpiredCourseList.less';
import { LearningContentIcon } from '@/common/constants/academic/taskEnum';
function ExpiredCourseList(props) {
const expiredCourseList = [1, 2, 3, 4];
return (
<div className='expired-list-page'>
<div className='tip'>为了不影响学员学习,「未成功开课」的课程已从任务中移出,具体课程如下所示:</div>
<List
dataSource={expiredCourseList}
renderItem={(item) => (
<List.Item>
<div className='item-detail'>
<span className='icon iconfont'>&#xe80b;</span>
<span className='content-status'>未成功开课</span>
<span className='stage-name'>阶段一、</span>
<span className='content-name'>
<img src={LearningContentIcon['LIVE']} />
<span>2.1 入门培训任务</span>
</span>
<span className='teacher-name'>张老师</span>
<span className='split'>|</span>
<span className='course-time'>2020-12-12 09:00~10:00</span>
<span className='del-btn'>删除记录</span>
</div>
</List.Item>
)}
/>
</div>
);
}
export default ExpiredCourseList;
.expired-list-page {
.tip {
color: #999999;
line-height: 20px;
margin-top: 8px;
margin-bottom: 16px;
}
.ant-list-item {
padding: 16px;
border: none !important;
&:nth-child(even) {
background: #ffffff;
}
&:nth-child(odd) {
background: #f7f8f9;
}
.item-detail {
display: inline-flex;
width: 100%;
height: 20px;
.icon {
font-size: 14px;
color: #ff4f4f;
margin-right: 4px;
}
.content-status {
color: #999999;
margin-right: 24px;
}
.stage-name {
color: #333333;
margin-right: 12px;
}
.content-name {
color: #333333;
margin-right: 12px;
vertical-align: middle;
> * {
vertical-align: middle;
}
img {
width: 20px;
height: 20px;
margin-right: 12px;
}
}
.teacher-name,
.course-time {
color: #999999;
}
.split {
margin-left: 12px;
margin-right: 12px;
color: #999999;
}
.del-btn {
margin-left: auto;
color: #2966ff;
cursor: pointer;
}
}
}
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-08-01 17:28:30 * @Date: 2021-08-01 17:28:30
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-08-02 11:09:50 * @LastEditTime: 2021-08-02 14:26:43
* @Description: 新建培训任务-关联课程抽屉 * @Description: 新建培训任务-关联课程抽屉
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
...@@ -553,7 +553,6 @@ class RelatedCourseDrawer extends Component { ...@@ -553,7 +553,6 @@ class RelatedCourseDrawer extends Component {
pictureTotalCount, pictureTotalCount,
videoCourseDivision, videoCourseDivision,
} = this.state; } = this.state;
const { visible } = this.props;
return ( return (
<Drawer <Drawer
...@@ -562,7 +561,7 @@ class RelatedCourseDrawer extends Component { ...@@ -562,7 +561,7 @@ class RelatedCourseDrawer extends Component {
maskClosable={false} maskClosable={false}
closable={true} closable={true}
onClose={this.props.onClose} onClose={this.props.onClose}
visible={visible} visible={true}
mask mask
className='related-course-drawer'> className='related-course-drawer'>
<div> <div>
...@@ -873,7 +872,18 @@ class RelatedCourseDrawer extends Component { ...@@ -873,7 +872,18 @@ class RelatedCourseDrawer extends Component {
</div> </div>
<div className='footer shrink-footer'> <div className='footer shrink-footer'>
<Button onClick={this.props.onClose}>取消</Button> <Button onClick={this.props.onClose}>取消</Button>
<Button type='primary'>保存</Button> <Button
type='primary'
onClick={() =>
this.props.onSelect([
...this.handleSelectVideo(selectVideo.internal),
...this.handleSelectVideo(selectVideo.external),
...this.handleSelectLive(selectLive),
...this.handleSelectPicture(selectPicture),
])
}>
确定
</Button>
</div> </div>
</Drawer> </Drawer>
); );
......
/*
* @Author: yuananting
* @Date: 2021-08-03 17:05:32
* @LastEditors: yuananting
* @LastEditTime: 2021-08-03 18:05:53
* @Description: 新建培训任务-关联考试抽屉
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useState, useRef } from 'react';
import { Drawer, Form, Input, Button, Tooltip, Switch, Radio, DatePicker, InputNumber } from 'antd';
import GraphicsEditor from '@/modules/course-manage/components/GraphicsEditor';
import moment from 'moment';
import './RelatedExamDrawer.less';
const { RangePicker } = DatePicker;
function RelatedExamDrawer(props) {
const paperInfoInit = { passScore: 60 };
const [showModal, setShowModal] = useState(false);
const [paperInfo, setPaperInfo] = useState(paperInfoInit);
const [paperId, setPaperId] = useState('');
const [passRate, setPassRate] = useState(60); //及格线
const [examStartTime, setStartTime] = useState('');
const [examEndTime, setExamEndTime] = useState('');
const [examName, setExamName] = useState('');
const [needPhone, setNeedPhone] = useState('DO_NOT_NEED_PHONE_VERIFY');
const [needOptionDisorder, setNeedOptionDisorder] = useState('OPTION_SORT');
const [resultContent, setResultContent] = useState('PASS_AND_SCORE');
const [answerAnalysis, setAnswerAnalysis] = useState('RIGHT_OR_WRONG');
const [resultShow, setResultShow] = useState('IMMEDIATELY');
const [examDesc, setExamDesc] = useState('');
const [passScore, setPassScore] = useState(100);
const [desclen, setDescLen] = useState(0);
const [check, setCheck] = useState(false);
const [getData, setGetData] = useState(false);
const [preview, setPreview] = useState(false);
const [examTotal, setExamTotal] = useState(0);
const [examList, setExamList] = useState([]);
const request = useRef(false);
const { match } = props;
const [examDuration, setExamDuration] = useState(undefined);
function disabledDate(current) {
// Can not select days before today and today
return current && current < moment().startOf('day');
}
function queryExamList() {
let param = {
current: 1,
size: 9999,
order: 'EXAM_START_TIME_DESC',
userId: User.getStoreUserId(),
tenantId: User.getStoreId(),
source: 0,
};
Service.Hades('public/hades/queryExamPageList', param).then((res) => {
const { result = {} } = res;
setExamList(result.records || []);
});
}
function queryExamDetail() {
Service.Hades('public/hades/queryExamDetail', {
examId: match.params.id,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0,
}).then((res) => {
const { result } = res;
setPaperInfo(result.examPaper);
setPaperId(result.examPaper.paperId);
setStartTime(props.type === 'edit' ? result.examStartTime : '');
setExamEndTime(props.type === 'edit' ? result.examEndTime : '');
setExamName(props.type === 'edit' ? result.examName : `${result.examName}(复制)`);
setPassRate(result.passRate * 100);
setNeedPhone(result.needPhone);
setExamDesc(result.examDesc);
setExamDuration(result.examDuration / 60 / 1000);
setAnswerAnalysis(result.answerAnalysis);
setNeedOptionDisorder(result.needOptionDisorder);
setPassScore(result.passScore);
setResultContent(result.resultContent);
setResultShow(result.resultShow);
setGetData(true);
});
}
function handleSave() {
if (request.current) {
return;
}
setCheck(true);
const param = {
paperId,
startTime: examStartTime,
endTime: examEndTime,
examName,
passRate: passRate / 100,
examStartTime,
examEndTime,
examDesc,
needPhone,
needOptionDisorder,
resultContent,
answerAnalysis,
resultShow,
examDuration: (examDuration || 0) * 60 * 1000,
passScore,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0,
examId: '',
};
if (!param.examName) {
message.warning('请输入考试名称');
return;
}
if (param.examName && param.examName.length > 40) {
message.warning('考试名称最多40字');
return;
}
if (checkExist(param.examName)) {
message.warning('此考试名称已存在');
return;
}
if (!paperId) {
message.warning('请选择试卷');
return;
}
if (!passRate) {
message.warning('请输入及格线');
return;
}
if (!examStartTime || !examEndTime) {
message.warning('请选择考试起止时间');
return;
}
if (Number(examStartTime) < moment().valueOf()) {
message.warning('开始时间不能早于现在');
return;
}
if (!examDuration) {
message.warning('请输入考试时长');
return;
}
if (examStartTime + examDuration * 60 * 1000 > examEndTime) {
message.warning('考试时长不得超过考试有效期时长');
return;
}
if (desclen > 1000) {
message.warning('内容过长,不能超过1000字');
return;
}
request.current = true;
setTimeout(() => {
request.current = false;
}, 2000);
if (props.type === 'edit') {
param.examId = match.params.id;
}
Service.Hades(props.type === 'edit' ? 'public/hades/editExam' : 'public/hades/createExam', param).then((res) => {
message.success(props.type === 'edit' ? '编辑成功' : '创建成功');
switch (props.type) {
case 'organizeExam': // 试卷列表-组织考试进入
case 'newPaperToAddExam': // 组卷保存组织考试
case 'editPaperToAddExam':
window.RCHistory.push('/examination-manage-index');
break;
case 'add':
case 'edit': // 考试列表-新建或编辑
case 'copy': // 考试列表-新建或编辑
props.freshList();
props.history.goBack();
break;
}
});
}
function disabledRangeTime(date, type) {
if (moment(date).isSame(moment(), 'day')) {
return {
disabledHours: () => {
const hours = [];
for (let i = 0; i < moment().hour(); i++) {
hours.push(i);
}
return hours;
},
disabledMinutes: () => {
const currentMinute = moment().minute();
const currentHour = moment(date).hour();
const minutes = [];
if (currentHour === moment().hour()) {
for (let i = 0; i < currentMinute; i++) {
minutes.push(i);
}
}
return minutes;
},
};
}
return {
disabledHours: () => [],
disabledMinutes: () => [],
disabledSeconds: () => [],
};
}
function handleGoBack() {
Modal.confirm({
title: '确定要返回吗?',
content: '返回后,本次编辑的内容将不被保存',
okText: '确认返回',
cancelText: '留在本页',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => {
window.RCHistory.push('/examination-manage-index');
},
});
}
// 校验考试名称是否存在
function checkExist(examName) {
var result = null;
examList.length > 0 &&
examList.forEach((item) => {
if (result != null) {
return result;
}
if (props.type === 'edit') {
if (item.examName === examName && item.examId !== match.params.id) {
result = item;
}
} else {
if (item.examName === examName) {
result = item;
}
}
});
return result;
}
return (
<Drawer title='添加考试' width={720} maskClosable={false} closable={true} onClose={props.onClose} visible={true} mask className='related-exam-drawer'>
<div>
<div className='module-title'>基本信息</div>
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 20 }} layout='horizontal'>
<Form.Item label='选择试卷' validateStatus={check && !paperId ? 'error' : ''} help={check && !paperId && '请选择试卷'} required>
<Button
onClick={() => {
setShowModal(true);
}}>
{paperInfo.paperId ? '重新选择' : '选择试卷'}
</Button>
</Form.Item>
<Form.Item
label='考试名称'
validateStatus={check && (!examName || examName.length > 40 || checkExist(examName)) ? 'error' : ''}
help={check && (!examName ? '请输入考试名称' : examName.length > 40 ? '考试名称最多40字' : checkExist(examName) && '此考试名称已存在')}
required>
<Input
placeholder='请输入考试名称(40字以内)'
maxLength={40}
value={examName}
onChange={(e) => {
setExamName(e.target.value);
}}
style={{ width: 300 }}
/>
</Form.Item>
<Form.Item
label={
<div>
<span>及格线</span>
<Tooltip title='默认为选中试卷所设置的及格线,可修改'>
<span className='icon iconfont' style={{ color: '#BFBFBF', marginLeft: 4 }}>
&#xe61d;
</span>
</Tooltip>
</div>
}
style={{ marginTop: 24 }}
validateStatus={check && !passRate ? 'error' : ''}
help={check && !passRate && '请输入及格线'}
required>
<InputNumber
value={passRate}
min={0}
max={100}
onChange={(value) => {
setPassRate(parseInt(value));
}}
style={{ width: 100 }}
/>
<span style={{ marginLeft: 8 }}>%</span>
<span style={{ marginLeft: 16, color: '#999' }}>{` 总分(${paperInfo.totalScore || 0})*及格线(${passRate || 0}%)=及格分数(${passScore})`}</span>
</Form.Item>
<Form.Item label='考试时长' validateStatus={check && !examDuration ? 'error' : ''} help={check && !examDuration && '请输入考试时长'} required>
<InputNumber
value={examDuration}
max={1440}
min={1}
onChange={(value) => {
setExamDuration(parseInt(value));
}}
style={{ width: 100 }}
/>
<span style={{ marginLeft: 8 }}>分钟</span>
<span style={{ marginLeft: 16, color: '#999' }}>{` 时长不能超过1440分钟(24小时)`}</span>
</Form.Item>
<Form.Item label='考试说明' validateStatus={check && desclen > 1000 ? 'error' : ''} help={check && desclen > 1000 && '最多只能输入1000个字'}>
<GraphicsEditor
maxLimit={1000}
isIntro={true}
detail={{
content: examDesc,
}}
onChange={(val, len) => {
setExamDesc(val);
setDescLen(len);
}}
/>
</Form.Item>
<div className='module-title'>考试设置</div>
<Form.Item label='选项乱序' required>
<div style={{ display: 'flex', marginLeft: 4 }}>
<Switch
checked={needOptionDisorder == 'OPTION_RANDOM'}
onChange={(val) => {
setNeedOptionDisorder(val ? 'OPTION_RANDOM' : 'OPTION_SORT');
}}></Switch>
<div style={{ position: 'relative', left: 8, color: '#999' }}>
{needOptionDisorder == 'OPTION_RANDOM' ? '已开启,选项随机排序' : '已关闭,选项按设置顺序排序'}
</div>
</div>
</Form.Item>
<Form.Item label='考试结果内容' required>
<Radio.Group
onChange={(e) => {
setResultContent(e.target.value);
}}
value={resultContent}>
<Radio value={'PASS_AND_SCORE'}>显示考试分数和是否及格</Radio>
<Radio value={'ONLY_SCORE'}>仅显示考试分数</Radio>
<Radio value={'ONLY_PASS'}>仅显示是否及格</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label='答案与解析' required>
<Radio.Group
onChange={(e) => {
setAnswerAnalysis(e.target.value);
}}
value={answerAnalysis}>
<Radio value={'ANALYSE_AND_RIGHT_OR_WRONG'}>显示对错与解析</Radio>
<Radio value={'RIGHT_OR_WRONG'}>仅显示对错</Radio>
<Radio value={'CAN_NOT_CHECK'}>都不显示</Radio>
</Radio.Group>
</Form.Item>
</Form>
</div>
</Drawer>
);
}
export default RelatedExamDrawer;
.related-exam-drawer {
.module-title {
font-size: 16px;
color: #333333;
line-height: 22px;
margin-bottom: 24px;
}
.ant-form-item {
margin-bottom: 24px !important;
.graphics-editor-container {
width: 550px;
height: 130px;
}
}
}
...@@ -2,25 +2,61 @@ ...@@ -2,25 +2,61 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-07-30 16:33:58 * @Date: 2021-07-30 16:33:58
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-08-02 11:14:47 * @LastEditTime: 2021-08-03 17:05:23
* @Description: 任务中心-培训任务-新建-培训内容 * @Description: 任务中心-培训任务-新建-培训内容
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Form, Input, Collapse, Dropdown, Menu } from 'antd';
import { Form, Button, Input, Space, DatePicker, Radio, Tag, Col, message, Tooltip, Collapse, Dropdown, Menu, Drawer } from 'antd';
import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'; import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc';
import arrayMove from 'array-move'; import arrayMove from 'array-move';
import './TrainContent.less'; import './TrainContent.less';
import RelatedCourseDrawer from './RelatedCourseDrawer'; import RelatedCourseDrawer from './RelatedCourseDrawer';
import RelatedExamDrawer from './RelatedExamDrawer';
import ExpiredCourseList from './ExpiredCourseList';
import { LearningContentIcon } from '@/common/constants/academic/taskEnum';
const { Panel } = Collapse; const { Panel } = Collapse;
const SortableTaskContainer = sortableContainer((props) => <div {...props}></div>); const SortableTaskContainer = sortableContainer((props) => <div {...props}></div>);
const SortableTaskItem = sortableElement((props) => <div {...props}>{props.taskitem}</div>); const SortableTaskItem = sortableElement((props) => <div {...props}>{props.taskitem}</div>);
const DragHandle = sortableHandle(() => <span className='drag-btn'>::</span>); const SortableContentContainer = sortableContainer((props) => <div {...props}></div>);
const SortableContentItem = sortableElement((props) => <div {...props}>{props.contentitem}</div>);
const DragHandle = sortableHandle(() => <span className='icon iconfont drag-btn'>&#xe61e;</span>);
const courseStateShow = {
UN_START: {
title: '待开播',
},
STARTING: {
title: '直播中',
},
FINISH: {
title: '回放',
},
EXPIRED: {
title: '未成功开课',
},
};
const SortConvert = {
1: '一',
2: '二',
3: '三',
4: '四',
5: '五',
6: '六',
7: '七',
8: '八',
9: '九',
10: '十',
};
// const id = window.getParameterByName('id');
// const type = window.getParameterByName('type');
class TrainContent extends Component { class TrainContent extends Component {
constructor(props) { constructor(props) {
...@@ -28,23 +64,107 @@ class TrainContent extends Component { ...@@ -28,23 +64,107 @@ class TrainContent extends Component {
this.state = { this.state = {
dataSource: props.data, dataSource: props.data,
showCourseDrawer: false, showCourseDrawer: false,
showExamDrawer: false,
selectedTaskIndex: 0,
expiredCourseList: [], // 失效课程
showStandardDetail: false, // 是否展开高级设置
}; };
} }
componentDidMount() {
// if (type === 'edit') {
// this.getPlanDetail();
// this.getPlanCustomerState();
// }
// Bus.bind('editorLimit', (editorTextLength) => {
// setEditorTextLength(editorTextLength);
// });
}
getPlanDetail = () => {
PlanService.getTrainingPlanDetail({
planId: id,
}).then((res) => {
const {
planId,
planName,
enableState,
operateType,
operateIds,
percentCompleteLive,
percentCompleteVideo,
percentCompletePicture,
courseMediaVOS,
trainingTaskList,
} = res.result;
let coverId;
let coverUrl;
let instro;
let hasIntro = false;
courseMediaVOS.map((item) => {
switch (item.contentType) {
case 'COVER':
coverId = item.mediaContent;
coverUrl = item.mediaUrl;
break;
case 'INTRO':
hasIntro = true;
instro = item.mediaContent;
break;
default:
break;
}
return item;
});
let _selectOperatorList = [];
if (operateIds) {
_selectOperatorList = operateIds.map((item, index) => {
let _item = {};
_item.id = item;
return _item;
});
}
setTaskList(trainingTaskList);
setBasicData({
planId,
planName,
coverUrl: coverUrl || defaultCover,
coverId,
enableState,
selectOperatorList: _selectOperatorList,
instro,
operateType,
percentCompleteLive,
percentCompleteVideo,
percentCompletePicture,
});
setHasGetDetail(true);
});
};
getPlanCustomerState = () => {
PlanService.getTrainingCourseAutoCancel({
planId: id,
}).then((res) => {
const expiredCourseList = res.result;
this.setState({ expiredCourseList });
});
};
setTrianTypeOption = () => { setTrianTypeOption = () => {
return ( return (
<Menu> <Menu>
<Menu.Item key='course' onClick={() => this.setState({ showCourseDrawer: true })}> <Menu.Item key='course' onClick={() => this.setState({ showCourseDrawer: true })}>
<img className='type-option-icon' src='https://image.xiaomaiketang.com/xm/6C2GjSpnDp.png' /> <img className='type-option-icon' src='https://image.xiaomaiketang.com/xm/6C2GjSpnDp.png' />
<span>课程</span> <span className='type-option-text'>课程</span>
</Menu.Item> </Menu.Item>
<Menu.Item key='exam'> <Menu.Item key='exam' onClick={() => this.setState({ showExamDrawer: true })}>
<img className='type-option-icon' src='https://image.xiaomaiketang.com/xm/M4BEXnRWbb.png' /> <img className='type-option-icon' src='https://image.xiaomaiketang.com/xm/M4BEXnRWbb.png' />
<span>考试</span> <span className='type-option-text'>考试</span>
</Menu.Item> </Menu.Item>
<Menu.Item key='homework'> <Menu.Item key='homework'>
<img className='type-option-icon' src='https://image.xiaomaiketang.com/xm/ypWQcFWnxB.png' /> <img className='type-option-icon' src='https://image.xiaomaiketang.com/xm/ypWQcFWnxB.png' />
<span>实操作业</span> <span className='type-option-text'>实操作业</span>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
); );
...@@ -66,6 +186,23 @@ class TrainContent extends Component { ...@@ -66,6 +186,23 @@ class TrainContent extends Component {
} }
}; };
onContentSortEnd = ({ oldIndex, newIndex }, parentIndex) => {
const { dataSource } = this.state;
const _dataSource = [...dataSource];
if (oldIndex !== newIndex) {
_dataSource[parentIndex].courseList = arrayMove([].concat(dataSource[parentIndex].courseList), oldIndex, newIndex).filter((el) => !!el);
this.setState(
{
dataSource: _dataSource,
},
() => {
this.props.onChange(_dataSource);
}
);
}
};
handleRenameTaskName = (e, item) => { handleRenameTaskName = (e, item) => {
const { value } = e.target; const { value } = e.target;
const { dataSource } = this.state; const { dataSource } = this.state;
...@@ -104,25 +241,25 @@ class TrainContent extends Component { ...@@ -104,25 +241,25 @@ class TrainContent extends Component {
return Promise.resolve(); return Promise.resolve();
}; };
renderTaskInfo = (item, index) => { renderStageInfo = (item, index) => {
return ( return (
<div className='sort-task-item'> <div className='sort-stage-item'>
<div className='item-info'>
<span className='info-number'>{SortConvert[index + 1]}</span>
<Choose> <Choose>
<When condition={item.type === 'input'}> <When condition={item.type === 'input'}>
<div className='task-name-con'>
{/* <span className='number'>{index + 1}.</span> */}
<Form> <Form>
<Form.Item <Form.Item
initialValue={item.taskName} initialValue={item.taskName}
validateTrigger={['onChange', 'onBlur']} validateTrigger={['onChange', 'onBlur']}
name={['taskName']} name={['stageName']}
rules={[ rules={[
{ {
validator: (rule, value) => this.handleValidatorTaskName(rule, value), validator: (rule, value) => this.handleValidatorTaskName(rule, value),
}, },
]}> ]}>
<Input <Input
className='task-name-input' className='info-input'
style={{ width: 300 }} style={{ width: 300 }}
placeholder='请输入阶段名称' placeholder='请输入阶段名称'
maxLength={20} maxLength={20}
...@@ -137,15 +274,12 @@ class TrainContent extends Component { ...@@ -137,15 +274,12 @@ class TrainContent extends Component {
/> />
</Form.Item> </Form.Item>
</Form> </Form>
</div>
</When> </When>
<Otherwise> <Otherwise>
<div className='task-name-con'> <span className='info-text'>{item.taskName}</span>
{/* <span className='number'>{index + 1}.</span> */}
<span className='task-name'>{item.taskName}</span>
</div>
</Otherwise> </Otherwise>
</Choose> </Choose>
</div>
<span className='item-operate'> <span className='item-operate'>
<span <span
className='operate__item' className='operate__item'
...@@ -174,24 +308,58 @@ class TrainContent extends Component { ...@@ -174,24 +308,58 @@ class TrainContent extends Component {
); );
}; };
renderTaskItem = (item, index) => { renderStageItem = (item, index) => {
return ( return (
<Collapse ghost> <Collapse ghost>
<Panel header={this.renderTaskInfo(item, index)} key={index}> <Panel header={this.renderStageInfo(item, index)} key={index}>
{/* {renderTaskItem(props.iteminfo, props.index)} */} <SortableContentContainer useDragHandle disableAutoscroll helperClass='row-dragging' onSortEnd={(item) => this.onContentSortEnd(item, index)}>
{item.courseList.map((contentItem, contentIndex) => (
<SortableContentItem
contentitem={this.renderContentItem(contentItem, contentIndex, index)}
index={contentIndex}
key={contentIndex}></SortableContentItem>
))}
</SortableContentContainer>
<Dropdown <Dropdown
overlay={this.setTrianTypeOption()} overlay={this.setTrianTypeOption()}
className='add-course-btn'
onClick={() => { onClick={() => {
// this.showRelatedCourseModal(index); // this.showRelatedCourseModal(index);
}}> }}>
<span>+ 关联课程</span> <span className='add-content-btn'>+ 添加学习内容</span>
</Dropdown> </Dropdown>
</Panel> </Panel>
</Collapse> </Collapse>
); );
}; };
renderContentItem = (record, index, parentIndex) => {
const { courseState, courseName, courseType, courseChapterNum } = record;
return (
<div className='sort-content-item'>
<div className='content-info'>
<img className='type-option-icon' src={LearningContentIcon[courseType]} />
<span className='content-name'>
{parentIndex + 1}.{index + 1} {courseName}
</span>
{courseState === 'EXPIRED' && <span className='icon iconfont tip'>&#xe834;</span>}
{courseType === 'LIVE' && <span className='extra-info'>{courseStateShow[record.courseState].title}</span>}
{courseType === 'VOICE' && <span className='extra-info'>(共{courseChapterNum || 1}小节)</span>}
</div>
<div className='content-operate'>
<span
className='operate__item'
onClick={() => {
this.handleDeleteCourse(parentIndex, index);
}}>
<span className='icon iconfont'>&#xe6f6;</span>
<span className='text'>删除</span>
</span>
</div>
<DragHandle />
</div>
);
};
// 添加阶段 // 添加阶段
addStage = () => { addStage = () => {
const { dataSource } = this.state; const { dataSource } = this.state;
...@@ -219,19 +387,114 @@ class TrainContent extends Component { ...@@ -219,19 +387,114 @@ class TrainContent extends Component {
}); });
}; };
onCloseExamDrawer = () => {
this.setState({
showExamDrawer: false,
});
};
confirmSelectCourse = (selectList) => {
const { selectedTaskIndex } = this.state;
const { dataSource } = this.state;
const newData = [...dataSource];
const selectData = [...newData[selectedTaskIndex].courseList];
const _selectData = [...selectData, ...selectList];
newData[selectedTaskIndex].courseList = _selectData;
this.setState(
{
showCourseDrawer: false,
dataSource: newData,
},
() => {
this.props.onChange(newData);
}
);
};
render() { render() {
const { dataSource, showCourseDrawer } = this.state; const { dataSource, showCourseDrawer, showExamDrawer, expiredCourseList, showStandardDetail } = this.state;
return ( return (
<div className='train-content-page'>
<div className='train-content__warp'> <div className='train-content__warp'>
<SortableTaskContainer useDragHandle disableAutoscroll helperClass='row-dragging' onSortEnd={this.onTaskSortEnd}> <SortableTaskContainer useDragHandle disableAutoscroll helperClass='row-dragging' onSortEnd={this.onTaskSortEnd}>
{dataSource.map((item, index) => ( {dataSource.map((item, index) => (
<SortableTaskItem taskitem={this.renderTaskItem(item, index)} index={index} key={index}></SortableTaskItem> <SortableTaskItem taskitem={this.renderStageItem(item, index)} index={index} key={index}></SortableTaskItem>
))} ))}
</SortableTaskContainer> </SortableTaskContainer>
<div className='add-task-btn' onClick={() => this.addStage()}> <div className='add-stage-btn' onClick={() => this.addStage()}>
+ 添加阶段 + 添加阶段
</div> </div>
<RelatedCourseDrawer data={dataSource} onClose={this.onCloseCourseDrawer} visible={showCourseDrawer} /> {showCourseDrawer && <RelatedCourseDrawer data={dataSource} onClose={this.onCloseCourseDrawer} onSelect={this.confirmSelectCourse} />}
{showExamDrawer && <RelatedExamDrawer onClose={this.onCloseExamDrawer} />}
</div>
<div className='expired-info__wrap'>
<div className='module-title'>失效课程</div>
<ExpiredCourseList expiredCourseList={expiredCourseList} />
</div>
<div className='finish-standard__warp'>
<div className='module-title'>
高级设置
<span
className={`icon iconfont ${showStandardDetail && 'rotate-arrow'}`}
onClick={() => {
this.setState({ showStandardDetail: !showStandardDetail });
}}>
&#xe614;
</span>
</div>
{showStandardDetail && (
<div className='detail-container'>
<div className='title-text'>完成标准:</div>
<div className='detail-box'>
<div className='item-info'>
<img src={LearningContentIcon['LIVE']} />
<span>
直播课单个课程,学员学习进度达到
<Input
// value={percentCompleteLive}
onChange={(e) => {
// this.props.onChange('percentCompleteLive', e.target.value.replace(/\D/g, ''));
}}
// onBlur={(e) => this.percentCompleteBlur(e, 'percentCompleteLive')}
className='input-box'
/>
%,即视为"已完成"学习
</span>
</div>
<div className='item-info'>
<img src={LearningContentIcon['VOICE']} />
<span>
线上课单个课节,学员学习进度达到
<Input
// value={percentCompleteVideo}
onChange={(e) => {
// this.props.onChange('percentCompleteVideo', e.target.value.replace(/\D/g, ''));
}}
// onBlur={(e) => this.percentCompleteBlur(e, 'percentCompleteVideo')}
className='input-box'
/>
%,即课节视为"已完成"学习
</span>
</div>
<div className='item-info'>
<img src={LearningContentIcon['PICTURE']} />
<span>
图文课单个课程,学员学习进度达到
<Input
// value={percentCompletePicture}
onChange={(e) => {
// this.props.onChange('percentCompletePicture', e.target.value.replace(/\D/g, ''));
}}
// onBlur={(e) => this.percentCompleteBlur(e, 'percentCompletePicture')}
className='input-box'
/>
%,即视为"已完成"学习
</span>
</div>
</div>
</div>
)}
</div>
</div> </div>
); );
} }
......
.train-content__warp { .train-content-page {
.ant-collapse-content { .train-content__warp {
padding-left: 24px !important; .ant-collapse-header {
padding: 15px 16px !important;
background-color: #f7f8f9;
}
.add-stage-btn {
color: #2966ff;
height: 52px;
background: #f7f8f9;
padding: 16px;
cursor: pointer;
}
}
.expired-info__wrap {
margin-top: 24px;
.module-title {
height: 22px;
font-size: 16px;
color: #333333;
line-height: 22px;
}
}
.finish-standard__warp {
margin-top: 24px;
.module-title {
height: 22px;
font-size: 16px;
color: #333333;
line-height: 22px;
.icon {
font-size: 12px;
margin-left: 8px;
color: #5e606a;
display: inline-block;
vertical-align: middle;
cursor: pointer;
}
.rotate-arrow {
transform: (rotate(180deg));
}
}
.detail-container {
margin-top: 24px;
display: flex;
.title-text {
color: #333333;
line-height: 20px;
}
.detail-box {
margin-top: -4px;
.item-info {
color: #333333;
vertical-align: middle;
margin-bottom: 20px;
* {
vertical-align: middle;
}
img {
width: 20px;
height: 20px;
margin-right: 8px;
}
.input-box {
width: 60px;
height: 32px;
border-radius: 2px;
border: 1px solid #e8e8e8;
margin: 0 4px;
}
}
}
}
} }
} }
.sort-task-item {
.sort-stage-item {
width: calc(100% - 24px); width: calc(100% - 24px);
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
.ant-form-item { line-height: 20px;
margin-bottom: 0 !important; .item-info {
}
.item-name {
color: #333333; color: #333333;
} }
.item-operate { .item-operate {
display: none; display: none;
margin-left: 30px; margin-left: 30px;
...@@ -28,32 +98,98 @@ ...@@ -28,32 +98,98 @@
} }
} }
} }
&:hover { &:hover {
.item-operate { .item-operate {
display: block; display: block;
} }
} }
.drag-btn { .ant-form {
margin-left: auto; display: inline-block;
.ant-form-item {
margin-bottom: 0 !important;
}
} }
} }
.add-course-btn { .sort-content-item {
color: #2966ff; display: flex;
padding: 15px 16px !important;
margin-left: 40px;
justify-content: space-between;
align-items: center;
border-bottom: 1px dotted #e8e8e8;
vertical-align: middle;
* {
vertical-align: middle;
height: 20px;
}
&:hover {
.content-operate {
display: block;
}
}
.content-info {
.content-name {
color: #333333;
font-size: 14px;
margin-left: 12px;
}
.extra-info {
color: #999999;
margin-left: 12px;
}
.tip {
font-size: 14px;
color: #ff4f4f;
margin-right: 2px;
}
}
.content-operate {
display: none;
margin-left: 26px;
.operate__item {
cursor: pointer;
color: #666666;
.icon {
font-size: 14px;
color: #bfbfbf;
}
.text {
margin-left: 8px;
}
}
}
} }
.add-task-btn {
.type-option-icon {
width: 20px;
height: 20px;
}
.type-option-text {
margin-left: 12px;
}
.drag-btn {
margin-left: auto;
color: #cccccc;
cursor: pointer;
}
.add-content-btn {
color: #2966ff; color: #2966ff;
height: 52px; height: 52px;
background: #f7f8f9;
border-radius: 2px; border-radius: 2px;
padding: 16px; padding: 16px;
margin-top: 16px;
cursor: pointer; cursor: pointer;
display: inline-block;
} }
.type-option-icon { .ant-collapse-content {
width: 20px; padding-left: 24px !important;
height: 20px; > .ant-collapse-content-box {
margin-right: 12px; padding: 0 !important;
}
} }
...@@ -7,14 +7,12 @@ ...@@ -7,14 +7,12 @@
*/ */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { withRouter } from 'react-router-dom';
import { Row, Input, Select, Tooltip } from 'antd'; import { Row, Input, Select, Tooltip } from 'antd';
import RangePicker from '@/modules/common/DateRangePicker'; import RangePicker from '@/modules/common/DateRangePicker';
import moment from 'moment'; import moment from 'moment';
import StoreService from '@/domains/store-domain/storeService'; import StoreService from '@/domains/store-domain/storeService';
import './TrainFilter.less'; import './TrainFilter.less';
const { Search } = Input; const { Search } = Input;
const { Option } = Select;
const DEFAULT_QUERY = { const DEFAULT_QUERY = {
// 头部筛选默认值 // 头部筛选默认值
...@@ -29,6 +27,7 @@ const DEFAULT_CREATOR_QUERY = { ...@@ -29,6 +27,7 @@ const DEFAULT_CREATOR_QUERY = {
current: 1, current: 1,
nickName: null, // 搜索值 nickName: null, // 搜索值
}; };
function TrainFilter(props) { function TrainFilter(props) {
const [query, setQuery] = useState(DEFAULT_QUERY); const [query, setQuery] = useState(DEFAULT_QUERY);
const [hasNext, setHasNext] = useState(false); const [hasNext, setHasNext] = useState(false);
...@@ -54,7 +53,7 @@ function TrainFilter(props) { ...@@ -54,7 +53,7 @@ function TrainFilter(props) {
}); });
} }
// 滑动加载更多讲师列表 // 滑动加载更多创建人列表(讲师)
function handleScrollCreatorList(e) { function handleScrollCreatorList(e) {
const container = e.target; const container = e.target;
const scrollToBottom = container && container.scrollHeight <= container.clientHeight + container.scrollTop; const scrollToBottom = container && container.scrollHeight <= container.clientHeight + container.scrollTop;
...@@ -145,7 +144,7 @@ function TrainFilter(props) { ...@@ -145,7 +144,7 @@ function TrainFilter(props) {
<div className='search-condition__item'> <div className='search-condition__item'>
<span className='search-date'>创建日期:</span> <span className='search-date'>创建日期:</span>
<RangePicker <RangePicker
id='course_date_picker' id='train_date_picker'
allowClear={false} allowClear={false}
value={query.startTime ? [moment(query.startTime), moment(query.endTime)] : null} value={query.startTime ? [moment(query.startTime), moment(query.endTime)] : null}
format={'YYYY-MM-DD'} format={'YYYY-MM-DD'}
...@@ -169,4 +168,4 @@ function TrainFilter(props) { ...@@ -169,4 +168,4 @@ function TrainFilter(props) {
); );
} }
export default withRouter(TrainFilter); export default TrainFilter;
.train-filter-page { .train-filter-page {
position: relative;
margin-bottom: 4px;
.ant-input-search-button{
border-left:none;
}
.search-condition { .search-condition {
width: calc(100% - 80px); width: calc(100% - 80px);
display: flex; display: flex;
...@@ -13,26 +8,16 @@ ...@@ -13,26 +8,16 @@
&__item { &__item {
width: 30%; width: 30%;
margin-right: 3%; margin-right: 3%;
margin-bottom: 12px;
line-height: 32px; line-height: 32px;
} }
} }
.reset-fold-area { .reset-fold-area {
position: absolute; position: absolute;
right: 12px; right: 12px;
}
.resetBtn { .resetBtn {
color: #999999; color: #999999;
font-size: 18px; font-size: 18px;
margin-right: 8px; margin-right: 8px;
} }
.fold-btn {
font-size: 14px;
color: #666666;
line-height: 20px;
.fold-icon {
font-size: 12px;
margin-left:4px;
}
} }
} }
...@@ -2,19 +2,17 @@ ...@@ -2,19 +2,17 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-07-28 14:56:52 * @Date: 2021-07-28 14:56:52
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-07-30 12:01:29 * @LastEditTime: 2021-08-02 15:45:37
* @Description: 描述一下咯 * @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Route, withRouter } from 'react-router-dom' import { Route, withRouter } from 'react-router-dom';
import { Tabs, Tooltip, Checkbox, Dropdown, Radio, Button, Space } from 'antd'; import { Tooltip, Checkbox, Dropdown, Radio, Button, Space, Badge } from 'antd';
import './TrainList.less'; import './TrainList.less';
import { XMTable, PageControl } from '@/components'; import { XMTable, PageControl } from '@/components';
import User from '@/common/js/user'; import User from '@/common/js/user';
const { TabPane } = Tabs;
function TrainList(props) { function TrainList(props) {
const { const {
query: { issueState, myAssist, current, size }, query: { issueState, myAssist, current, size },
...@@ -24,6 +22,7 @@ function TrainList(props) { ...@@ -24,6 +22,7 @@ function TrainList(props) {
function renderMoreOperate(item) { function renderMoreOperate(item) {
return ( return (
<div className='live-course-more-menu'> <div className='live-course-more-menu'>
<div className='operate__item'>取消发布</div>
<div className='operate__item'>编辑</div> <div className='operate__item'>编辑</div>
<div className='operate__item'>分享</div> <div className='operate__item'>分享</div>
<div className='operate__item'>审批作业</div> <div className='operate__item'>审批作业</div>
...@@ -42,16 +41,16 @@ function TrainList(props) { ...@@ -42,16 +41,16 @@ function TrainList(props) {
fixed: 'left', fixed: 'left',
render: (val, record) => { render: (val, record) => {
return ( return (
<div className='plan_name_item'> <div className='train-task-name'>
<img className='plan-cover' src={record.coverUrl || 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png'} alt='' /> <img className='train-cover' src={record.coverUrl || 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png'} alt='' />
<Choose> <Choose>
<When condition={record.planName.length > 25}> <When condition={record.planName.length > 25}>
<Tooltip title={record.planName}> <Tooltip title={record.planName}>
<div className='plan-name'>{val}</div> <div className='train-name'>{val}</div>
</Tooltip> </Tooltip>
</When> </When>
<Otherwise> <Otherwise>
<div className='plan-name'>{val}</div> <div className='train-name'>{val}</div>
</Otherwise> </Otherwise>
</Choose> </Choose>
</div> </div>
...@@ -64,7 +63,7 @@ function TrainList(props) { ...@@ -64,7 +63,7 @@ function TrainList(props) {
key: 'status', key: 'status',
dataIndex: 'status', dataIndex: 'status',
render: (val, record) => { render: (val, record) => {
return <div className='course-number'>{'未开始'}</div>; return <span>{'未开始'}</span>;
}, },
}, },
{ {
...@@ -73,18 +72,17 @@ function TrainList(props) { ...@@ -73,18 +72,17 @@ function TrainList(props) {
key: 'courseNum', key: 'courseNum',
dataIndex: 'courseNum', dataIndex: 'courseNum',
render: (val, record) => { render: (val, record) => {
return <div className='course-number'>{val}</div>; return <span>{val}</span>;
}, },
}, },
{ {
title: '学习人数', title: '学习人数',
width: '8%', width: '10%',
align: 'right',
key: 'cultureCustomerNum', key: 'cultureCustomerNum',
dataIndex: 'cultureCustomerNum', dataIndex: 'cultureCustomerNum',
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return <div className='join-number'>{val}</div>; return <span style={{ color: '#2966FF' }}>{val}</span>;
}, },
}, },
{ {
...@@ -95,7 +93,7 @@ function TrainList(props) { ...@@ -95,7 +93,7 @@ function TrainList(props) {
<i <i
className='icon iconfont' className='icon iconfont'
style={{ style={{
marginLeft: '5px', marginLeft: '4px',
cursor: 'pointer', cursor: 'pointer',
color: '#bfbfbf', color: '#bfbfbf',
fontSize: '14px', fontSize: '14px',
...@@ -127,14 +125,14 @@ function TrainList(props) { ...@@ -127,14 +125,14 @@ function TrainList(props) {
title: '创建人', title: '创建人',
key: 'createName', key: 'createName',
dataIndex: 'createName', dataIndex: 'createName',
width: '10%', width: '12%',
render: (val) => { render: (val) => {
return <div className='create-name'>{val}</div>; return <span>{val}</span>;
}, },
}, },
{ {
title: '创建时间', title: '创建时间',
width: '14%', width: '12%',
key: 'created', key: 'created',
dataIndex: 'created', dataIndex: 'created',
sorter: true, sorter: true,
...@@ -144,7 +142,7 @@ function TrainList(props) { ...@@ -144,7 +142,7 @@ function TrainList(props) {
}, },
{ {
title: '更新时间', title: '更新时间',
width: '10%', width: '12%',
key: 'updated', key: 'updated',
dataIndex: 'updated', dataIndex: 'updated',
sorter: true, sorter: true,
...@@ -165,9 +163,7 @@ function TrainList(props) { ...@@ -165,9 +163,7 @@ function TrainList(props) {
<div className='operate__item' onClick={() => toLearningDataPage(record)}> <div className='operate__item' onClick={() => toLearningDataPage(record)}>
数据 数据
</div> </div>
{record.enableState === 'YES' && ( <span className='split'> | </span>
<>
<span className='operate__item split'> | </span>
<div <div
className='operate__item' className='operate__item'
onClick={() => { onClick={() => {
...@@ -175,33 +171,15 @@ function TrainList(props) { ...@@ -175,33 +171,15 @@ function TrainList(props) {
}}> }}>
指派 指派
</div> </div>
</> <span className='split'> | </span>
)}
{record.enableState === 'YES' && (
<>
<span className='operate__item split'> | </span>
<div
className='operate__item'
onClick={() => {
handleShowShareModal(record);
}}>
发布
</div>
</>
)}
{(User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') && (
<>
<span className='operate__item split'> | </span>
<Dropdown overlay={renderMoreOperate(record)}> <Dropdown overlay={renderMoreOperate(record)}>
<span className='more-operate'> <span className='more-operate'>
<span className='operate-text'>更多</span> <span className='more-text'>更多</span>
<span className='iconfont icon' style={{ color: '#2966FF' }}> <span className='iconfont icon' style={{ color: '#2966FF' }}>
&#xe824; &#xe824;
</span> </span>
</span> </span>
</Dropdown> </Dropdown>
</>
)}
</div> </div>
); );
}, },
...@@ -240,9 +218,9 @@ function TrainList(props) { ...@@ -240,9 +218,9 @@ function TrainList(props) {
onChange={(e) => { onChange={(e) => {
handleChangeQuery('issueState', e.target.value); handleChangeQuery('issueState', e.target.value);
}}> }}>
<Radio.Button value='ALL'>全部</Radio.Button> <Radio.Button value='ALL'>全部(2)</Radio.Button>
<Radio.Button value='YES'>已发布</Radio.Button> <Radio.Button value='YES'>已发布(2)</Radio.Button>
<Radio.Button value='NO'>未发布</Radio.Button> <Radio.Button value='NO'>未发布(10)</Radio.Button>
</Radio.Group> </Radio.Group>
<Checkbox style={{ lineHeight: '32px' }} value={myAssist} onChange={(e) => handleChangeQuery('myAssist', e.target.checked)}> <Checkbox style={{ lineHeight: '32px' }} value={myAssist} onChange={(e) => handleChangeQuery('myAssist', e.target.checked)}>
只看我协同的 ({10}) 只看我协同的 ({10})
...@@ -260,7 +238,7 @@ function TrainList(props) { ...@@ -260,7 +238,7 @@ function TrainList(props) {
bordered bordered
size='middle' size='middle'
scroll={{ x: 1600 }} scroll={{ x: 1600 }}
// className='plan-list-table' className='train-list-table'
renderEmpty={{ renderEmpty={{
description: <span style={{ display: 'block', paddingBottom: 24 }}>暂无数据</span>, description: <span style={{ display: 'block', paddingBottom: 24 }}>暂无数据</span>,
}} }}
......
.train-list-page { .train-list-page {
margin-top: 12px;
.header-line { .header-line {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin-top: 16px;
margin-bottom: 12px; margin-bottom: 12px;
} .ant-radio-button-wrapper {
.ant-tabs-nav { color: #9d9d9d;
margin: 0 !important;
}
.course-number {
margin-right: 45px;
}
.plan-list-table { &:first-child {
thead.ant-table-thead { border-radius: 4px 0 0 4px;
tr {
th {
padding: 10px 12px;
}
}
}
tbody {
tr {
td.ant-table-cell {
padding: 16px 12px;
color: #333;
}
&:nth-child(even) {
background: transparent;
td {
background: #fff;
}
}
&:nth-child(odd) {
background: #fafafa;
td {
background: #fafafa;
}
}
&:hover {
td {
background: #f3f6fa;
} }
&:last-child {
border-radius: 0 4px 4px 0;
} }
} }
.ant-radio-button-wrapper-checked {
color: #2966ff;
} }
} }
.plan_name_item {
.train-list-table {
.train-task-name {
display: flex; display: flex;
align-items: center; align-items: center;
.plan-cover { .train-cover {
width: 106px; width: 106px;
height: 60px; height: 60px;
border-radius: 2px; border-radius: 2px;
margin-right: 8px; margin-right: 8px;
} }
.plan-name { .train-name {
width: 188px; width: 188px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
...@@ -66,28 +40,61 @@ ...@@ -66,28 +40,61 @@
height: 40px; height: 40px;
} }
} }
.operate-text {
color: #2966ff;
cursor: pointer;
}
.operate { .operate {
display: flex; display: flex;
&__item { &__item {
color: #2966ff; color: #2966ff;
cursor: pointer; cursor: pointer;
}
&.split { .split {
margin: 0 8px; margin: 0 8px;
color: #bfbfbf; color: #bfbfbf;
} }
.more-text {
color: #2966ff;
cursor: pointer;
}
}
.tip {
cursor: pointer;
color: '#bfbfbf';
margin-left: 5px;
font-weight: normal;
}
thead.ant-table-thead {
tr {
th {
padding: 10px 16px;
}
}
}
tbody {
tr {
td.ant-table-cell {
padding: 20px 16px;
color: #333;
}
&:nth-child(even) {
background: transparent;
td {
background: #fff;
}
}
&:nth-child(odd) {
background: #fafafa;
td {
background: #fafafa;
}
}
&:hover {
td {
background: #f3f6fa;
}
} }
} }
.join-number {
text-align: right;
margin-right: 12px;
} }
.more-operate {
line-height: 20px;
} }
} }
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