Commit 136034ff by yuananting

feat:新建培训任务相关接口联调

parent 11f4ee76
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-08-06 17:35:35
* @LastEditors: yuananting
* @LastEditTime: 2021-08-09 14:23:07
* @LastEditTime: 2021-08-10 15:28:07
* @Description: 任务中心接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
......@@ -13,6 +13,10 @@ export function getTrainingTaskPage(params: object) {
return Service.Hades('public/hades/getTrainingTaskPage', params);
}
export function getStoreTaskNum(params: object) {
return Service.Hades('public/hades/getStoreTaskNum', params);
}
export function createTrainingExam(params: object) {
return Service.Hades('public/hades/createTrainingExam', params);
}
......@@ -20,3 +24,19 @@ export function createTrainingExam(params: object) {
export function createTrainingTask(params: object) {
return Service.Hades('public/hades/createTrainingTask', params);
}
export function updateIssueStateTraining(params: object) {
return Service.Hades('public/hades/updateIssueStateTraining', params);
}
export function deleteTrainingTask(params: object) {
return Service.Hades('public/hades/deleteTrainingTask', params);
}
export function getTrainingTaskDetail(params: object) {
return Service.Hades('public/hades/getTrainingTaskDetail', params);
}
export function updateTrainingTask(params: object) {
return Service.Hades('public/hades/updateTrainingTask', params);
}
......@@ -2,12 +2,21 @@
* @Author: yuananting
* @Date: 2021-08-06 17:32:41
* @LastEditors: yuananting
* @LastEditTime: 2021-08-09 14:23:33
* @LastEditTime: 2021-08-10 15:28:39
* @Description: 任务中心-培训任务接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import { getTrainingTaskPage, createTrainingExam, createTrainingTask } from '@/data-source/taskCenter/request-apis';
import {
getTrainingTaskPage,
getStoreTaskNum,
createTrainingExam,
createTrainingTask,
updateIssueStateTraining,
deleteTrainingTask,
getTrainingTaskDetail,
updateTrainingTask,
} from '@/data-source/taskCenter/request-apis';
export default class TaskCenterService {
// 获取培训任务列表
......@@ -15,6 +24,11 @@ export default class TaskCenterService {
return getTrainingTaskPage(params);
}
// 获取学院任务数量
static getStoreTaskNum(params: any) {
return getStoreTaskNum(params);
}
// 培训任务创建考试
static createTrainingExam(params: any) {
return createTrainingExam(params);
......@@ -24,4 +38,24 @@ export default class TaskCenterService {
static createTrainingTask(params: any) {
return createTrainingTask(params);
}
// 发布/取消发布培训任务
static updateIssueStateTraining(params: any) {
return updateIssueStateTraining(params);
}
// 删除培训任务
static deleteTrainingTask(params: any) {
return deleteTrainingTask(params);
}
// 获取培训任务详情
static getTrainingTaskDetail(params: any) {
return getTrainingTaskDetail(params);
}
// 修改培训任务
static updateTrainingTask(params: any) {
return updateTrainingTask(params);
}
}
import React, { useEffect, useState } from 'react'
import { Select, Tooltip, Carousel, Popover } from 'antd'
import DataSet from '@antv/data-set'
import { Chart as G2Chart } from '@antv/g2'
import { G2, Chart, Geom, Axis, Tooltip as G2Tooltip, Coord, Label, Legend, View, Guide, Shape, Facet, Util } from 'bizcharts'
import moment from 'moment'
import Service from '@/common/js/service'
import User from '@/common/js/user'
import React, { useEffect, useState } from 'react';
import { Select, Tooltip, Carousel, Popover } from 'antd';
import DataSet from '@antv/data-set';
import { Chart as G2Chart } from '@antv/g2';
import { G2, Chart, Geom, Axis, Tooltip as G2Tooltip, Coord, Label, Legend, View, Guide, Shape, Facet, Util } from 'bizcharts';
import moment from 'moment';
import Service from '@/common/js/service';
import User from '@/common/js/user';
import HomeTip from './HomeTip'
import './Home.less'
import HomeTip from './HomeTip';
import './Home.less';
const Option = Select.Option
const Option = Select.Option;
class Home extends React.Component {
constructor(props) {
super(props)
super(props);
this.state = {
scheduleType: 'LIVE',
list: [],
......@@ -34,54 +34,54 @@ class Home extends React.Component {
incWeekVisitCustomerNum: 0,
courseNum: 0, //课程总数
inCourseNum: 0, //本月新增课程总数
}
this._chart = null
};
this._chart = null;
}
componentDidMount() {
this.getPanelInfo()
this.getStudyInfo()
this.getHotCourse()
this.getTrainingInfo()
this.getPanelInfo();
this.getStudyInfo();
this.getHotCourse();
this.getTrainingInfo();
}
getTrainingInfo() {
Service.Hades('public/hades/planOverview', { storeId: User.getStoreId() }).then((res) => {
if (res.success) {
this.setState(res.result)
this.setState(res.result);
}
})
});
}
getHotCourse() {
const { timeRange, scheduleType } = this.state
const { timeRange, scheduleType } = this.state;
const data = {
hotNum: 5,
scheduleType,
storeId: User.getStoreId(),
timeRange,
}
};
Service.Hades('public/courseCloud/hotCourse', data).then((res) => {
if (res.success) {
this.setState({
list: res.result,
})
});
}
})
});
}
getStudyInfo() {
const { studyTimeRange } = this.state
const { studyTimeRange } = this.state;
Service.Hades('public/hades/studyInfo', { storeId: User.getStoreId(), timeRange: studyTimeRange }).then((res) => {
if (res.success) {
const dataList = res.result.map((item) => ({
time: moment(item.dateline).format('MM-DD'),
studyNum: item.studyNum,
studyTime: Math.round(item.studyTime / 6) / 10,
}))
this.createChart(dataList)
}));
this.createChart(dataList);
}
})
});
}
getPanelInfo() {
......@@ -106,22 +106,22 @@ class Home extends React.Component {
incWeekVisitCustomerNum: res.result.incWeekVisitCustomerNum,
courseNum: res.result.videoCourseNum + res.result.liveCourseNum + res.result.pictureCourseNum + res.result.offlineCourseNum,
inCourseNum: res.result.incLiveCourseNum + res.result.incVideoCourseNum + res.result.incPictureCourseNum + res.result.incOfflineCourseNum,
})
});
}
})
});
}
showNumber(index) {
switch (index) {
case 0:
return 'https://image.xiaomaiketang.com/xm/D64QhNn74S.png'
return 'https://image.xiaomaiketang.com/xm/D64QhNn74S.png';
case 1:
return 'https://image.xiaomaiketang.com/xm/Qfib4mnGJT.png'
return 'https://image.xiaomaiketang.com/xm/Qfib4mnGJT.png';
case 2:
return 'https://image.xiaomaiketang.com/xm/8jKXHyrDaG.png'
return 'https://image.xiaomaiketang.com/xm/8jKXHyrDaG.png';
default:
return 'https://image.xiaomaiketang.com/xm/D64QhNn74S.png'
return 'https://image.xiaomaiketang.com/xm/D64QhNn74S.png';
}
}
......@@ -132,28 +132,28 @@ class Home extends React.Component {
forceFit: true,
height: 290,
padding: [48, 64],
})
});
}
this._chart.clear()
this._chart.clear();
this._chart.source(data, {
studyTime: {
formatter: (val) => {
return val
return val;
},
tickCount: 5,
},
time: {
formatter: (val) => {
return `${val}`
return `${val}`;
},
},
studyNum: {
formatter: (val) => {
return val
return val;
},
tickCount: 5,
},
})
});
this._chart.axis('time', {
label: {
offset: 20,
......@@ -168,7 +168,7 @@ class Home extends React.Component {
tickLine: {
stroke: '#E8E8E8',
},
})
});
this._chart.axis('submitCount', {
label: {
textStyle: {
......@@ -176,7 +176,7 @@ class Home extends React.Component {
fontSize: 14,
},
},
})
});
this._chart.axis('studyTime', {
label: {
textStyle: {
......@@ -184,7 +184,7 @@ class Home extends React.Component {
fontSize: 14,
},
},
})
});
this._chart
.line()
.position('time*studyNum')
......@@ -193,8 +193,8 @@ class Home extends React.Component {
return {
name: '学习人数',
value: studyNum + '人',
}
})
};
});
this._chart
.line()
.position('time*studyTime')
......@@ -203,10 +203,10 @@ class Home extends React.Component {
return {
name: '人均学习时长',
value: studyTime + '分钟',
}
})
};
});
this._chart.legend(false)
this._chart.legend(false);
this._chart.tooltip({
containerTpl:
'<div class="g2-tooltip" style="background: #fff !important;">' +
......@@ -214,9 +214,9 @@ class Home extends React.Component {
'<ul class="g2-tooltip-list"></ul></div>', // tooltip 容器模板
itemTpl:
'<li data-index={index}><span style="background-color:{color};width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:8px;"></span>{name}<span style="display: inline-block; float: right; margin-left: 30px;">{value}</span></li>', // tooltip 每项记录的默认模板
})
this._chart.render()
}
});
this._chart.render();
};
render() {
const {
......@@ -245,7 +245,7 @@ class Home extends React.Component {
offlineCourseNum,
weekVisitCustomerNum,
incWeekVisitCustomerNum,
} = this.state
} = this.state;
const data = [
{
item: '已完成培训',
......@@ -255,26 +255,26 @@ class Home extends React.Component {
item: '未完成培训',
count: unfinishedNum,
},
]
const { DataView } = DataSet
const { Html } = Guide
const sum = data[0].count + data[1].count
const dv = new DataView()
];
const { DataView } = DataSet;
const { Html } = Guide;
const sum = data[0].count + data[1].count;
const dv = new DataView();
sum &&
dv.source(data).transform({
type: 'percent',
field: 'count',
dimension: 'item',
as: 'percent',
})
});
const cols = {
percent: {
formatter: (val) => {
val = val * 100 + '%'
return val
val = val * 100 + '%';
return val;
},
},
}
};
return (
<div className='home-page'>
<HomeTip />
......@@ -357,7 +357,7 @@ class Home extends React.Component {
<div className='data-item'>
<div className='header'>
<img className='header-icon' src='https://image.xiaomaiketang.com/xm/jZf3GNY5tY.png' alt='' />
<span className='header-word'>培训计划总数 (个)</span>
<span className='header-word'>培训任务总数 (个)</span>
</div>
<div className='data-number'>{trainingPlanNum}</div>
<div className='data-footer'>
......@@ -416,7 +416,7 @@ class Home extends React.Component {
style={{ width: 88 }}
value={timeRange}
onChange={(value) => {
this.setState({ timeRange: value }, () => this.getHotCourse())
this.setState({ timeRange: value }, () => this.getHotCourse());
}}>
<Option value='7'>近7天</Option>
<Option value='15'>近15天</Option>
......@@ -459,8 +459,8 @@ class Home extends React.Component {
</div>
<div className='study-item'>
<div className='study-title'>
培训计划完成情况
<Tooltip overlayClassName='data-plan-tooltip' title='若某人加入多个培训计划,则需完成所有已加入的培训计划后,才视为已完成培训'>
培训任务完成情况
<Tooltip overlayClassName='data-plan-tooltip' title='若某人加入多个培训任务,则需完成所有已加入的培训任务后,才视为已完成培训'>
<span className='iconfont icon'>&#xe61d;</span>
</Tooltip>
<span className='tip'>(本月)</span>
......@@ -479,7 +479,7 @@ class Home extends React.Component {
<Axis name='percent' />
<G2Tooltip
showTitle={false}
itemTpl='<li><span style="background-color:{color};" class="g2-tooltip-marker"></span>{name}: {value}</li>'
itemTpl='<li><span style="background-color:{color};" class="g2-tooltip-marker"></span><span style="margin-right:16px;">{percent}</span><span>{num}</span></li>'
/>
<Guide>
<Html
......@@ -492,15 +492,15 @@ class Home extends React.Component {
<Geom
type='intervalStack'
position='percent'
color={['item', ['#FFBB54', '#2966FF']]}
color={['item', ['#14CCA7', '#2966FF']]}
tooltip={[
'item*percent',
(item, percent) => {
percent = Math.round(percent * 100) + '%'
percent = Math.round(percent * 100) + '%';
return {
name: item,
value: percent,
}
num: item === '未完成培训' ? unfinishedNum + '人' : completeNum + '人',
percent: percent,
};
},
]}
style={{
......@@ -526,15 +526,15 @@ class Home extends React.Component {
</Otherwise>
</Choose>
<div className='circle-tip unfinished'>
<div className='circle-tip finished'>
<div className='spot'></div>
<div className='number'>{unfinishedNum}</div>
<div className='word'>完成培训</div>
{/* <div className='number'>{completeNum}人</div> */}
<div className='word'>完成培训</div>
</div>
<div className='circle-tip finished'>
<div className='circle-tip unfinished'>
<div className='spot'></div>
<div className='number'>{completeNum}</div>
<div className='word'>完成培训</div>
{/* <div className='number'>{unfinishedNum}人</div> */}
<div className='word'>完成培训</div>
</div>
</div>
</div>
......@@ -554,7 +554,7 @@ class Home extends React.Component {
style={{ width: 88 }}
value={studyTimeRange}
onChange={(value) => {
this.setState({ studyTimeRange: value }, () => this.getStudyInfo())
this.setState({ studyTimeRange: value }, () => this.getStudyInfo());
}}>
<Option value='7'>近7天</Option>
<Option value='15'>近15天</Option>
......@@ -582,8 +582,8 @@ class Home extends React.Component {
</div>
</div>
</div>
)
);
}
}
export default Home
export default Home;
......@@ -278,18 +278,19 @@
}
.circle-tip {
position: absolute;
left: 70%;
padding-left: 16px;
&.unfinished {
top: 152px;
bottom: 2%;
left: 37%;
.spot {
background: #2966ff;
}
}
&.finished {
top: 232px;
bottom: 2%;
left: 14%;
.spot {
background: #ffbb54;
background: #14cca7;
}
}
.spot {
......@@ -297,7 +298,7 @@
width: 8px;
height: 8px;
border-radius: 4px;
top: 20px;
top: 8px;
left: 0;
}
.number {
......
......@@ -6,66 +6,66 @@
* @Description: 大班直播分享弹窗
*/
import React from 'react'
import { Modal, Button, message } from 'antd'
import domtoimage from 'dom-to-image'
import qrcode from '@/libs/qrcode/qrcode.js'
import User from '@/common/js/user'
import $ from 'jquery'
import CourseService from '@/domains/course-domain/CourseService'
import React from 'react';
import { Modal, Button, message } from 'antd';
import domtoimage from 'dom-to-image';
import qrcode from '@/libs/qrcode/qrcode.js';
import User from '@/common/js/user';
import $ from 'jquery';
import CourseService from '@/domains/course-domain/CourseService';
import './SharePlanModal.less'
import './SharePlanModal.less';
const DEFAULT_COVER = 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png'
const DEFAULT_COVER = 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png';
class ShareLiveModal extends React.Component {
constructor(props) {
super(props)
super(props);
this.state = {
shareUrl: '',
}
};
}
componentDidMount() {
// 获取短链接
this.handleConvertShortUrl()
this.handleConvertShortUrl();
}
handleConvertShortUrl = () => {
const { longUrl } = this.props.data
const { longUrl } = this.props.data;
// 发请求
CourseService.getQrcode({
urls: [longUrl],
}).then((res) => {
const { result = [] } = res
const { result = [] } = res;
this.setState(
{
shareUrl: result[0].shortUrl,
},
() => {
const qrcodeWrapDom = document.querySelector('#qrcodeWrap')
const qrcodeWrapDom = document.querySelector('#qrcodeWrap');
const qrcodeNode = new qrcode({
text: this.state.shareUrl,
size: 98,
})
qrcodeWrapDom.appendChild(qrcodeNode)
});
qrcodeWrapDom.appendChild(qrcodeNode);
const qrcodeWrapDomDownload = document.querySelector('#qrcodeWrap-dowload')
const qrcodeWrapDomDownload = document.querySelector('#qrcodeWrap-dowload');
const qrcodeNodeDownLoad = new qrcode({
text: this.state.shareUrl,
size: 196,
})
qrcodeWrapDomDownload.appendChild(qrcodeNodeDownLoad)
});
qrcodeWrapDomDownload.appendChild(qrcodeNodeDownLoad);
}
)
})
}
);
});
};
componentWillUnmount() {
// 页面销毁之前清空定时器
clearTimeout(this.timer)
clearTimeout(this.timer);
}
// 下载海报
......@@ -77,29 +77,29 @@ class ShareLiveModal extends React.Component {
},
() => {
this.setState({ time: new Date().valueOf() }, () => {
let node = document.getElementById('poster-dowload')
let node = document.getElementById('poster-dowload');
domtoimage.toPng(node).then((imgData) => {
const download = document.createElement('a')
const { planName } = this.props.data
$(download).attr('href', imgData).attr('download', `${planName}.png`).get(0).click()
})
})
const download = document.createElement('a');
const { planName } = this.props.data;
$(download).attr('href', imgData).attr('download', `${planName}.png`).get(0).click();
});
});
}
)
}
);
};
// 复制分享链接
handleCopy = () => {
const textContent = document.getElementById('shareUrl').innerText
const textContent = document.getElementById('shareUrl').innerText;
window.copyText(textContent)
message.success('复制成功!')
}
window.copyText(textContent);
message.success('复制成功!');
};
render() {
const { data } = this.props
const { planName, coverUrl = DEFAULT_COVER } = data
const { shareUrl, showImg, time } = this.state
const { data } = this.props;
const { planName, coverUrl = DEFAULT_COVER } = data;
const { shareUrl, showImg, time } = this.state;
return (
<Modal
title={'分享培训计划'}
......@@ -185,8 +185,8 @@ class ShareLiveModal extends React.Component {
</div>
</div>
</Modal>
)
);
}
}
export default ShareLiveModal
export default ShareLiveModal;
......@@ -2,12 +2,12 @@
* @Author: yuananting
* @Date: 2021-07-29 13:57:03
* @LastEditors: yuananting
* @LastEditTime: 2021-08-09 17:39:56
* @LastEditTime: 2021-08-11 12:46:17
* @Description: 任务中心-培训任务-新建页面
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import { Button, message, Modal, Space } from 'antd';
import ShowTips from '@/components/ShowTips';
import Breadcrumbs from '@/components/Breadcrumbs';
......@@ -18,14 +18,15 @@ import _ from 'underscore';
import './AddTrainTask.less';
import Upload from '@/core/upload';
import { randomString } from '@/domains/basic-domain/utils';
import TaskCenterService from '@/domains/task-center-domain/TaskCenterService';
import Bus from '@/core/bus';
const defaultCover = 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png';
const DEFAULT_BASIC_INFO = {
taskName: '1', // 培训任务名称
taskName: '', // 培训任务名称
coverUrl: defaultCover,
coverId: null,
trainingStageList: [], // 培训内容
helpStoreUserIds: [], // 指定协同者id
timeType: 'FOREVER', // 培训时间,默认永久有效
startTime: null, // 固定时间段-开始时间
......@@ -51,17 +52,85 @@ const DEFAULT_FINISH_STANDARD = {
function AddTrainTask() {
const type = getParameterByName('type');
const taskId = getParameterByName('taskId');
const taskState = getParameterByName('taskState');
const [activeStep, setActiveStep] = useState('BASIC_INFO');
const [basicInfo, setBasicInfo] = useState(DEFAULT_BASIC_INFO);
const [stageList, setStageList] = useState(DEFAULT_STAGE_LIST);
const [finishStandard, setFinishStandard] = useState(DEFAULT_FINISH_STANDARD); // 完成百分比
const [startCheck, setStartCheck] = useState(false); // 是否启动校验
const basicInfoRef = useRef(null);
useEffect(() => {
basicInfoRef.current = basicInfo;
}, [basicInfo]);
useEffect(() => {
if (type === 'edit') {
TaskCenterService.getTrainingTaskDetail({
taskId,
}).then((res) => {
const {
result: {
taskName,
courseMediaVOS,
helpStoreUsers,
timeType,
startTime,
endTime,
learnType,
assignList,
percentCompleteLive,
percentCompletePicture,
percentCompleteVideo,
trainingStageList,
},
} = res;
const _helpStoreUserIds = helpStoreUsers.map((item) => {
item.nickName = item.storeUserName;
item.userId = item.storeUserId;
return item;
});
// useEffect(() => {
const coverInfo = courseMediaVOS.filter((item) => item.contentType === 'COVER')[0];
const coverUrl = coverInfo ? coverInfo.mediaUrl : defaultCover;
const coverId = coverInfo ? coverInfo.mediaContent : null;
// }, [basicInfo]);
console.log('basicInfo=====>', basicInfo);
const ITEM_BASIC_INFO = {
taskName,
coverUrl,
coverId,
helpStoreUserIds: _helpStoreUserIds,
timeType,
startTime,
endTime,
learnType,
assignList,
// introduce: null, // 培训目的
};
const ITEM_FINISH_STANDARD = {
percentCompleteLive,
percentCompletePicture,
percentCompleteVideo,
};
const _stageList = trainingStageList.map((item) => {
item.contentList = item.contentVOList;
item.type = 'text';
return item;
});
setBasicInfo(ITEM_BASIC_INFO);
setFinishStandard(ITEM_FINISH_STANDARD);
setStageList(_stageList);
});
}
}, []);
// 渲染底部操作按钮
function renderFooter() {
return (
<Choose>
......@@ -90,34 +159,55 @@ function AddTrainTask() {
);
}
// 确认保存新建
function submitRemote(introduceId, issue) {
const { endTime, helpStoreUserIds, learnType, startTime, taskName, timeType, coverId, coverUrl } = basicInfo;
const { percentCompleteLive, percentCompletePicture, percentCompleteVideo } = finishStandard;
const _stageList = stageList.map((item, index) => {
delete item.contentVOList;
delete item.check;
delete item.type;
delete item.liveFailure;
item.sequence = index + 1;
return item;
});
const _helpStoreUserIds = helpStoreUserIds.map((item) => {
return item.userId;
});
const assignList = [
{
assignId: '1379762403510968321',
assignType: 'CUSTOM',
},
];
// const _assignList = assignList((item) => {
// if (item.depType === 'DEP_CUSTOM') {
// // item.
// }
// });
// console.log(assignList);
const commonParams = {
assignList: [
// 指派对象
{
assignId: '',
assignName: '',
assignType: '',
},
],
createId: User.getUserId(),
assignList,
createId: User.getStoreUserId(),
endTime,
helpStoreUserIds, // 协同者集合
helpStoreUserIds: _helpStoreUserIds, // 协同者集合
issueState: issue, // 是否发布
learnType, // 学习模式
scheduleMediaRequests: [
{
contentType: 'COVER',
mediaContent: coverId,
mediaType: 'PICTURE',
mediaUrl: coverUrl,
},
],
scheduleMediaRequests: coverId
? [
{
contentType: 'COVER',
mediaContent: coverId,
mediaType: 'PICTURE',
mediaUrl: coverUrl,
},
]
: [],
startTime,
storeId: User.getStoreId(),
taskName,
......@@ -126,17 +216,60 @@ function AddTrainTask() {
percentCompletePicture,
percentCompleteVideo,
introduceId,
trainingStageList: _stageList,
};
console.log('commonParams:', commonParams);
// if (type === 'edit' && timeType === 'FOREVER') {
// delete commonParams.startTime;
// delete commonParams.endTime;
// }
if (type === 'edit') {
TaskCenterService.updateTrainingTask({ ...commonParams, id: taskId }).then((res) => {
message.success('保存成功');
RCHistory.goBack();
});
} else {
TaskCenterService.createTrainingTask(commonParams).then((res) => {
message.success('保存成功');
RCHistory.goBack();
});
}
Bus.trigger('getTrainingTaskPage');
Bus.trigger('getStoreTaskNum');
}
// 保存
function handleSubmit(issue) {
setStartCheck(true);
const { taskName, assignList } = basicInfo;
const { percentCompleteLive, percentCompleteVideo, percentCompletePicture } = finishStandard;
console.log(percentCompleteLive, percentCompleteVideo, percentCompletePicture);
if (!taskName) {
return message.warning('请输入培训任务名称');
}
// if (assignList.length === 0) {
// return message.warning('请选择指派对象');
// }
if (stageList.length === 0) {
return message.warning('请添加阶段');
}
const stageNameEmpty = stageList.filter((item) => !item.stageName);
if (stageNameEmpty.length > 0) {
return message.warning('请输入阶段名称');
}
const stageNameArr = stageList.map((item) => item.stageName);
const stageNameSet = new Set(stageNameArr);
if (stageNameSet.size !== stageNameArr.length) {
return message.warning('阶段名称不能重复');
}
if (percentCompleteLive === '' || percentCompleteVideo === '' || percentCompletePicture === '') {
return message.warning('请输入完成标准');
}
Upload.uploadTextToOSS(
basicInfo.introduce,
`${randomString()}.txt`,
......@@ -147,39 +280,37 @@ function AddTrainTask() {
);
}
// 返回、取消
function handleGoBack() {
window.RCHistory.goBack();
}
function handleChangeBasicInfo(field, value) {
console.log('handleChangeBasicInfo====>', basicInfo, field, value);
if (field === 'coverUrl') {
setBasicInfo({
...basicInfo,
...basicInfoRef.current,
coverUrl: value.fileUrl,
coverId: value.resourceId,
});
} else if (field === 'trainDate') {
// 固定培训时间,设置起始
setBasicInfo({
...basicInfo,
...basicInfoRef.current,
startTime: value && value[0]?.valueOf(),
endTime: value && value[1]?.valueOf(),
});
} else if (field === 'timeType' && value === 'FOREVER') {
setBasicInfo({
...basicInfo,
...basicInfoRef.current,
[field]: value,
startTime: null,
endTime: null,
});
} else {
let a = {
...basicInfo,
setBasicInfo({
...basicInfoRef.current,
[field]: value,
};
console.log('a====>', a);
setBasicInfo(a);
});
}
}
......@@ -187,10 +318,7 @@ function AddTrainTask() {
if (field === 'stageList') {
setStageList(value);
} else {
setFinishStandard({
...finishStandard,
[field]: value,
});
setFinishStandard(value);
}
}
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-07-28 11:25:58
* @LastEditors: yuananting
* @LastEditTime: 2021-08-09 11:41:40
* @LastEditTime: 2021-08-10 15:10:44
* @Description: 任务中心-培训任务
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -13,6 +13,8 @@ import TrainList from './components/TrainList';
import TaskCenterService from '@/domains/task-center-domain/TaskCenterService';
import DataCenter from '../data-center/Index';
import User from '@/common/js/user';
import Bus from '@/core/bus';
function TrainTaskPage(props) {
const [trainListData, setTrainListData] = useState([]); // 培训任务列表
const [query, setQuery] = useState({
......@@ -28,12 +30,33 @@ function TrainTaskPage(props) {
taskName: '',
});
const [totalCount, setTotalCount] = useState(0); // 总数
const [storeTaskNum, setStoreTaskNum] = useState({});
const { match } = props;
useEffect(() => {
getTrainingTaskPage();
}, [query]);
useEffect(() => {
getStoreTaskNum();
initPageData();
return () => {
removePageData();
};
}, []);
useEffect(() => {}, []);
const initPageData = () => {
Bus.bind('getTrainingTaskPage', getTrainingTaskPage);
Bus.bind('getStoreTaskNum', getStoreTaskNum);
};
const removePageData = () => {
Bus.unbind('getTrainingTaskPage', getTrainingTaskPage);
Bus.unbind('getStoreTaskNum', getStoreTaskNum);
};
// 获取计划列表
function getTrainingTaskPage() {
let _query = _.clone(query);
......@@ -49,6 +72,13 @@ function TrainTaskPage(props) {
});
}
// 获取学院任务数量
function getStoreTaskNum() {
TaskCenterService.getStoreTaskNum({ storeId: User.getStoreId() }).then((res) => {
setStoreTaskNum(res.result);
});
}
// 搜索条件修改
function queryChange(_query) {
setQuery({ ...query, ..._query });
......@@ -59,7 +89,7 @@ function TrainTaskPage(props) {
<div className='content-header'>培训任务</div>
<div className='box'>
<TrainFilter onChange={queryChange} />
<TrainList trainListData={trainListData} query={query} totalCount={totalCount} onChange={queryChange} />
<TrainList trainListData={trainListData} storeTaskNum={storeTaskNum} query={query} totalCount={totalCount} onChange={queryChange} />
</div>
<Route path={`${match.url}/data`} component={DataCenter} />
</div>
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-07-29 14:32:24
* @LastEditors: yuananting
* @LastEditTime: 2021-08-09 17:46:08
* @LastEditTime: 2021-08-11 10:20:33
* @Description: 任务中心-培训任务-新建-基本信息
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
......@@ -24,6 +24,9 @@ const { RangePicker } = DatePicker;
const FormItem = Form.Item;
function BasicInfo(props) {
const taskState = getParameterByName('taskState');
const type = getParameterByName('type');
const { basicInfo, startCheck } = props;
const { taskName, coverUrl, helpStoreUserIds, timeType, startTime, endTime, learnType, assignList, introduce } = basicInfo;
const [imageFile, setImageFile] = useState(null); // 需要被截取的图片
......@@ -94,18 +97,21 @@ function BasicInfo(props) {
}
function confirmAddCollaborator(data) {
const helpStoreUserIds = data.map((item) => item.userId);
props.onChange('helpStoreUserIds', helpStoreUserIds);
props.onChange('helpStoreUserIds', data);
}
function confirmAddAssignor(data) {
props.onChange('assignList', data);
}
function removeSelectedCollaborator(tag, index) {
const _helpStoreUserIds = helpStoreUserIds.filter((item) => item !== tag);
function removeSelectedCollaborator(tag) {
const _helpStoreUserIds = helpStoreUserIds.filter((item) => item.userId !== tag);
props.onChange('helpStoreUserIds', _helpStoreUserIds);
}
function test(data) {
console.log(data);
props.onChange('introduce', data);
// props.onChange('introduce', data);
function removeCheckedAssignor(tag) {
const _assignList = assignList.filter((item) => item.id !== tag.id);
props.onChange('assignList', _assignList);
}
return (
......@@ -148,6 +154,7 @@ function BasicInfo(props) {
<div className='duration__wrap'>
<Radio.Group
value={timeType}
disabled={taskState === 'STARTING'}
onChange={(e) => {
props.onChange('timeType', e.target.value);
}}>
......@@ -172,6 +179,7 @@ function BasicInfo(props) {
}}
disabledDate={disabledDate}
disabledTime={disabledRangeTime}
disabled={[type === 'edit' && taskState === 'STARTING', false]}
value={[startTime ? moment(Number(startTime)) : null, endTime ? moment(Number(endTime)) : null]}
format='YYYY-MM-DD HH:mm'
onChange={(date) => {
......@@ -188,7 +196,7 @@ function BasicInfo(props) {
</FormItem>
<FormItem label='学习模式' required extra={<div className='learning-model-tips'>提示:任务开始后学习模式将不允许更换</div>}>
<div className='learning-model__wrap'>
<Radio.Group value={learnType} onChange={(e) => props.onChange('learnType', e.target.value)}>
<Radio.Group value={learnType} disabled={taskState === 'STARTING'} onChange={(e) => props.onChange('learnType', e.target.value)}>
<Space direction='vertical' size={16}>
<Radio value='FREEDOM'>自由学习</Radio>
<Radio value='BREAKTHROUGH_LEARNING'>
......@@ -214,7 +222,7 @@ function BasicInfo(props) {
<FormItem
label={
<span>
闯关学习
指派对象
<Tooltip title='选择员工协同完成任务指派和督学工作'>
<i
className='icon iconfont'
......@@ -230,7 +238,10 @@ function BasicInfo(props) {
</Tooltip>
</span>
}
required>
required
// validateStatus={startCheck && assignList.length === 0 ? 'error' : ''}
// help={startCheck && assignList.length === 0 && '请选择培训时间'}>
>
<Button
style={{ display: 'block' }}
onClick={() => {
......@@ -238,14 +249,18 @@ function BasicInfo(props) {
}}>
添加指派对象
</Button>
{assignList > 0 && (
{/* {assignList.length > 0 && (
<Space size={'12'} direction={'vertical'} className='select-obj'>
{assignList.length > 0 && (
<div className='obj-type-container'>
<div className='type-title'>已选组织:</div>
<div className='tag-box'>
{_.map(assignList, (item) => {
return <Tag closable>{item.name}</Tag>;
{_.map(assignList, (tag) => {
return (
<Tag key={tag.id} onClose={() => removeCheckedAssignor(tag)} closable>
{tag.name}
</Tag>
);
})}
</div>
</div>
......@@ -254,14 +269,18 @@ function BasicInfo(props) {
<div className='obj-type-container'>
<div className='type-title'>已选学员:</div>
<div className='tag-box'>
{_.map(assignList, (item) => {
return <Tag closable>{item.name}</Tag>;
{_.map(assignList, (tag) => {
return (
<Tag key={tag.id} onClose={() => removeCheckedAssignor(tag)} closable>
{tag.name}
</Tag>
);
})}
</div>
</div>
)}
</Space>
)}
)} */}
</FormItem>
<FormItem
label={
......@@ -291,9 +310,9 @@ function BasicInfo(props) {
</Button>
{helpStoreUserIds.length > 0 && (
<div className='select-obj'>
{_.map(helpStoreUserIds, (tag, index) => {
{_.map(helpStoreUserIds, (tag) => {
return (
<Tag key={tag.id} onClose={() => removeSelectedCollaborator(tag, index)} closable>
<Tag key={tag.userId} onClose={() => removeSelectedCollaborator(tag)} closable>
<WWOpenDataCom type='userName' openid={tag.nickName} />
</Tag>
);
......@@ -310,8 +329,8 @@ function BasicInfo(props) {
detail={{
content: introduce,
}}
onChange={(val, length) => {
test(val);
onChange={(val) => {
props.onChange('introduce', val);
}}
/>
</FormItem>
......@@ -343,14 +362,13 @@ function BasicInfo(props) {
)}
{assignorModalVisible && (
<ChooseAssignorModal
currentAssignorList={assignList}
visible={assignorModalVisible}
type='CUSTOMER'
treeDepType={'DEP_CHAT'}
close={() => {
// closeChooseMembersModal();
onClose={() => {
setAssignorModalVisible(false);
}}
onConfirm={() => {
// confirmAddCustomer();
onConfirm={(data) => {
confirmAddAssignor(data);
}}
/>
)}
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-08-01 17:28:30
* @LastEditors: yuananting
* @LastEditTime: 2021-08-09 14:25:18
* @LastEditTime: 2021-08-11 11:27:09
* @Description: 新建培训任务-关联课程抽屉
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
......@@ -83,7 +83,7 @@ class RelatedCourseDrawer extends Component {
currentPictureCourseListData: [], //页面中已关联的线上课程
activeKey: 'live',
currentTaskCourseData: [],
currentTaskContentData: props.data[props.selectedStageIndex].contentList || [],
};
}
......@@ -97,11 +97,12 @@ class RelatedCourseDrawer extends Component {
handleFetchLiveDataList = () => {
const { liveQuery, liveSize } = this.state;
const _data = [...this.props.data];
console.log('data=========>', _data);
let currentLiveCourseListData = [];
_data.map((item) => {
item.contentList.map((childItem, childIndex) => {
if (childItem.courseType === 'LIVE') {
currentLiveCourseListData.push(childItem.courseId);
currentLiveCourseListData.push(childItem.contentId);
}
return childItem;
});
......@@ -379,14 +380,14 @@ class RelatedCourseDrawer extends Component {
};
selectLiveList = (record, selected) => {
const { selectVideo, currentTaskCourseData, selectLive, selectPicture } = this.state;
const { selectVideo, currentTaskContentData, selectLive, selectPicture } = this.state;
let _list = [];
if (selected || !_.find(selectLive, (item) => item.liveCourseId === record.liveCourseId)) {
_list = _.uniq(selectLive.concat([record]), false, (item) => item.liveCourseId);
} else {
_list = _.reject(selectLive, (item) => item.liveCourseId === record.liveCourseId);
}
if (_list.length + currentTaskCourseData.length + selectVideo.internal.length + selectVideo.external.length + selectPicture.length > 20) {
if (_list.length + currentTaskContentData.length + selectVideo.internal.length + selectVideo.external.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
}
......@@ -394,7 +395,7 @@ class RelatedCourseDrawer extends Component {
};
selectVideoList = (record, selected) => {
const { selectVideo, currentTaskCourseData, selectLive, selectPicture, videoCourseDivision } = this.state;
const { selectVideo, currentTaskContentData, selectLive, selectPicture, videoCourseDivision } = this.state;
let { [videoCourseDivision]: selectList } = selectVideo;
let otherVideoCourseDivision = videoCourseDivision === 'internal' ? 'external' : 'internal';
......@@ -404,7 +405,7 @@ class RelatedCourseDrawer extends Component {
} else {
_list = _.reject(selectList, (item) => item.id === record.id);
}
if (_list.length + selectVideo[otherVideoCourseDivision]?.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20) {
if (_list.length + selectVideo[otherVideoCourseDivision]?.length + currentTaskContentData.length + selectLive.length + selectPicture.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
}
......@@ -417,14 +418,14 @@ class RelatedCourseDrawer extends Component {
};
selectPictureList = (record, selected) => {
const { selectVideo, currentTaskCourseData, selectLive, selectPicture } = this.state;
const { selectVideo, currentTaskContentData, selectLive, selectPicture } = this.state;
let _list = [];
if (selected || !_.find(selectPicture, (item) => item.id === record.id)) {
_list = _.uniq(selectPicture.concat([record]), false, (item) => item.id);
} else {
_list = _.reject(selectPicture, (item) => item.id === record.id);
}
if (_list.length + currentTaskCourseData.length + selectLive.length + selectVideo.internal.length + selectVideo.external.length > 20) {
if (_list.length + currentTaskContentData.length + selectLive.length + selectVideo.internal.length + selectVideo.external.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
}
......@@ -485,7 +486,8 @@ class RelatedCourseDrawer extends Component {
return _item;
});
};
renderFooter = () => {
renderTipFooter = () => {
const { activeKey } = this.state;
let href = '';
switch (activeKey) {
......@@ -544,13 +546,14 @@ class RelatedCourseDrawer extends Component {
videoSearchDefalt,
videoTotalCount,
selectVideo,
currentTaskCourseData,
currentTaskContentData,
selectPicture,
pictureDataSource,
pictureSize,
pictureQuery,
pictureTotalCount,
videoCourseDivision,
activeKey,
} = this.state;
return (
......@@ -600,7 +603,7 @@ class RelatedCourseDrawer extends Component {
</div>
</div>
<div className='related-box'>
该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20 - currentTaskCourseData.length}
该任务已关联{currentTaskContentData.length}个课程,可继续选择{20 - currentTaskContentData.length}
</div>
</div>
<div>
......@@ -627,10 +630,13 @@ class RelatedCourseDrawer extends Component {
} else {
_list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId));
}
if (_list.length + currentTaskCourseData.length + selectVideo.internal.length + selectVideo.external.length + selectPicture.length > 20) {
if (
_list.length + currentTaskContentData.length + selectVideo.internal.length + selectVideo.external.length + selectPicture.length >
20
) {
message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength =
_list.length + currentTaskCourseData.length + selectVideo.internal.length + selectVideo.external.length + +selectPicture.length - 20;
_list.length + currentTaskContentData.length + selectVideo.internal.length + selectVideo.external.length + +selectPicture.length - 20;
_list.splice(_list.length - extraLength, extraLength);
}
this.setState({ selectLive: _list });
......@@ -696,7 +702,7 @@ class RelatedCourseDrawer extends Component {
</div>
</div>
<div className='related-box'>
该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20 - currentTaskCourseData.length}
该任务已关联{currentTaskContentData.length}个课程,可继续选择{20 - currentTaskContentData.length}
</div>
</div>
<div>
......@@ -725,14 +731,18 @@ class RelatedCourseDrawer extends Component {
_list = _.reject(selectVideo[videoCourseDivision], (item) => _.find(changeRows, (data) => data.id === item.id));
}
if (
_list.length + selectVideo[otherVideoCourseDivision]?.length + currentTaskCourseData.length + selectLive.length + selectPicture.length >
_list.length +
selectVideo[otherVideoCourseDivision]?.length +
currentTaskContentData.length +
selectLive.length +
selectPicture.length >
20
) {
message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength =
_list.length +
selectVideo[otherVideoCourseDivision]?.length +
currentTaskCourseData.length +
currentTaskContentData.length +
selectLive.length +
selectPicture.length -
20;
......@@ -806,7 +816,7 @@ class RelatedCourseDrawer extends Component {
</div>
</div>
<div className='related-box'>
该任务已关联{currentTaskCourseData.length}个课程,可继续选择{20 - currentTaskCourseData.length}
该任务已关联{currentTaskContentData.length}个课程,可继续选择{20 - currentTaskContentData.length}
</div>
</div>
<div>
......@@ -833,10 +843,10 @@ class RelatedCourseDrawer extends Component {
} else {
_list = _.reject(selectPicture, (item) => _.find(changeRows, (data) => data.id === item.id));
}
if (_list.length + currentTaskCourseData.length + selectVideo.internal.length + selectVideo.external.length + selectLive.length > 20) {
if (_list.length + currentTaskContentData.length + selectVideo.internal.length + selectVideo.external.length + selectLive.length > 20) {
message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength =
_list.length + currentTaskCourseData.length + selectVideo.internal.length + selectVideo.external.length + selectLive.length - 20;
_list.length + currentTaskContentData.length + selectVideo.internal.length + selectVideo.external.length + selectLive.length - 20;
_list.splice(_list.length - extraLength, extraLength);
}
this.setState({ selectPicture: _list });
......@@ -870,6 +880,7 @@ class RelatedCourseDrawer extends Component {
</Tabs>
</div>
<div className='footer shrink-footer'>
{!(activeKey === 'video' && videoCourseDivision === 'external') && this.renderTipFooter()}
<Button onClick={this.props.onClose}>取消</Button>
<Button
type='primary'
......
......@@ -125,12 +125,11 @@
position: fixed;
right: 0;
bottom: 0;
height: 50px;
width: 720px;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 24px;
padding: 16px 24px;
background: #fff;
border-top: 1px solid #e8e8e8;
z-index: 9999;
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-08-03 17:05:32
* @LastEditors: yuananting
* @LastEditTime: 2021-08-09 14:36:05
* @LastEditTime: 2021-08-11 11:49:11
* @Description: 新建培训任务-关联考试抽屉
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
......@@ -20,7 +20,6 @@ import Bus from '@/core/bus';
function RelatedExamDrawer(props) {
const [showPaperModal, setShowPaperModal] = useState(false);
const [paperInfo, setPaperInfo] = useState({});
const [basicInfo, setBasicInfo] = useState(props.basicInfo);
const [answerAnalysis, setAnswerAnalysis] = useState('RIGHT_OR_WRONG'); // 答案与解析-默认仅显示对错
const [examDesc, setExamDesc] = useState(''); // 考试说明
const [examDuration, setExamDuration] = useState(null); // 考试时长
......@@ -36,6 +35,8 @@ function RelatedExamDrawer(props) {
const [check, setCheck] = useState(false);
const [examTotal, setExamTotal] = useState(0);
const [samePaper, setSamePaper] = useState(false);
const request = useRef(false);
useEffect(() => {
......@@ -55,11 +56,6 @@ function RelatedExamDrawer(props) {
setExamTotal(paperInfo.singleChoiceCnt + paperInfo.multiChoiceCnt + paperInfo.judgeCnt + paperInfo.gapFillingCnt + paperInfo.indefiniteChoiceCnt || 0);
}, [paperInfo.paperId, passRate]);
function disabledDate(current) {
// Can not select days before today and today
return current && current < moment().startOf('day');
}
function handleSave() {
if (request.current) {
return;
......@@ -75,23 +71,28 @@ function RelatedExamDrawer(props) {
resultShow,
resultContent,
answerAnalysis,
examEndTime: basicInfo.endTime,
examStartTime: basicInfo.startTime || new Date().valueOf(), // 新建(永久)取当前时间,固定取培训开始,编辑取培训创建的时间
examEndTime: props.basicInfo.endTime || null,
examStartTime: props.basicInfo.startTime || new Date().valueOf(),
passScore,
source: 0,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
};
console.log('sfkajsdfiohasiudfhaiusdfhiaus', params);
console.log('新建入参==============>', params);
if (
!paperId ||
samePaper ||
!examName ||
examName.length > 40 ||
!passRate ||
!examDuration ||
editorTextLength > 1000 ||
(basicInfo.timeType === 'VALIDITY' && basicInfo.startTime && basicInfo.endTime && basicInfo.startTime + examDuration * 60 * 1000 > basicInfo.endTime)
(props.basicInfo.timeType === 'VALIDITY' &&
props.basicInfo.startTime &&
props.basicInfo.endTime &&
props.basicInfo.startTime + examDuration * 60 * 1000 > props.basicInfo.endTime)
) {
return;
}
......@@ -102,7 +103,7 @@ function RelatedExamDrawer(props) {
}, 2000);
TaskCenterService.createTrainingExam(params).then((res) => {
console.log(res);
props.onSave(res.result);
});
}
......@@ -110,7 +111,11 @@ function RelatedExamDrawer(props) {
<Drawer title='添加考试' width={720} maskClosable={false} closable={true} onClose={props.onClose} visible={true} mask className='related-exam-drawer'>
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 20 }} layout='horizontal'>
<div className='module-title'>基本信息</div>
<Form.Item label='选择试卷' validateStatus={check && !paperId ? 'error' : ''} help={check && !paperId && '请选择试卷'} required>
<Form.Item
label='选择试卷'
validateStatus={(check && !paperId) || samePaper ? 'error' : ''}
help={(check && !paperId && '请选择试卷') || (samePaper && '已存在相同试卷')}
required>
<Button
onClick={() => {
setShowPaperModal(true);
......@@ -199,20 +204,20 @@ function RelatedExamDrawer(props) {
validateStatus={
check &&
(!examDuration ||
(basicInfo.timeType === 'VALIDITY' &&
basicInfo.startTime &&
basicInfo.endTime &&
basicInfo.startTime + examDuration * 60 * 1000 > basicInfo.endTime))
(props.basicInfo.timeType === 'VALIDITY' &&
props.basicInfo.startTime &&
props.basicInfo.endTime &&
props.basicInfo.startTime + examDuration * 60 * 1000 > props.basicInfo.endTime))
? 'error'
: ''
}
help={
check &&
((!examDuration && '请输入考试时长') ||
(basicInfo.timeType === 'VALIDITY' &&
basicInfo.startTime &&
basicInfo.endTime &&
basicInfo.startTime + examDuration * 60 * 1000 > basicInfo.endTime &&
(props.basicInfo.timeType === 'VALIDITY' &&
props.basicInfo.startTime &&
props.basicInfo.endTime &&
props.basicInfo.startTime + examDuration * 60 * 1000 > props.basicInfo.endTime &&
'考试时长不得超过培训有效期时长'))
}
required>
......@@ -300,6 +305,17 @@ function RelatedExamDrawer(props) {
{showPaperModal && (
<SelectPaperModal
onSelect={(info) => {
const contentList = props.stageList.map((item) => {
return item.contentList;
});
const existedPaperId = contentList.flat().filter((item) => {
return item.paperId === info.paperId;
});
if (existedPaperId.length > 0) {
setSamePaper(true);
} else {
setSamePaper(false);
}
setPaperInfo(info);
}}
paperInfo={paperInfo}
......
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-07-30 16:33:58
* @LastEditors: yuananting
* @LastEditTime: 2021-08-09 13:56:24
* @LastEditTime: 2021-08-11 13:33:44
* @Description: 任务中心-培训任务-新建-培训内容
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
......@@ -64,85 +64,7 @@ class TrainContent extends Component {
};
}
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 });
});
};
componentDidMount() {}
setTrianTypeOption = (index) => {
return (
......@@ -196,8 +118,7 @@ class TrainContent extends Component {
}
};
handleRenameStageName = (e, item) => {
const { value } = e.target;
handleRenameStageName = (value, item) => {
const { stageList } = this.state;
item.stageName = value;
item.check = true;
......@@ -211,9 +132,15 @@ class TrainContent extends Component {
);
};
handleStageNameBlur = (e, item) => {
handleStageNameBlur = (e, item, currentIndex) => {
const { value } = e.target;
const { stageList } = this.state;
const sameStageName = stageList.filter((item, index) => {
return item.stageName === value && index !== currentIndex;
});
if (sameStageName.length > 0) {
return;
}
let input = /^[\s]*$/;
if (value && !input.test(value)) {
item.type = 'text';
......@@ -271,6 +198,36 @@ class TrainContent extends Component {
);
};
handleDeleteCourse = (parentIndex, index) => {
return confirm({
title: '删除学习内容',
content: '确定删除该学习内容吗?',
icon: <span className='icon iconfont default-confirm-icon'>&#xe839; </span>,
okText: '删除',
okType: 'danger',
cancelText: '取消',
onOk: () => {
this.handleConfirmDeleteCourse(parentIndex, index);
},
});
};
handleConfirmDeleteCourse = (parentIndex, index) => {
const { stageList } = this.state;
const _stageList = [...stageList];
const selectData = [..._stageList[parentIndex].contentList];
selectData.splice(index, 1);
_stageList[parentIndex].contentList = selectData;
this.setState(
{
stageList: _stageList,
},
() => {
this.props.onChange(_stageList);
}
);
};
renderStageInfo = (item, index) => {
const { startCheck } = this.props;
return (
......@@ -291,10 +248,10 @@ class TrainContent extends Component {
maxLength={20}
value={item.stageName}
onChange={(e) => {
this.handleRenameStageName(e, item);
this.handleRenameStageName(e.target.value, item);
}}
onBlur={(e) => {
this.handleStageNameBlur(e, item);
this.handleStageNameBlur(e, item, index);
}}
/>
</Form.Item>
......@@ -356,16 +313,16 @@ class TrainContent extends Component {
};
renderContentItem = (record, index, parentIndex) => {
const { courseState, courseName, courseType, courseChapterNum } = record;
const { courseState, contentName, contentType, courseType, courseChapterNum } = record;
return (
<div className='sort-content-item'>
<div className='content-info'>
<img className='type-option-icon' src={ENUM.LearningContentIcon[courseType]} />
<img className='type-option-icon' src={ENUM.LearningContentIcon[courseType || contentType]} />
<span className='content-name'>
{parentIndex + 1}.{index + 1} {courseName}
{parentIndex + 1}.{index + 1} {contentName}
</span>
{courseState === 'EXPIRED' && <span className='icon iconfont tip'>&#xe834;</span>}
{courseType === 'LIVE' && <span className='extra-info'>{courseStateShow[record.courseState].title}</span>}
{courseType === 'LIVE' && <span className='extra-info'>{courseStateShow[courseState].title}</span>}
{courseType === 'VOICE' && <span className='extra-info'>(共{courseChapterNum || 1}小节)</span>}
</div>
<div className='content-operate'>
......@@ -388,9 +345,7 @@ class TrainContent extends Component {
const { stageList } = this.state;
const stageObj = {
stageName: '',
index: stageList.length,
type: 'input',
open: true,
contentList: [],
check: false,
};
......@@ -422,7 +377,17 @@ class TrainContent extends Component {
const { stageList } = this.state;
const _stageList = [...stageList];
const selectData = [..._stageList[selectedStageIndex].contentList];
const _selectData = [...selectData, ...selectList];
let _selectList = [];
selectList.forEach((item) => {
_selectList.push({
contentId: item.courseId,
contentName: item.courseName,
contentType: 'COURSE',
courseType: item.courseType,
courseState: item.courseState,
});
});
const _selectData = [...selectData, ..._selectList];
_stageList[selectedStageIndex].contentList = _selectData;
this.setState(
{
......@@ -430,24 +395,34 @@ class TrainContent extends Component {
stageList: _stageList,
},
() => {
console.log('_stageList=============>', _stageList);
this.props.onChange('stageList', _stageList);
}
);
};
percentCompleteBlur = (e, field) => {
let _percentCompleteLive;
const { value } = e.target;
if (value > 100) {
_percentCompleteLive = 100;
} else {
if (value < 0) {
_percentCompleteLive = 0;
} else {
_percentCompleteLive = value;
confirmCreateExam = (examInfo) => {
const { selectedStageIndex } = this.state;
const { stageList } = this.state;
const _stageList = [...stageList];
const selectData = [..._stageList[selectedStageIndex].contentList];
const _examInfo = {
contentId: examInfo.examId,
contentName: examInfo.examName,
contentType: 'EXAM',
paperId: examInfo.paperId,
};
selectData.push(_examInfo);
_stageList[selectedStageIndex].contentList = selectData;
this.setState(
{
showExamDrawer: false,
stageList: _stageList,
},
() => {
this.props.onChange('stageList', _stageList);
}
}
this.props.onChange(field, _percentCompleteLive);
);
};
changePercentComplete = (e, field) => {
......@@ -477,7 +452,8 @@ class TrainContent extends Component {
};
render() {
const { stageList, showCourseDrawer, showExamDrawer, expiredCourseList, showStandardDetail, finishStandard, basicInfo } = this.state;
const { stageList, showCourseDrawer, showExamDrawer, expiredCourseList, showStandardDetail, finishStandard, basicInfo, selectedStageIndex } = this.state;
console.log('finishStandard==================>', finishStandard);
const { percentCompleteLive, percentCompleteVideo, percentCompletePicture } = finishStandard;
const { startCheck } = this.props;
......@@ -495,23 +471,30 @@ class TrainContent extends Component {
+ 添加阶段
</div>
)}
{showCourseDrawer && <RelatedCourseDrawer data={stageList} onClose={this.onCloseCourseDrawer} onSelect={this.confirmSelectCourse} />}
{showExamDrawer && <RelatedExamDrawer basicInfo={basicInfo} onClose={this.onCloseExamDrawer} />}
</div>
<div className='expired-info__wrap'>
<div className='module-title'>失效课程</div>
<ExpiredCourseList expiredCourseList={expiredCourseList} />
{showCourseDrawer && (
<RelatedCourseDrawer
data={stageList}
selectedStageIndex={selectedStageIndex}
onClose={this.onCloseCourseDrawer}
onSelect={this.confirmSelectCourse}
/>
)}
{showExamDrawer && <RelatedExamDrawer basicInfo={basicInfo} stageList={stageList} onClose={this.onCloseExamDrawer} onSave={this.confirmCreateExam} />}
</div>
{window.getParameterByName('type') === 'edit' && expiredCourseList.length > 0 && (
<div className='expired-info__wrap'>
<div className='module-title'>失效课程</div>
<ExpiredCourseList expiredCourseList={expiredCourseList} />
</div>
)}
<div className='finish-standard__warp'>
<div className='module-title'>
<div
className='module-title'
onClick={() => {
this.setState({ showStandardDetail: !showStandardDetail });
}}>
高级设置
<span
className={`icon iconfont ${showStandardDetail && 'rotate-arrow'}`}
onClick={() => {
this.setState({ showStandardDetail: !showStandardDetail });
}}>
&#xe614;
</span>
<span className={`icon iconfont ${showStandardDetail && 'rotate-arrow'}`}>&#xe614;</span>
</div>
{showStandardDetail && (
<div className='detail-container'>
......@@ -523,7 +506,9 @@ class TrainContent extends Component {
</span>
<span>
直播课单个课程,学员学习进度达到
<FormItem validateStatus={startCheck && !percentCompleteLive ? 'error' : ''} help={startCheck && !percentCompleteLive && '请输入完成标准'}>
<FormItem
validateStatus={startCheck && percentCompleteLive === '' ? 'error' : ''}
help={startCheck && percentCompleteLive === '' && '请输入完成标准'}>
<Input
value={percentCompleteLive}
onChange={(e) => {
......@@ -540,8 +525,8 @@ class TrainContent extends Component {
<span>
线上课单个课节,学员学习进度达到
<FormItem
validateStatus={startCheck && !percentCompleteVideo ? 'error' : ''}
help={startCheck && !percentCompleteVideo && '请输入完成标准'}>
validateStatus={startCheck && percentCompleteVideo === '' ? 'error' : ''}
help={startCheck && percentCompleteVideo === '' && '请输入完成标准'}>
<Input
value={percentCompleteVideo}
onChange={(e) => {
......@@ -558,8 +543,8 @@ class TrainContent extends Component {
<span>
图文课单个课程,学员学习进度达到
<FormItem
validateStatus={startCheck && !percentCompletePicture ? 'error' : ''}
help={startCheck && !percentCompletePicture && '请输入完成标准'}>
validateStatus={startCheck && percentCompletePicture === '' ? 'error' : ''}
help={startCheck && percentCompletePicture === '' && '请输入完成标准'}>
<Input
value={percentCompletePicture}
onChange={(e) => {
......
......@@ -23,18 +23,19 @@
}
.finish-standard__warp {
margin-top: 24px;
.module-title {
height: 22px;
font-size: 16px;
color: #333333;
line-height: 22px;
cursor: pointer;
.icon {
font-size: 12px;
margin-left: 8px;
color: #5e606a;
display: inline-block;
vertical-align: middle;
cursor: pointer;
}
.rotate-arrow {
transform: (rotate(180deg));
......
......@@ -2,17 +2,21 @@
* @Author: yuananting
* @Date: 2021-07-28 14:56:52
* @LastEditors: yuananting
* @LastEditTime: 2021-08-09 11:13:14
* @LastEditTime: 2021-08-11 11:01:49
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useState, useEffect } from 'react';
import { Route, withRouter } from 'react-router-dom';
import { Tooltip, Checkbox, Dropdown, Radio, Button, Space, Badge } from 'antd';
import { Tooltip, Checkbox, Dropdown, Radio, Button, Space, Modal, message } from 'antd';
import './TrainList.less';
import { XMTable, PageControl } from '@/components';
import User from '@/common/js/user';
import ENUM from '../../enum';
import TaskCenterService from '@/domains/task-center-domain/TaskCenterService';
import ShareTrainTaskModal from '../modal/ShareTrainTaskModal';
import { LIVE_SHARE } from '@/domains/course-domain/constants';
import Bus from '@/core/bus';
function TrainList(props) {
const {
......@@ -21,14 +25,110 @@ function TrainList(props) {
match,
} = props;
const [shareTrainTaskModal, setShareTrainTaskModal] = useState(null);
// 发布或取消发布培训任务
function updateIssueStateTrain(taskId, issueState) {
Modal.confirm({
title: '提示',
content:
issueState === 'YES'
? '发布后,被指派学员将任务列表中看到该任务,确定要发布?'
: '取消发布后,任务对学员暂不可见,可能会影响正在学习学员,确定要取消?',
okText: '确定',
cancelText: '取消',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => {
TaskCenterService.updateIssueStateTraining({
taskId,
issueState,
}).then((res) => {
message.success(issueState === 'YES' ? '发布成功' : '取消发布成功');
Bus.trigger('getTrainingTaskPage');
Bus.trigger('getStoreTaskNum');
});
},
});
}
// 删除培训任务
function deleteTrainTask(taskId) {
Modal.confirm({
title: '你确定要删除吗?',
content: '删除后,此培训任务的用户将无法继续学习,所有学习数据将同步删除不可恢复',
okText: '确定',
cancelText: '取消',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => {
TaskCenterService.deleteTrainingTask({
taskId,
}).then((res) => {
message.success('删除成功');
Bus.trigger('getTrainingTaskPage');
Bus.trigger('getStoreTaskNum');
});
},
});
}
// 编辑培训任务-跳转新建/编辑页
function editTrainingTask(item) {
window.RCHistory.push({
pathname: `/create-train-task?type=edit&taskId=${item.taskId}&taskState=${item.taskState}`,
});
}
// 显示分享弹窗
function handleShowShareModal(item) {
const htmlUrl = `${LIVE_SHARE}training_task_detail/${item.taskId}?id=${User.getStoreId()}&storeUserId=${User.getStoreUserId()}`;
const longUrl = htmlUrl;
const shareData = { ...item, longUrl };
const shareTrainTaskModal = (
<ShareTrainTaskModal
data={shareData}
type='liveClass'
close={() => {
setShareTrainTaskModal(null);
}}
/>
);
setShareTrainTaskModal(shareTrainTaskModal);
}
function renderMoreOperate(item) {
const isUnableIssue = item.issueState === 'YES'; // 已发布
const isUnableAbleEdit = item.issueState === 'YES' || item.taskState === 'FINISH'; // 已发布或已结束
const isUnableShare = item.issueState === 'NO' || item.taskState === 'FINISH'; //未发布或已结束
return (
<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>
{!isUnableIssue && (
<div className='operate__item' onClick={() => updateIssueStateTrain(item.taskId, 'YES')}>
发布
</div>
)}
{isUnableIssue && (
<div className='operate__item' onClick={() => updateIssueStateTrain(item.taskId, 'NO')}>
取消发布
</div>
)}
<div
className={`operate__item ${isUnableAbleEdit && 'disabled'} `}
onClick={() => {
editTrainingTask(item);
}}>
编辑
</div>
<div className={`operate__item ${isUnableShare && 'disabled'} `} onClick={() => handleShowShareModal(item)}>
分享
</div>
{/* <div className='operate__item'>审批作业</div> */}
<div
className='operate__item'
onClick={() => {
deleteTrainTask(item.taskId);
}}>
删除
</div>
</div>
);
}
......@@ -132,7 +232,7 @@ function TrainList(props) {
},
{
title: '培训时间',
width: '14%',
width: '12.5%',
key: 'trainTime',
dataIndex: 'trainTime',
render: (val, record) => {
......@@ -140,7 +240,7 @@ function TrainList(props) {
return <span>不限时</span>;
}
return (
<span style={{ whiteSpace: 'nowrap' }}>
<span>
{window.formatDate('YYYY-MM-DD H:i', record.startTime)}~{window.formatDate('YYYY-MM-DD H:i', record.endTime)}
</span>
);
......@@ -194,9 +294,9 @@ function TrainList(props) {
</div>
<span className='split'> | </span>
<div
className='operate__item'
className={`operate__item ${record.taskState === 'FINISH' && 'disabled'} `}
onClick={() => {
handleShowShareModal(record);
// handleShowShareModal(record);
}}>
指派
</div>
......@@ -305,12 +405,12 @@ function TrainList(props) {
onChange={(e) => {
handleChangeQuery('issueState', e.target.value);
}}>
<Radio.Button value='ALL'>全部(2)</Radio.Button>
<Radio.Button value='YES'>已发布(2)</Radio.Button>
<Radio.Button value='NO'>未发布(10)</Radio.Button>
<Radio.Button value='ALL'>全部({props.storeTaskNum.allNum})</Radio.Button>
<Radio.Button value='YES'>已发布({props.storeTaskNum.issueNum})</Radio.Button>
<Radio.Button value='NO'>未发布({props.storeTaskNum.notIssueNum})</Radio.Button>
</Radio.Group>
<Checkbox style={{ lineHeight: '32px' }} value={myAssist} onChange={(e) => handleChangeQuery('myAssist', e.target.checked)}>
只看我协同的 ({10})
只看我协同的 ({props.storeTaskNum.myAssistNum})
</Checkbox>
</Space>
</div>
......@@ -342,6 +442,7 @@ function TrainList(props) {
/>
</div>
</div>
{shareTrainTaskModal}
</div>
);
}
......
......@@ -109,3 +109,9 @@
}
}
}
.disabled {
color: #ccc !important;
cursor: not-allowed !important;
pointer-events: none !important;
}
......@@ -2,7 +2,7 @@
* @Author: yuananting
* @Date: 2021-08-05 17:09:36
* @LastEditors: yuananting
* @LastEditTime: 2021-08-06 17:23:31
* @LastEditTime: 2021-08-10 20:21:36
* @Description: 新建培训任务-选择指派对象
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
......@@ -21,6 +21,7 @@ import StoreService from '@/domains/store-domain/storeService';
// import SearchUser from '../components/SearchUser';
import WWOpenDataCom from '@/components/WWOpenDataCom';
import './ChooseAssignorModal.less';
import Item from 'antd/lib/list/Item';
// import _ from 'underscore';
const { Search } = Input;
......@@ -30,6 +31,8 @@ const { DirectoryTree } = Tree;
function ChooseAssignorModal(props) {
const [structureData, setStructureData] = useState([]);
const [activeKey, setActiveKey] = useState('departMentTab');
const [checkedAssignorList, setCheckedAssignorList] = useState(props.currentAssignorList || []); // 勾选的指派对象
const [checkedAssignorKeys, setCheckedAssignorKeys] = useState(props.currentAssignorList.map((item) => item.id) || []);
useEffect(() => {
getStructureData();
......@@ -53,6 +56,9 @@ function ChooseAssignorModal(props) {
function handleStructureData(dataArray) {
const _dataArray = dataArray.map((item, index) => {
item.key = item.id;
if (item.depLevel === 0) {
item.disableCheckbox = true;
}
if (item.sonDepartmentVOList) {
item.children = item.sonDepartmentVOList;
handleStructureData(item.sonDepartmentVOList);
......@@ -62,17 +68,40 @@ function ChooseAssignorModal(props) {
return _dataArray;
}
function onSelectAssignor(value) {}
function onCheckAssignor(key, e) {
const { node, checked } = e;
let _checkedAssignorList = [...checkedAssignorList];
if (checked) {
_checkedAssignorList.push(node);
} else {
_checkedAssignorList = checkedAssignorList.filter((item) => item.id !== node.id);
}
const _checkedAssignorKeys = _checkedAssignorList.map((item) => item.id);
setCheckedAssignorKeys(_checkedAssignorKeys);
setCheckedAssignorList(_checkedAssignorList);
}
function removeCheckedAssignor(item) {
const _checkedAssignorList = checkedAssignorList.filter((innerItem) => innerItem.id !== item.id);
const _checkedAssignorKeys = _checkedAssignorList.map((item) => item.id);
setCheckedAssignorKeys(_checkedAssignorKeys);
setCheckedAssignorList(_checkedAssignorList);
}
function clearCheckedAssignor() {
setCheckedAssignorKeys([]);
setCheckedAssignorList([]);
}
return (
<Modal
className='choose-assignor-modal'
title='添加指派对象'
visible={true}
// onCancel={props.onClose}
onCancel={props.onClose}
onOk={() => {
// props.onConfirm(seletedAssignorList);
// props.onClose();
props.onConfirm(checkedAssignorList);
props.onClose();
}}
width={680}
maskClosable={false}>
......@@ -97,8 +126,8 @@ function ChooseAssignorModal(props) {
checkable
showIcon={false}
treeData={structureData}
// selectedKeys={selectedKeys}
onSelect={onSelectAssignor}
checkedKeys={checkedAssignorKeys}
onCheck={(key, e) => onCheckAssignor(key, e)}
titleRender={(nodeData) => {
return (
<div className='node-title-div'>
......@@ -124,11 +153,27 @@ function ChooseAssignorModal(props) {
<div className='right-list'>
<div className='header-line'>
<span className='tip-text'>已选择</span>
<span className='clear-btn' onClick={() => {}}>
<span className='clear-btn' onClick={clearCheckedAssignor}>
清空
</span>
</div>
<div className='data-body'></div>
<div className='data-body'>
{checkedAssignorList.map((item, index) => {
return (
<div className='selected-item'>
<span className='item-name'>
<span className='icon iconfont avatar-icon'>&#xe84a;</span>
<Tooltip title={item.name}>
<span className='name-text'>{item.name}</span>
</Tooltip>
</span>
<span className='icon iconfont clear-icon' onClick={() => removeCheckedAssignor(item)}>
&#xe717;
</span>
</div>
);
})}
</div>
</div>
</div>
</Modal>
......
......@@ -89,7 +89,7 @@
padding: 12px 12px 12px 16px;
display: flex;
justify-content: space-between;
.user-name {
.item-name {
width: 80%;
overflow: hidden;
text-overflow: ellipsis;
......
......@@ -207,10 +207,10 @@ function ChooseCollaboratorModal(props) {
{seletedCollaboratorList.map((item, index) => {
return (
<div className='selected-item'>
<span className='user-name'>
<span className='item-name'>
<span className='icon iconfont avatar-icon'>&#xe84a;</span>
<Tooltip title={<WWOpenDataCom type='userName' openid={item.nickName} />}>
<span className='user-name'>
<span className='name-text'>
<WWOpenDataCom type='userName' openid={item.nickName} />
</span>
</Tooltip>
......
......@@ -64,7 +64,7 @@
padding: 12px 12px 12px 16px;
display: flex;
justify-content: space-between;
.user-name {
.item-name {
width: 80%;
overflow: hidden;
text-overflow: ellipsis;
......
/*
* @Author: 吴文洁
* @Date: 2020-07-20 19:12:49
* @Last Modified by: 吴文洁
* @Last Modified time: 2020-07-20 20:25:13
* @Description: 大班直播分享弹窗
*/
import React from 'react';
import { Modal, Button, message } from 'antd';
import domtoimage from 'dom-to-image';
import qrcode from '@/libs/qrcode/qrcode.js';
import User from '@/common/js/user';
import $ from 'jquery';
import CourseService from '@/domains/course-domain/CourseService';
import './ShareTrainTaskModal.less';
const DEFAULT_COVER = 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png';
class ShareTrainTaskModal extends React.Component {
constructor(props) {
super(props);
this.state = {
shareUrl: '',
};
}
componentDidMount() {
// 获取短链接
this.handleConvertShortUrl();
}
handleConvertShortUrl = () => {
const { longUrl } = this.props.data;
// 发请求
CourseService.getQrcode({
urls: [longUrl],
}).then((res) => {
const { result = [] } = res;
this.setState(
{
shareUrl: result[0].shortUrl,
},
() => {
const qrcodeWrapDom = document.querySelector('#qrcodeWrap');
const qrcodeNode = new qrcode({
text: this.state.shareUrl,
size: 98,
});
qrcodeWrapDom.appendChild(qrcodeNode);
const qrcodeWrapDomDownload = document.querySelector('#qrcodeWrap-dowload');
const qrcodeNodeDownLoad = new qrcode({
text: this.state.shareUrl,
size: 196,
});
qrcodeWrapDomDownload.appendChild(qrcodeNodeDownLoad);
}
);
});
};
componentWillUnmount() {
// 页面销毁之前清空定时器
clearTimeout(this.timer);
}
// 下载海报
handleDownloadPoster = () => {
this.setState(
{
showImg: true,
time: new Date().valueOf(),
},
() => {
this.setState({ time: new Date().valueOf() }, () => {
let node = document.getElementById('poster-dowload');
domtoimage.toPng(node).then((imgData) => {
const download = document.createElement('a');
const { planName } = this.props.data;
$(download).attr('href', imgData).attr('download', `${planName}.png`).get(0).click();
});
});
}
);
};
// 复制分享链接
handleCopy = () => {
const textContent = document.getElementById('shareUrl').innerText;
window.copyText(textContent);
message.success('复制成功!');
};
render() {
const { data } = this.props;
const { planName, coverUrl = DEFAULT_COVER } = data;
const { shareUrl, showImg, time } = this.state;
return (
<Modal
title={'分享培训任务'}
width={680}
visible={true}
footer={null}
maskClosable={false}
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
className='share-task-modal'
onCancel={this.props.close}>
<div className='left'>
<div id='poster'>
<div className='store-name'>
<span className='text'>{User.getStoreName()}</span>
</div>
<div className='course-name-title'>邀请你参与培训:</div>
<div className='live-couse-name'>{planName}</div>
<Choose>
<When condition={showImg}>
<img crossOrigin='*' src={coverUrl + `?=${time}`} className='course-cover' alt='' />
</When>
<Otherwise>
<img src={coverUrl + `?=${time}`} className='course-cover' alt='' />
</Otherwise>
</Choose>
<div className='qrcode-wrap'>
<div className='qrcode-wrap__left'>
<div className='text'>长按识别二维码进入观看</div>
<img className='finger' src='https://image.xiaomaiketang.com/xm/thpkWDwJsC.png' alt='' />
</div>
<div className='qrcode-wrap__right' id='qrcodeWrap'></div>
</div>
</div>
<div id='poster-dowload'>
<div className='store-name'>
<span className='text'>{User.getStoreName()}</span>
</div>
<div className='course-name-title'>邀请你参与培训:</div>
<div className='live-couse-name'>{planName}</div>
<Choose>
<When condition={showImg}>
<img crossOrigin='*' src={coverUrl + `?=${time}`} className='course-cover' alt='' />
</When>
<Otherwise>
<img src={coverUrl + `?=${time}`} className='course-cover' alt='' />
</Otherwise>
</Choose>
<div className='qrcode-wrap'>
<div className='qrcode-wrap__left'>
<div className='text'>长按识别二维码进入观看</div>
<img className='finger' src='https://image.xiaomaiketang.com/xm/thpkWDwJsC.png' alt='' />
</div>
<div className='qrcode-wrap__right' id='qrcodeWrap-dowload'></div>
</div>
</div>
</div>
<div className='right'>
<div className='share-poster right__item'>
<div className='title'>① 海报分享</div>
<div className='sub-title'>学员可通过微信扫描海报二维码,查看培训计划</div>
<div className='content' onClick={this.handleDownloadPoster}>
下载海报
</div>
</div>
<div className='share-url right__item'>
<div className='title'>② 链接分享</div>
<div className='sub-title'>学员可通过微信或浏览器打开以下链接,查看培训计划</div>
<div className='content url-content'>
<div className='share-url' id='shareUrl'>
{shareUrl}
</div>
<Button type='primary' onClick={this.handleCopy}>
复制
</Button>
</div>
</div>
</div>
</Modal>
);
}
}
export default ShareTrainTaskModal;
.share-task-modal {
.ant-modal-body {
display: flex;
height: 510px !important;
overflow: hidden !important;
.left {
width: 303px;
margin: 0 32px 0 16px;
box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.05);
border-radius: 12px;
#poster {
background: #fff;
margin: 0;
padding: 20px;
margin-bottom: 140px;
.course-name-title {
font-size: 14px;
color: #333;
line-height: 20px;
margin-bottom: 4px;
}
.live-couse-name {
font-size: 16px;
color: #333333;
font-weight: 600;
}
.course-name {
color: #333;
font-size: 16px;
font-weight: 600;
line-height: 20px;
}
.course-cover {
width: 263px;
height: 143px;
border-radius: 6px;
margin-top: 8px;
}
.qrcode-wrap {
padding: 0 16px;
display: flex;
align-items: center;
margin: 24px 0 16px 0;
&__left {
width: 98px;
text-align: center;
margin-right: 22px;
.text {
line-height: 20px;
}
.finger {
width: 40px;
height: 40px;
margin-top: 8px;
}
}
&__right {
width: 110px;
height: 110px;
padding: 6px;
}
}
.store-name {
// padding: 8px 16px;
display: flex;
align-items: center;
margin-bottom: 8px;
.text {
font-size: 12px;
color: #999;
font-size: 14px;
line-height: 20px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 100%;
}
}
}
#poster-dowload {
background: #fff;
margin: 0;
padding: 40px;
width: 606px;
.course-name-title {
font-size: 28px;
color: #333;
line-height: 40px;
margin-bottom: 8px;
}
.live-couse-name {
font-size: 32px;
color: #333333;
font-weight: 600;
}
.course-name {
color: #333;
font-size: 32px;
font-weight: 600;
line-height: 40px;
}
.course-cover {
width: 526px;
height: 286px;
border-radius: 6px;
margin-top: 16px;
}
.qrcode-wrap {
padding: 0 32px;
display: flex;
align-items: center;
margin: 48px 0 32px 0;
&__left {
width: 196px;
text-align: center;
margin-right: 44px;
.text {
line-height: 40px;
}
.finger {
width: 80px;
height: 80px;
margin-top: 16px;
}
}
&__right {
width: 220px;
height: 220px;
padding: 12px;
}
}
.store-name {
// padding: 8px 16px;
display: flex;
align-items: center;
margin-bottom: 16px;
.text {
font-size: 12px;
color: #999;
font-size: 28px;
line-height: 40px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 100%;
}
}
}
}
.right {
.title {
color: #333;
font-weight: 500;
}
.sub-title {
color: #999;
margin-top: 16px;
}
.content {
display: flex;
align-items: center;
margin-top: 8px;
.share-url {
width: 212px;
overflow: hidden;
height: 28px;
line-height: 28px;
border-radius: 4px 0 0 4px;
padding-left: 12px;
white-space: nowrap;
color: #999999;
background: #efefef;
}
.ant-btn {
margin-left: -2px;
}
}
.url-content {
position: relative;
&:after {
content: '';
width: 12px;
height: 22px;
background: #efefef;
position: absolute;
right: 71px;
}
}
.share-poster {
margin-bottom: 40px;
.content {
color: #2966ff;
cursor: pointer;
}
}
}
}
}
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