Commit 1e2f3e0e by yuananting

fix:课程改造统一修改(简介、开关、封面图)

parent b490f02c
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-07-17 15:49:11 * @Date: 2020-07-17 15:49:11
* @Last Modified by: chenshu * @Last Modified by: chenshu
* @Last Modified time: 2021-04-06 16:43:23 * @Last Modified time: 2021-04-06 16:43:23
* @Description: 大班互动-添加/编辑直播课 * @Description: 大班互动-添加/编辑直播课
*/ */
import React from 'react'; import React from 'react';
import { withRouter } from "react-router-dom"; import { withRouter } from 'react-router-dom';
import { Button, message, Modal } from 'antd'; import { Button, message, Modal } from 'antd';
import ShowTips from "@/components/ShowTips"; import ShowTips from '@/components/ShowTips';
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from '@/components/Breadcrumbs';
import Bus from '../../core/bus' import Bus from '../../core/bus';
import AddLiveBasic from './components/AddLiveBasic'; import AddLiveBasic from './components/AddLiveBasic';
import AddLiveClass from './components/AddLiveClass'; import AddLiveClass from './components/AddLiveClass';
...@@ -20,25 +20,25 @@ import AddLiveIntro from './components/AddLiveIntro'; ...@@ -20,25 +20,25 @@ import AddLiveIntro from './components/AddLiveIntro';
import { randomString } from '@/domains/basic-domain/utils'; import { randomString } from '@/domains/basic-domain/utils';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
import PreviewCourseModal from './modal/PreviewCourseModal'; import PreviewCourseModal from './modal/PreviewCourseModal';
import CourseService from "@/domains/course-domain/CourseService"; import CourseService from '@/domains/course-domain/CourseService';
import moment from 'moment'; import moment from 'moment';
import User from '@/common/js/user'; import User from '@/common/js/user';
import _ from "underscore"; import _ from 'underscore';
import $ from 'jquery'; import $ from 'jquery';
import './AddLive.less'; import './AddLive.less';
const defaultCover = 'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png'; const defaultCover = 'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png';
const defaultBasicInfo = { const defaultBasicInfo = {
courseName: null, // 课程名称 courseName: null, // 课程名称
coverUrl: defaultCover, coverUrl: defaultCover,
coverId:null, coverId: null,
categoryId:null, categoryId: null,
categoryName:null, categoryName: null,
}; };
const defaultClassInfo = { const defaultClassInfo = {
teacherId: null, //讲师的Id teacherId: null, //讲师的Id
assistant:[], //助教 assistant: [], //助教
teacherName: null, teacherName: null,
liveDate: null, liveDate: null,
timeHorizonStart: null, timeHorizonStart: null,
...@@ -50,394 +50,391 @@ const defaultClassInfo = { ...@@ -50,394 +50,391 @@ const defaultClassInfo = {
const defaultIntroInfo = { const defaultIntroInfo = {
needRecord: 'YES', needRecord: 'YES',
whetherVisitorsJoin:'NO', whetherVisitorsJoin: 'NO',
liveCourseWarmMedia: {}, liveCourseWarmMedia: {},
// 讲师简介 // 讲师简介
liveCourseMediaRequests: [{ liveCourseMediaRequests: [
contentType:"INTRO", {
mediaType: 'TEXT', contentType: 'INTRO',
mediaContent: '', mediaType: 'TEXT',
key: Math.random() mediaContent: '',
}], key: Math.random(),
} },
],
};
class AddLive extends React.Component { class AddLive extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const id = getParameterByName("id"); const id = getParameterByName('id');
const type = getParameterByName("type"); const type = getParameterByName('type');
this.state = { this.state = {
id, id,
type, type,
loading: false, loading: false,
isEdit: true, isEdit: true,
courseState:'UN_START', courseState: 'UN_START',
// 直播课基本信息 // 直播课基本信息
addLiveBasicInfo: { addLiveBasicInfo: {
courseName: null, // 课程名称 courseName: null, // 课程名称
coverUrl: defaultCover, coverUrl: defaultCover,
coverId:null, coverId: null,
categoryId:null, categoryId: null,
categoryName:null, categoryName: null,
}, },
// 直播课上课信息 // 直播课上课信息
addLiveClassInfo: { addLiveClassInfo: {
teacherId: null, teacherId: null,
teacherName: null, teacherName: null,
assistant:[], assistant: [],
assistantStoreUserId:[], assistantStoreUserId: [],
assistantNames:[], assistantNames: [],
liveDate: null, liveDate: null,
timeHorizonStart: null, timeHorizonStart: null,
timeHorizonEnd: null, timeHorizonEnd: null,
calendarTime: [], // 批量排课 calendarTime: [], // 批量排课
startTime: new Date().getTime() + 300000, // 批量开始时分 startTime: new Date().getTime() + 300000, // 批量开始时分
endTime: new Date().getTime() + 300000 // 批量结束时分 endTime: new Date().getTime() + 300000, // 批量结束时分
}, },
// 直播课简介 // 直播课简介
addLiveIntroInfo: { addLiveIntroInfo: {
needRecord: 'YES', needRecord: 'YES',
whetherVisitorsJoin:'NO', whetherVisitorsJoin: 'NO',
liveCourseWarmMedia: {}, liveCourseWarmMedia: {},
introduce: '', introduce: '',
liveCourseMediaRequests: [], liveCourseMediaRequests: [],
}, },
} };
} }
componentDidMount() { componentDidMount() {
const { type } = this.state; const { type } = this.state;
if (type === 'edit') { if (type === 'edit') {
this.getCourseDetail(); this.getCourseDetail();
} }
Bus.bind('editorLimit', (textLength) => {
this.setState({
textLength,
});
});
} }
getCourseDetail = () => { getCourseDetail = () => {
let { isEdit } = this.state; let { isEdit } = this.state;
this.setState({ loading: true }); this.setState({ loading: true });
CourseService.getLiveCloudCourseDetail({ CourseService.getLiveCloudCourseDetail({
liveCourseId: this.state.id liveCourseId: this.state.id,
}).then((res) => { }).then((res) => {
const { const {
teacherId, teacherId,
teacherName, teacherName,
courseName, courseName,
startTime, startTime,
endTime, endTime,
courseMediaVOS, courseMediaVOS,
nickname, nickname,
needRecord, needRecord,
whetherVisitorsJoin, whetherVisitorsJoin,
warmMedia, warmMedia,
categoryId, categoryId,
categoryName, categoryName,
admins, admins,
courseState courseState,
} = res.result; } = res.result;
let coverId; let coverId;
let coverUrl; let coverUrl;
let liveCourseMediaRequests = []; let liveCourseMediaRequests = [];
let liveCourseWarmMedia; let liveCourseWarmMedia;
let hasIntro = false; let hasIntro = false;
courseMediaVOS.map((item) => { courseMediaVOS.map((item) => {
switch (item.contentType){ switch (item.contentType) {
case "COVER": case 'COVER':
coverId = item.mediaContent; coverId = item.mediaContent;
coverUrl = item.mediaUrl; coverUrl = item.mediaUrl;
break; break;
case "WARMUP": case 'WARMUP':
liveCourseWarmMedia = item; liveCourseWarmMedia = item;
break; break;
case "INTRO": case 'INTRO':
hasIntro = true; hasIntro = true;
this.getTextDetail('introduce', item.mediaUrl); this.getTextDetail('introduce', item.mediaUrl);
break; break;
default: default:
break; break;
}
return item;
})
const addLiveBasicInfo = {
courseName,
coverUrl: coverUrl || defaultCover,
coverId,
categoryId,
categoryName
};
const liveDate = startTime;
const timeHorizonStart = startTime;
const timeHorizonEnd = endTime;
const assistant = _.pluck(admins, "adminId");
const assistantStoreUserId = _.pluck(admins, "adminStoreUserId"); //编辑时的选中的助教的查询用storeUserId查询
const assistantNames = _.pluck(admins, "adminName");
const addLiveClassInfo = {
assistant,
liveDate,
nickname,
teacherId,
teacherName,
timeHorizonStart,
timeHorizonEnd,
startTime,
endTime,
assistantNames,
assistantStoreUserId
} }
return item;
const addLiveIntroInfo = { });
liveCourseWarmMedia,
needRecord,
whetherVisitorsJoin,
liveCourseMediaRequests,
}
// 晚于开课前30分钟 const addLiveBasicInfo = {
if(new Date().getTime() > startTime - 1800000) { courseName,
isEdit = false coverUrl: coverUrl || defaultCover,
} coverId,
this.setState({ categoryId,
loadintroduce: !hasIntro, categoryName,
isEdit, };
loading: false,
courseState, const liveDate = startTime;
addLiveIntroInfo, const timeHorizonStart = startTime;
addLiveClassInfo, const timeHorizonEnd = endTime;
addLiveBasicInfo, const assistant = _.pluck(admins, 'adminId');
}); const assistantStoreUserId = _.pluck(admins, 'adminStoreUserId'); //编辑时的选中的助教的查询用storeUserId查询
}) const assistantNames = _.pluck(admins, 'adminName');
} const addLiveClassInfo = {
assistant,
liveDate,
nickname,
teacherId,
teacherName,
timeHorizonStart,
timeHorizonEnd,
startTime,
endTime,
assistantNames,
assistantStoreUserId,
};
const addLiveIntroInfo = {
liveCourseWarmMedia,
needRecord,
whetherVisitorsJoin,
liveCourseMediaRequests,
};
// 晚于开课前30分钟
if (new Date().getTime() > startTime - 1800000) {
isEdit = false;
}
this.setState({
loadintroduce: !hasIntro,
isEdit,
loading: false,
courseState,
addLiveIntroInfo,
addLiveClassInfo,
addLiveBasicInfo,
});
});
};
getTextDetail = (key, url) => { getTextDetail = (key, url) => {
$.ajax({ $.ajax({
data: {}, data: {},
type: 'GET', type: 'GET',
url, url,
contentType:'application/x-www-form-urlencoded; charset=UTF-8', contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
success: (res) => { success: (res) => {
this.setState({ addLiveIntroInfo: { ...this.state.addLiveIntroInfo, [key]: res }, [`load${key}`]: true }); this.setState({ addLiveIntroInfo: { ...this.state.addLiveIntroInfo, [key]: res }, [`load${key}`]: true });
}, },
error: () => { error: () => {
message.warning('获取简介失败') message.warning('获取简介失败');
} },
}) });
} };
// 修改基本信息 // 修改基本信息
// 修改基本信息 // 修改基本信息
handleChangeBasicInfo = (field, value) => { handleChangeBasicInfo = (field, value) => {
const { coverUrl } = this.state.addLiveBasicInfo; const { coverUrl } = this.state.addLiveBasicInfo;
this.setState({ this.setState({
addLiveBasicInfo: { addLiveBasicInfo: {
...this.state.addLiveBasicInfo, ...this.state.addLiveBasicInfo,
[field]: value, [field]: value,
} },
}) });
} };
// 修改上课信息 // 修改上课信息
handleChangeClassInfo = (field, value ,type, optionValue) => { handleChangeClassInfo = (field, value, type, optionValue) => {
const _value = value ? value.valueOf() : null; const _value = value ? value.valueOf() : null;
const { teacherName } = this.state.addLiveClassInfo; const { teacherName } = this.state.addLiveClassInfo;
const { assistantNames } = this.state.addLiveClassInfo; const { assistantNames } = this.state.addLiveClassInfo;
const { assistantStoreUserId } = this.state.addLiveClassInfo const { assistantStoreUserId } = this.state.addLiveClassInfo;
this.setState({ this.setState({
addLiveClassInfo: { addLiveClassInfo: {
...this.state.addLiveClassInfo, ...this.state.addLiveClassInfo,
[field]: _value, [field]: _value,
teacherName:type==='teacherType'?optionValue:teacherName, teacherName: type === 'teacherType' ? optionValue : teacherName,
assistantNames:type==='assistantType'?_.pluck(optionValue, "children"):assistantNames, assistantNames: type === 'assistantType' ? _.pluck(optionValue, 'children') : assistantNames,
assistantStoreUserId:type==='assistantType'?_.pluck(optionValue, "key"):assistantStoreUserId, assistantStoreUserId: type === 'assistantType' ? _.pluck(optionValue, 'key') : assistantStoreUserId,
} },
}); });
} };
// 修改简介 // 修改简介
handleChangeIntroInfo = (field, value) => { handleChangeIntroInfo = (field, value) => {
this.setState({ this.setState({
addLiveIntroInfo: { addLiveIntroInfo: {
...this.state.addLiveIntroInfo, ...this.state.addLiveIntroInfo,
[field]: value [field]: value,
} },
}) });
} };
// 完成创建/编辑 // 完成创建/编辑
handleSubmit = () => { handleSubmit = () => {
//过期判断 //过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) { if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({ Modal.warning({
title:"服务已到期", title: '服务已到期',
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买", content: '当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买',
okText: "我知道了" okText: '我知道了',
}) });
return return;
} }
const { addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, id, isEdit, type } = this.state; const { addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, id, isEdit, type } = this.state;
const {liveDate, timeHorizonStart} = addLiveClassInfo; const { liveDate, timeHorizonStart } = addLiveClassInfo;
const _liveDate = moment(liveDate).format("YYYY-MM-DD"); const _liveDate = moment(liveDate).format('YYYY-MM-DD');
const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm'); const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm');
const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x'); const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x');
if(type === 'edit' && isEdit && new Date().getTime() > startTime - 1800000) { if (type === 'edit' && isEdit && new Date().getTime() > startTime - 1800000) {
Modal.info({ Modal.info({
title: "提示", title: '提示',
icon: ( icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
<span className="icon iconfont default-confirm-icon"> content: '晚于开课前30分钟,部分信息不可修改',
&#xe6f4;
</span>
),
content: "晚于开课前30分钟,部分信息不可修改",
okText: '我知道了', okText: '我知道了',
onOk: () => { onOk: () => {
this.getCourseDetail(); this.getCourseDetail();
} },
}); });
return return;
} }
this.handleValidate(addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, isEdit).then((res) => { this.handleValidate(addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, isEdit).then((res) => {
if (!res) return; if (!res) return;
Upload.uploadTextToOSS(addLiveIntroInfo.introduce, `${randomString()}.txt`, (introduceId) => { Upload.uploadTextToOSS(
this.submitRemote({ introduceId, addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, id }); addLiveIntroInfo.introduce,
}, () => message.warning('上传课程简介失败')); `${randomString()}.txt`,
}) (introduceId) => {
} this.submitRemote({ introduceId, addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, id });
},
() => message.warning('上传课程简介失败')
);
});
};
submitRemote = ({ introduceId, addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, id }) => { submitRemote = ({ introduceId, addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, id }) => {
const { type } = this.state; const { type } = this.state;
const { courseName,coverUrl,coverId,categoryId} = addLiveBasicInfo; const { courseName, coverUrl, coverId, categoryId } = addLiveBasicInfo;
const { const { liveDate, teacherId, assistant, timeHorizonEnd, timeHorizonStart, calendarTime } = addLiveClassInfo;
liveDate,
teacherId,
assistant,
timeHorizonEnd,
timeHorizonStart,
calendarTime,
} = addLiveClassInfo;
let { startTime, endTime } = addLiveClassInfo; let { startTime, endTime } = addLiveClassInfo;
const { needRecord,whetherVisitorsJoin,liveCourseWarmMedia} = addLiveIntroInfo; const { needRecord, whetherVisitorsJoin, liveCourseWarmMedia } = addLiveIntroInfo;
if(type === 'add') { if (type === 'add') {
startTime = startTime; startTime = startTime;
endTime = endTime; endTime = endTime;
} else { } else {
const _liveDate = moment(liveDate).format("YYYY-MM-DD"); const _liveDate = moment(liveDate).format('YYYY-MM-DD');
const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm'); const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm');
const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm'); const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm');
startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x'); startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x');
endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x'); endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x');
} }
let coverObj ={ let coverObj = {
contentType:'COVER', contentType: 'COVER',
mediaContent:coverId, mediaContent: coverId,
mediaType:'PICTURE', mediaType: 'PICTURE',
mediaUrl: coverUrl, mediaUrl: coverUrl,
} };
let scheduleMediaRequests = []; let scheduleMediaRequests = [];
if(coverId){ if (coverId) {
scheduleMediaRequests = [coverObj,...scheduleMediaRequests] scheduleMediaRequests = [coverObj, ...scheduleMediaRequests];
} }
if(liveCourseWarmMedia && liveCourseWarmMedia.mediaUrl){ if (liveCourseWarmMedia && liveCourseWarmMedia.mediaUrl) {
scheduleMediaRequests = [liveCourseWarmMedia,...scheduleMediaRequests] scheduleMediaRequests = [liveCourseWarmMedia, ...scheduleMediaRequests];
} }
const commonParams = { const commonParams = {
adminIds:assistant, adminIds: assistant,
calendarTime, calendarTime,
categoryId, categoryId,
endTime, endTime,
needRecord, needRecord,
startTime, startTime,
courseName: courseName.trim(), courseName: courseName.trim(),
storeId:User.getStoreId(), storeId: User.getStoreId(),
teacherId:teacherId, teacherId: teacherId,
whetherVisitorsJoin, whetherVisitorsJoin,
scheduleMediaRequests scheduleMediaRequests,
} };
if (type === 'add') { if (type === 'add') {
const params = { const params = {
...commonParams, ...commonParams,
operatorId: User.getUserId(), operatorId: User.getUserId(),
introduceId, introduceId,
} };
CourseService.createLiveCloudCourse(params).then((res) => { CourseService.createLiveCloudCourse(params).then((res) => {
if (res.success){ if (res.success) {
message.success("新建成功"); message.success('新建成功');
window.RCHistory.push({ window.RCHistory.push({
pathname: `/live-course`, pathname: `/live-course`,
}); });
} }
}); });
} else { } else {
const params = { const params = {
...commonParams, ...commonParams,
updateUserId:User.getUserId(), updateUserId: User.getUserId(),
liveCourseId: id, liveCourseId: id,
introduceId, introduceId,
} };
CourseService.updateLiveCloudCourse(params).then((res) => { CourseService.updateLiveCloudCourse(params).then((res) => {
if (res.success){ if (res.success) {
message.success("更新成功"); message.success('更新成功');
window.RCHistory.push({ window.RCHistory.push({
pathname: `/live-course`, pathname: `/live-course`,
}); });
} }
}); });
} }
} };
handleValidate = (addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, isEdit) => { handleValidate = (addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, isEdit) => {
return new Promise((resolve) => { return new Promise((resolve) => {
const { type } = this.state; const { type, textLength } = this.state;
const { courseName,categoryId} = addLiveBasicInfo; const { courseName, categoryId } = addLiveBasicInfo;
const { const { liveDate, timeHorizonStart, timeHorizonEnd, teacherId, calendarTime } = addLiveClassInfo;
liveDate, timeHorizonStart, timeHorizonEnd, teacherId, calendarTime
} = addLiveClassInfo;
const { liveCourseMediaRequests } = addLiveIntroInfo; const { liveCourseMediaRequests } = addLiveIntroInfo;
const currentTime = +new Date(); const currentTime = +new Date();
if(!courseName) { if (!courseName) {
message.warning('请输入课程名称'); message.warning('请输入课程名称');
resolve(false); resolve(false);
return; return;
} }
if(!categoryId){ if (!categoryId) {
message.warning('请选择课程分类'); message.warning('请选择课程分类');
resolve(false); resolve(false);
return; return;
} }
if(type === 'add') { if (type === 'add') {
const { startTime, endTime } = addLiveClassInfo; const { startTime, endTime } = addLiveClassInfo;
if(calendarTime.length === 0) { if (calendarTime.length === 0) {
message.warning('请选择上课日期'); message.warning('请选择上课日期');
resolve(false); resolve(false);
return; return;
} else if(startTime === endTime || startTime > endTime) { } else if (startTime === endTime || startTime > endTime) {
message.warning('结束时间必须晚于开始时间'); message.warning('结束时间必须晚于开始时间');
resolve(false); resolve(false);
return; return;
} }
// 若有今日排课 校验当前时间 // 若有今日排课 校验当前时间
const currentDay = moment(currentTime).format('YYYY-MM-DD'); const currentDay = moment(currentTime).format('YYYY-MM-DD');
const itemToday = _.find(calendarTime, (item) => { const itemToday = _.find(calendarTime, (item) => {
const itemDay = moment(item).format('YYYY-MM-DD'); const itemDay = moment(item).format('YYYY-MM-DD');
return itemDay === currentDay; return itemDay === currentDay;
}) });
if(itemToday) { if (itemToday) {
const itemDay = moment(itemToday).format('YYYY-MM-DD'); const itemDay = moment(itemToday).format('YYYY-MM-DD');
const itemHour = moment(startTime).format('HH:mm'); const itemHour = moment(startTime).format('HH:mm');
if(itemDay === currentDay) { if (itemDay === currentDay) {
if(moment(itemDay + ' ' + itemHour).format('x') < currentTime) { if (moment(itemDay + ' ' + itemHour).format('x') < currentTime) {
message.warning('开始时间不能早于现在'); message.warning('开始时间不能早于现在');
resolve(false); resolve(false);
return; return;
...@@ -445,50 +442,56 @@ handleChangeBasicInfo = (field, value) => { ...@@ -445,50 +442,56 @@ handleChangeBasicInfo = (field, value) => {
} }
} }
} else { } else {
const _liveDate = moment(liveDate).format("YYYY-MM-DD"); const _liveDate = moment(liveDate).format('YYYY-MM-DD');
const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm'); const _timeHorizonStart = moment(timeHorizonStart).format('HH:mm');
const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm'); const _timeHorizonEnd = moment(timeHorizonEnd).format('HH:mm');
const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x'); const startTime = moment(_liveDate + ' ' + _timeHorizonStart).format('x');
const endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x'); const endTime = moment(_liveDate + ' ' + _timeHorizonEnd).format('x');
if(!startTime || !endTime) { if (!startTime || !endTime) {
message.warning('日期不能为空'); message.warning('日期不能为空');
resolve(false); resolve(false);
return; return;
} else if (!timeHorizonStart) { } else if (!timeHorizonStart) {
message.warning('开始时间不能为空'); message.warning('开始时间不能为空');
resolve(false); resolve(false);
return; return;
} else if (!timeHorizonEnd) { } else if (!timeHorizonEnd) {
message.warning('结束时间不能为空'); message.warning('结束时间不能为空');
resolve(false); resolve(false);
return; return;
} else if (isEdit && startTime < currentTime) { } else if (isEdit && startTime < currentTime) {
message.warning('开始时间不能早于当前时间'); message.warning('开始时间不能早于当前时间');
resolve(false); resolve(false);
return; return;
} else if (isEdit && endTime < currentTime) { } else if (isEdit && endTime < currentTime) {
message.warning('结束时间不能早于当前时间'); message.warning('结束时间不能早于当前时间');
resolve(false); resolve(false);
return; return;
} else if (isEdit && endTime <= startTime) { } else if (isEdit && endTime <= startTime) {
message.warning("结束时间不能早于开始时间"); message.warning('结束时间不能早于开始时间');
resolve(false); resolve(false);
return; return;
} }
} }
if(!teacherId){ if (!teacherId) {
message.warning('请选择讲师'); message.warning('请选择讲师');
resolve(false); resolve(false);
return; return;
}
if (textLength > 1000) {
message.warning('课程简介超过字数限定');
resolve(false);
return;
} }
resolve(true) resolve(true);
}); });
} };
// 显示预览课程弹窗 // 显示预览课程弹窗
handleShowPreviewModal = () => { handleShowPreviewModal = () => {
const { addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo,type,courseState} = this.state; const { addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, type, courseState } = this.state;
const previewLiveCourseModal = ( const previewLiveCourseModal = (
<PreviewCourseModal <PreviewCourseModal
courseBasicInfo={addLiveBasicInfo} courseBasicInfo={addLiveBasicInfo}
...@@ -498,105 +501,78 @@ handleChangeBasicInfo = (field, value) => { ...@@ -498,105 +501,78 @@ handleChangeBasicInfo = (field, value) => {
courseState={courseState} courseState={courseState}
close={() => { close={() => {
this.setState({ this.setState({
previewLiveCourseModal: null previewLiveCourseModal: null,
}) });
}} }}
/> />
); );
this.setState({ previewLiveCourseModal }); this.setState({ previewLiveCourseModal });
} };
// 取消编辑并返回上一级路由 // 取消编辑并返回上一级路由
handleGoBack = () => { handleGoBack = () => {
// 比较state的addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo和默认数据是否相等 // 比较state的addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo和默认数据是否相等
const { addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo } = this.state; const { addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo } = this.state;
if (!_.isEqual(addLiveBasicInfo, defaultBasicInfo) || if (!_.isEqual(addLiveBasicInfo, defaultBasicInfo) || !_.isEqual(addLiveClassInfo, defaultClassInfo) || !_.isEqual(addLiveIntroInfo, defaultIntroInfo)) {
!_.isEqual(addLiveClassInfo, defaultClassInfo) ||
!_.isEqual(addLiveIntroInfo, defaultIntroInfo)
) {
Modal.confirm({ Modal.confirm({
title: '确定要返回吗?', title: '确定要返回吗?',
content: '返回后,本次编辑的内容将不被保存', content: '返回后,本次编辑的内容将不被保存',
okText: '确认返回', okText: '确认返回',
cancelText: '留在本页', cancelText: '留在本页',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>, icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => { onOk: () => {
window.RCHistory.push({ window.RCHistory.push({
pathname: `/live-course`, pathname: `/live-course`,
}); });
} },
}) });
} else { } else {
window.RCHistory.push({ window.RCHistory.push({
pathname: `/live-course`, pathname: `/live-course`,
}); });
} }
} };
render() { render() {
const { id, type, addLiveBasicInfo, addLiveClassInfo, addLiveIntroInfo, isEdit, loadintroduce } = this.state;
const {
id,
type,
addLiveBasicInfo,
addLiveClassInfo,
addLiveIntroInfo,
isEdit,
loadintroduce,
} = this.state;
return ( return (
<div className="page add-live-page"> <div className='page add-live-page'>
<Breadcrumbs <Breadcrumbs navList={type == 'add' ? '新建直播课' : '编辑直播课'} goBack={this.handleGoBack} />
navList={type == "add" ? "新建直播课" : "编辑直播课"} <div className='box'>
goBack={this.handleGoBack} <div className='show-tips'>
/> <ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
<div className="box">
<div className="show-tips">
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利" />
</div> </div>
<div className="add-live-page__form"> <div className='add-live-page__form'>
<div className="basic-info__wrap"> <div className='basic-info__wrap'>
<div className="title">基本信息</div> <div className='title'>基本信息</div>
<AddLiveBasic <AddLiveBasic isEdit={isEdit} pageType={type} data={addLiveBasicInfo} onChange={this.handleChangeBasicInfo} />
isEdit={isEdit}
pageType={type}
data={addLiveBasicInfo}
onChange={this.handleChangeBasicInfo}
/>
</div> </div>
<div className="class-info__wrap"> <div className='class-info__wrap'>
<div className="title">上课信息</div> <div className='title'>上课信息</div>
<AddLiveClass <AddLiveClass isEdit={isEdit} pageType={type} data={{ ...addLiveClassInfo, id }} onChange={this.handleChangeClassInfo} />
isEdit={isEdit}
pageType={type}
data={{...addLiveClassInfo, id} }
onChange={this.handleChangeClassInfo}
/>
</div> </div>
<div className="intro-info__wrap"> <div className='intro-info__wrap'>
<div className="title">更多信息</div> <div className='title'>更多信息</div>
<AddLiveIntro <AddLiveIntro isEdit={isEdit} data={{ ...addLiveIntroInfo, loadintroduce, id }} onChange={this.handleChangeIntroInfo} />
isEdit={isEdit}
data={{ ...addLiveIntroInfo, loadintroduce, id }}
onChange={this.handleChangeIntroInfo}
/>
</div> </div>
</div> </div>
</div> </div>
<div className="footer shrink-footer"> <div className='footer shrink-footer'>
<Button onClick={this.handleGoBack}>取消</Button> <Button onClick={this.handleGoBack}>取消</Button>
<Button onClick={this.handleShowPreviewModal}>预览</Button> <Button onClick={this.handleShowPreviewModal}>预览</Button>
<Button type="primary" onClick={_.debounce(() => this.handleSubmit(), 3000, true)}>保存</Button> <Button type='primary' onClick={_.debounce(() => this.handleSubmit(), 3000, true)}>
保存
</Button>
</div> </div>
{ this.state.previewLiveCourseModal } {this.state.previewLiveCourseModal}
{ this.state.lackConsumeStudentModal } {this.state.lackConsumeStudentModal}
</div> </div>
) );
} }
} }
export default withRouter(AddLive); export default withRouter(AddLive);
\ No newline at end of file
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-07-15 17:29:12 * @Date: 2020-07-15 17:29:12
* @Last Modified by: chenshu * @Last Modified by: chenshu
* @Last Modified time: 2021-03-29 16:19:49 * @Last Modified time: 2021-03-29 16:19:49
* @Description: 新建/编辑直播课-基本信息 * @Description: 新建/编辑直播课-基本信息
*/ */
import React from 'react'; import React from 'react';
import { Input, Button, message ,Cascader,Modal} from 'antd'; import { Input, Button, message, Cascader, Modal } from 'antd';
import UploadOss from "@/core/upload"; import UploadOss from '@/core/upload';
import { ImgCutModalNew } from '@/components'; import { ImgCutModalNew } from '@/components';
import StoreService from "@/domains/store-domain/storeService"; import StoreService from '@/domains/store-domain/storeService';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal'; import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
// import PhotoClip from 'photoclip' // import PhotoClip from 'photoclip'
...@@ -18,37 +18,34 @@ import Upload from '@/core/upload'; ...@@ -18,37 +18,34 @@ import Upload from '@/core/upload';
import './AddLiveBasic.less'; import './AddLiveBasic.less';
const defaultCover = 'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png'; const defaultCover = 'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png';
const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryList' }; const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryList' };
let cutFlag = false; let cutFlag = false;
let timer = null let timer = null;
class AddLiveBasic extends React.Component { class AddLiveBasic extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
imageFile: null, imageFile: null,
showCutModal: false, showCutModal: false,
courseCatalogList:[], courseCatalogList: [],
showSelectFileModal: false, showSelectFileModal: false,
cutImageBlob: null, cutImageBlob: null,
hasImgReady: false // 图片是否上传成功 hasImgReady: false, // 图片是否上传成功
} };
} }
componentWillUnmount() { componentWillUnmount() {}
} componentDidMount() {
componentDidMount(){
this.getCourseCatalogList(); this.getCourseCatalogList();
} }
getCourseCatalogList = ()=>{ getCourseCatalogList = () => {
StoreService.getCourseCatalogList({current:1,size:1000}).then((res) => { StoreService.getCourseCatalogList({ current: 1, size: 1000 }).then((res) => {
this.setState({ this.setState({
courseCatalogList:res.result.records courseCatalogList: res.result.records,
}) });
}); });
} };
// 上传封面图 // 上传封面图
handleShowImgCutModal = (event) => { handleShowImgCutModal = (event) => {
const imageFile = event.target.files[0]; const imageFile = event.target.files[0];
...@@ -57,44 +54,45 @@ class AddLiveBasic extends React.Component { ...@@ -57,44 +54,45 @@ class AddLiveBasic extends React.Component {
imageFile, imageFile,
showCutModal: true, showCutModal: true,
}); });
} };
// 使用默认封面图 // 使用默认封面图
handleResetCoverUrl = () => { handleResetCoverUrl = () => {
const { data: { coverUrl } } = this.props; const {
data: { coverUrl },
} = this.props;
const isDefaultCover = coverUrl === defaultCover; const isDefaultCover = coverUrl === defaultCover;
// 如果已经是默认图的话,不做任何任何处理 // 如果已经是默认图的话,不做任何任何处理
if (isDefaultCover) return; if (isDefaultCover) return;
message.success('已替换为默认图'); message.success('已替换为默认图');
this.props.onChange('coverUrl',defaultCover); this.props.onChange('coverUrl', defaultCover);
setTimeout(()=>{ setTimeout(() => {
this.props.onChange('coverId', null); this.props.onChange('coverId', null);
},1000) }, 1000);
} };
catalogChange= (value) => { catalogChange = (value) => {
const changeValueLength = value.length; const changeValueLength = value.length;
switch (changeValueLength){ switch (changeValueLength) {
case 1: case 1:
this.props.onChange('categoryId',value[0]); this.props.onChange('categoryId', value[0]);
break; break;
case 2: case 2:
this.props.onChange('categoryId',value[1]); this.props.onChange('categoryId', value[1]);
break; break;
default: default:
this.props.onChange('categoryId',null); this.props.onChange('categoryId', null);
break; break;
} }
} };
handleSelectCover = (file) => { handleSelectCover = (file) => {
this.uploadImage(file); this.uploadImage(file);
} };
//上传图片 //上传图片
uploadImage = (imageFile) => { uploadImage = (imageFile) => {
const { folderName } = imageFile; const { folderName } = imageFile;
const fileName = const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf('.'));
window.random_string(16) + folderName.slice(folderName.lastIndexOf("."));
const self = this; const self = this;
this.setState( this.setState(
{ {
...@@ -102,57 +100,55 @@ class AddLiveBasic extends React.Component { ...@@ -102,57 +100,55 @@ class AddLiveBasic extends React.Component {
}, },
() => { () => {
setTimeout(() => { setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal"); const okBtnDom = document.querySelector('#headPicModal');
const options = { const options = {
size: [500, 282], size: [500, 282],
ok: okBtnDom, ok: okBtnDom,
maxZoom: 3, maxZoom: 3,
style: { style: {
jpgFillColor: "transparent", jpgFillColor: 'transparent',
}, },
done: function (dataUrl) { done: function (dataUrl) {
clearTimeout(self.timer); clearTimeout(self.timer);
self.timer = setTimeout(() => { self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) { if (self.state.rotate != this.rotate() || self.state.scale != this.scale()) {
console.log(this.scale(), 'scale') console.log(this.scale(), 'scale');
const _dataUrl = this.clip() const _dataUrl = this.clip();
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl); const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({ self.setState({
cutImageBlob, cutImageBlob,
dataUrl: _dataUrl, dataUrl: _dataUrl,
rotate: this.rotate(), rotate: this.rotate(),
scale: this.scale() scale: this.scale(),
}) });
} }
}, 500);
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl); const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({ self.setState({
cutImageBlob, cutImageBlob,
dataUrl dataUrl,
}) });
setTimeout(() => { setTimeout(() => {
cutFlag = false; cutFlag = false;
}, 2000); }, 2000);
}, },
fail: (failInfo) => { fail: (failInfo) => {
message.error("图片上传失败了,请重新上传"); message.error('图片上传失败了,请重新上传');
}, },
loadComplete: function (img) { loadComplete: function (img) {
setTimeout(() => { setTimeout(() => {
const _dataUrl = this.clip() const _dataUrl = this.clip();
self.setState({ self.setState({
dataUrl: _dataUrl, dataUrl: _dataUrl,
hasImgReady: true hasImgReady: true,
}) });
}, 100) }, 100);
}, },
}; };
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}` const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`;
if (!this.state.photoclip) { if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options); const _photoclip = new PhotoClip('#headPicModal', options);
_photoclip.load(imgUrl); _photoclip.load(imgUrl);
this.setState({ this.setState({
photoclip: _photoclip, photoclip: _photoclip,
...@@ -161,101 +157,131 @@ class AddLiveBasic extends React.Component { ...@@ -161,101 +157,131 @@ class AddLiveBasic extends React.Component {
this.state.photoclip.clear(); this.state.photoclip.clear();
this.state.photoclip.load(imgUrl); this.state.photoclip.load(imgUrl);
} }
}, 200); }, 200);
} }
); );
}; };
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
const { choosedBannerId } = this.state; const { choosedBannerId } = this.state;
Upload.uploadBlobToOSS(blob, 'cover' + (new Date()).valueOf(),null,'signInfo').then((signInfo) => { Upload.uploadBlobToOSS(blob, 'cover' + new Date().valueOf(), null, 'signInfo').then((signInfo) => {
this.setState({ this.setState(
coverClicpPath:signInfo.fileUrl, {
coverId:signInfo.resourceId, coverClicpPath: signInfo.fileUrl,
visible: false coverId: signInfo.resourceId,
},()=>this.updateCover()) visible: false,
},
() => this.updateCover()
);
}); });
}; };
// base64转换成blob // base64转换成blob
convertBase64UrlToBlob = (urlData) => { convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]); const bytes = window.atob(urlData.split(',')[1]);
const ab = new ArrayBuffer(bytes.length); const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab); const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) { for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i); ia[i] = bytes.charCodeAt(i);
} }
return new Blob([ab], { type: "image/png" }); return new Blob([ab], { type: 'image/png' });
}; };
updateCover = () =>{ updateCover = () => {
const {coverClicpPath,coverId} = this.state const { coverClicpPath, coverId } = this.state;
this.setState({ this.setState({
showSelectFileModal: false showSelectFileModal: false,
}) });
this.props.onChange('coverUrl', coverClicpPath); this.props.onChange('coverUrl', coverClicpPath);
setTimeout(()=>{ setTimeout(() => {
this.props.onChange('coverId', coverId); this.props.onChange('coverId', coverId);
},1000) }, 1000);
};
}
render() { render() {
const { showCutModal, imageFile,courseCatalogList,showSelectFileModal,visible,cutImageBlob,hasImgReady const { showCutModal, imageFile, courseCatalogList, showSelectFileModal, visible, cutImageBlob, hasImgReady } = this.state;
} = this.state; const { data, pageType, isEdit } = this.props;
const { data,pageType,isEdit} = this.props; const { courseName, categoryName, coverUrl } = data;
const { courseName,categoryName,coverUrl} = data;
const fileName = ''; const fileName = '';
// 当前是否使用的是默认图片 // 当前是否使用的是默认图片
const isDefaultCover = coverUrl === defaultCover; const isDefaultCover = coverUrl === defaultCover;
return ( return (
<div className="add-live__basic-info"> <div className='add-live__basic-info'>
<div className="course-name"> <div className='course-name'>
<span className="label"><span className="require">*</span>课程名称:</span> <span className='label'>
<span className='require'>*</span>课程名称:
</span>
<Input <Input
value={courseName} value={courseName}
placeholder="请输入直播名称(40字以内)" placeholder='请输入直播名称(40字以内)'
maxLength={40} maxLength={40}
style={{ width: 240 }} style={{ width: 240 }}
onChange={(e) => { this.props.onChange('courseName', e.target.value)}} onChange={(e) => {
this.props.onChange('courseName', e.target.value);
}}
/> />
</div> </div>
<div className="course-cover"> <div className='course-cover'>
<span className="label">封面图:</span> <span className='label'>封面图:</span>
<div className="course-cover__wrap"> <div className='course-cover__wrap'>
<div className='opt-btns'>
<div className="img-content"> <Button
{ onClick={() => {
isDefaultCover && <span className="tag">默认图</span> this.setState({
} showSelectFileModal: true,
<img src={coverUrl} /> });
}}>
上传图片
</Button>
<span className={`default-btn ${isDefaultCover ? 'disabled' : ''}`} onClick={this.handleResetCoverUrl}>
使用默认图
</span>
<div className='tips'>建议尺寸1280*720px,图片支持jpg、jpeg、png格式。</div>
</div> </div>
<div className="opt-btns"> <div className='img-content'>
<Button onClick={() => { {isDefaultCover && <span className='tag'>默认图</span>}
this.setState({ <img src={coverUrl} />
showSelectFileModal: true
})
}}>上传图片</Button>
<span
className={`default-btn ${isDefaultCover ? 'disabled' : ''}`}
onClick={this.handleResetCoverUrl}
>使用默认图</span>
<div className="tips">建议尺寸1280*720px,图片支持jpg、jpeg、png格式。</div>
</div> </div>
</div> </div>
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label"><span className="require">*</span>课程分类:</span> <span className='label'>
{ pageType === 'add' && <span className='require'>*</span>课程分类:
<Cascader options={courseCatalogList} displayRender={ label => label.join('-')} fieldNames={fieldNames} onChange={this.catalogChange} style={{ width: 240 }} placeholder="请选择课程分类" suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>}/> </span>
} {pageType === 'add' && (
{ (pageType === 'edit' && categoryName) && <Cascader
<Cascader disabled={!isEdit ? true: false} defaultValue={[categoryName]} options={courseCatalogList} displayRender={ label => label.join('-')} fieldNames={fieldNames} onChange={this.catalogChange} style={{ width: 240 }} placeholder="请选择课程分类" suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>} /> options={courseCatalogList}
} displayRender={(label) => label.join('-')}
fieldNames={fieldNames}
onChange={this.catalogChange}
style={{ width: 240 }}
placeholder='请选择课程分类'
suffixIcon={
<span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
}
/>
)}
{pageType === 'edit' && categoryName && (
<Cascader
disabled={!isEdit ? true : false}
defaultValue={[categoryName]}
options={courseCatalogList}
displayRender={(label) => label.join('-')}
fieldNames={fieldNames}
onChange={this.catalogChange}
style={{ width: 240 }}
placeholder='请选择课程分类'
suffixIcon={
<span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
}
/>
)}
</div> </div>
<ImgCutModalNew <ImgCutModalNew
title="裁剪" title='裁剪'
width={530} width={530}
cutWidth={500} cutWidth={500}
cutHeight={282} cutHeight={282}
...@@ -270,45 +296,46 @@ class AddLiveBasic extends React.Component { ...@@ -270,45 +296,46 @@ class AddLiveBasic extends React.Component {
this.state.currentInputFile.value = ''; this.state.currentInputFile.value = '';
}} }}
onClose={() => this.setState({ showCutModal: false })} onClose={() => this.setState({ showCutModal: false })}
reUpload={() => { this.state.currentInputFile.click() }} reUpload={() => {
this.state.currentInputFile.click();
}}
/> />
{showSelectFileModal && {showSelectFileModal && (
<SelectPrepareFileModal <SelectPrepareFileModal
key="basic" key='basic'
operateType="select" operateType='select'
multiple={false} multiple={false}
accept="image/jpeg,image/png,image/jpg" accept='image/jpeg,image/png,image/jpg'
selectTypeList={['JPG', 'JPEG', 'PNG']} selectTypeList={['JPG', 'JPEG', 'PNG']}
tooltip='支持文件类型:jpg、jpeg、png' tooltip='支持文件类型:jpg、jpeg、png'
isOpen={showSelectFileModal} isOpen={showSelectFileModal}
onClose={() => { onClose={() => {
this.setState({ showSelectFileModal: false }) this.setState({ showSelectFileModal: false });
}} }}
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
} )}
<Modal <Modal
title="设置图片" title='设置图片'
width={1080} width={1080}
visible={visible} visible={visible}
maskClosable={false} maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>} closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
onCancel={() => { onCancel={() => {
this.setState({ visible: false }); this.setState({ visible: false });
}} }}
zIndex={10001} zIndex={10001}
footer={[ footer={[
<Button <Button
key="back" key='back'
onClick={() => { onClick={() => {
this.setState({ visible: false }); this.setState({ visible: false });
}} }}>
>
重新上传 重新上传
</Button>, </Button>,
<Button <Button
key="submit" key='submit'
type="primary" type='primary'
disabled={!hasImgReady} disabled={!hasImgReady}
onClick={() => { onClick={() => {
if (!cutFlag) { if (!cutFlag) {
...@@ -316,40 +343,37 @@ class AddLiveBasic extends React.Component { ...@@ -316,40 +343,37 @@ class AddLiveBasic extends React.Component {
this.refs.hiddenBtn.click(); this.refs.hiddenBtn.click();
} }
this.getSignature(cutImageBlob); this.getSignature(cutImageBlob);
}} }}>
>
确定 确定
</Button>, </Button>,
]} ]}>
> <div className='clip-box'>
<div className="clip-box">
<div <div
id="headPicModal" id='headPicModal'
ref="headPicModal" ref='headPicModal'
style={{ style={{
width: "500px", width: '500px',
height: "430px", height: '430px',
marginBottom: 0, marginBottom: 0,
}} }}></div>
></div> <div id='clipBtn' style={{ display: 'none' }} ref='hiddenBtn'></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div> <div className='preview-img'>
<div className="preview-img"> <div className='title'>效果预览</div>
<div className="title">效果预览</div>
{/* <img src={previewUrl} alt="图片预览" className="preview-url" /> */} {/* <img src={previewUrl} alt="图片预览" className="preview-url" /> */}
<div id="preview-url-box" style={{width:500,height:282}}> <div id='preview-url-box' style={{ width: 500, height: 282 }}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" /> <img src={this.state.dataUrl} style={{ width: '100%' }} alt='' />
</div> </div>
<div className="tip-box"> <div className='tip-box'>
<div className="tip">温馨提示</div> <div className='tip'>温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div> <div className='tip'>①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div> <div className='tip'>②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div> </div>
</div> </div>
</div> </div>
</Modal> </Modal>
</div> </div>
) );
} }
} }
export default AddLiveBasic; export default AddLiveBasic;
\ No newline at end of file
...@@ -2,83 +2,78 @@ ...@@ -2,83 +2,78 @@
.label { .label {
width: 100px; width: 100px;
text-align: right; text-align: right;
display:inline-block; display: inline-block;
.require { .require {
color: #EC4B35; color: #ec4b35;
} }
} }
.course-cover { .course-cover {
margin-left: 14px; margin-left: 14px;
display: flex; display: flex;
margin-top: 16px; margin-top: 24px;
&__wrap { &__wrap {
position: relative; position: relative;
.tag { .img-content {
border-radius: 2px; margin-top: 8px;
background: #D6D6D6; margin-right: 20px;
font-size: 12px; width: 299px;
height: 18px; height: 169px;
width: 52px; position: relative;
text-align: center;
color: #FFF;
position: absolute;
top: 8px;
left: 8px;
}
}
.course-cover__wrap {
display: flex;
flex-direction: row;
}
.img-content {
margin-right: 20px;
width: 299px;
height: 169px;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: contain; object-fit: contain;
border-radius: 4px; border-radius: 4px;
}
.tag {
border-radius: 2px;
background: #d6d6d6;
font-size: 12px;
height: 18px;
width: 52px;
text-align: center;
color: #fff;
position: absolute;
top: 8px;
left: 8px;
}
} }
}
.opt-btns { .opt-btns {
.default-btn {
margin-left: 14px;
color: #2966ff;
cursor: pointer;
.default-btn { &.disabled {
margin-left: 16px; color: #ccc;
color: #2966FF; cursor: not-allowed;
cursor: pointer; }
}
&.disabled { .ant-upload-list {
color: #CCC; display: none;
cursor: not-allowed;
} }
}
.ant-upload-list { .tips {
display: none; margin-top: 8px;
color: #999;
}
} }
} }
.tips {
margin-top: 8px;
color: #999;
}
} }
.course-catalog{ .course-catalog {
margin:20px 0 0 14px; margin: 20px 0 0 14px;
} }
} }
.ant-cascader-menu-item-active:not(.ant-cascader-menu-item-disabled){ .ant-cascader-menu-item-active:not(.ant-cascader-menu-item-disabled) {
font-weight:normal !important; font-weight: normal !important;
color:#2966FF !important; color: #2966ff !important;
} }
#imgCutModalNew { #imgCutModalNew {
width: 500px; width: 500px;
height: 300px; height: 300px;
} }
\ No newline at end of file
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-07-16 11:05:17 * @Date: 2020-07-16 11:05:17
* @Last Modified by: chenshu * @Last Modified by: chenshu
* @Last Modified time: 2021-04-06 16:40:24 * @Last Modified time: 2021-04-06 16:40:24
* @Description: 添加直播-简介 * @Description: 添加直播-简介
...@@ -17,209 +17,222 @@ import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepare ...@@ -17,209 +17,222 @@ import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepare
import { DISK_MAP } from '@/common/constants/academic/lessonEnum'; import { DISK_MAP } from '@/common/constants/academic/lessonEnum';
import { ImgCutModalNew } from '@/components'; import { ImgCutModalNew } from '@/components';
const { TextArea } = Input; const { TextArea } = Input;
const defaultCover = 'https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1599635741526.png'; const defaultCover = 'https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1599635741526.png';
class AddLiveIntro extends React.Component { class AddLiveIntro extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
warmUrl: defaultCover, warmUrl: defaultCover,
showSelectFileModal: false, showSelectFileModal: false,
diskList: [], diskList: [],
selectType:null selectType: null,
} };
} }
// 上传封面图 // 上传封面图
handleShowImgCutModal = (event) => { handleShowImgCutModal = (event) => {
const imageFile = event.target.files[0]; const imageFile = event.target.files[0];
if (!imageFile) return; if (!imageFile) return;
this.setState({ this.setState({
imageFile, imageFile,
showCutModal: true, showCutModal: true,
}); });
} };
// 选择暖场资源 // 选择暖场资源
handleSelectVideo = (file) => { handleSelectVideo = (file) => {
const { selectType } = this.state; const { selectType } = this.state;
this.setState({ this.setState({
showSelectFileModal: false showSelectFileModal: false,
}) });
const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file; const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file;
if(selectType === 'WARMUP'){ if (selectType === 'WARMUP') {
const liveCourseWarmMedia = { const liveCourseWarmMedia = {
contentType: 'WARMUP', contentType: 'WARMUP',
mediaType: folderFormat === 'MP4' || folderFormat === 'video/mp4' ? 'VIDEO' : 'PICTURE', mediaType: folderFormat === 'MP4' || folderFormat === 'video/mp4' ? 'VIDEO' : 'PICTURE',
mediaContent: resourceId, mediaContent: resourceId,
mediaUrl: ossUrl, mediaUrl: ossUrl,
mediaName: folderName, mediaName: folderName,
size: folderSize size: folderSize,
} };
this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia); this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia);
}else{ } else {
// 最多添加九图片 // 最多添加九图片
const { liveCourseMediaRequests } = this.props.data; const { liveCourseMediaRequests } = this.props.data;
const list = _.filter(liveCourseMediaRequests, (item) => { const list = _.filter(liveCourseMediaRequests, (item) => {
return item.mediaType == "PICTURE"; return item.mediaType == 'PICTURE';
}); });
if (list.length > 8) { if (list.length > 8) {
message.warning("最多添加9张图片"); message.warning('最多添加9张图片');
return; return;
} }
liveCourseMediaRequests.push({ liveCourseMediaRequests.push({
contentType: 'INTRO', contentType: 'INTRO',
size: folderSize, size: folderSize,
mediaName: folderName, mediaName: folderName,
mediaContent: resourceId, mediaContent: resourceId,
mediaType: 'PICTURE', mediaType: 'PICTURE',
mediaUrl: ossUrl, mediaUrl: ossUrl,
}); });
this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests); this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests);
} }
};
}
changeIntro = (value) => { changeIntro = (value) => {
this.props.onChange('introduce', value); this.props.onChange('introduce', value);
} };
whetherVisitorsJoinChange = ()=>{ whetherVisitorsJoinChange = () => {
if(this.props.data.whetherVisitorsJoin==="NO"){ if (this.props.data.whetherVisitorsJoin === 'NO') {
this.props.onChange('whetherVisitorsJoin','YES') this.props.onChange('whetherVisitorsJoin', 'YES');
}else{ } else {
this.props.onChange('whetherVisitorsJoin','NO') this.props.onChange('whetherVisitorsJoin', 'NO');
} }
} };
render() { render() {
const {liveType, isXiaomai, isEdit, data: { id, introduce, needRecord,whetherVisitorsJoin, loadintroduce, liveCourseWarmMedia = {} } } = this.props; const {
const { showCutModal, warmUrl, showSelectFileModal, diskList, imageFile,selectType} = this.state liveType,
isXiaomai,
isEdit,
data: { id, introduce, needRecord, whetherVisitorsJoin, loadintroduce, liveCourseWarmMedia = {} },
} = this.props;
const { showCutModal, warmUrl, showSelectFileModal, diskList, imageFile, selectType } = this.state;
return ( return (
<div className="add-live__intro-info"> <div className='add-live__intro-info'>
<div className="playback" > <div className='playback'>
<span className='label'>
<span className="label"><span className="require">*</span>直播回放:</span> <span className='require'>*</span>直播回放:
<div className="content"> </span>
<Radio.Group value={needRecord} onChange={(e) => { this.props.onChange('needRecord', e.target.value) }} disabled={!isEdit ? true: false}> <div className='content'>
<Radio.Group
value={needRecord}
onChange={(e) => {
this.props.onChange('needRecord', e.target.value);
}}
disabled={!isEdit ? true : false}>
<Row style={{ marginBottom: '5px' }}> <Row style={{ marginBottom: '5px' }}>
<Col span={24}> <Col span={24}>
<Radio value="YES"> <Radio value='YES'>
自动录制 自动录制
<span className="playback__text">系统自助进行全程直播录制</span> <span className='playback__text'>系统自助进行全程直播录制</span>
</Radio> </Radio>
</Col> </Col>
</Row> </Row>
<Row> <Row>
<Col span={24}> <Col span={24}>
<Radio value="NO"> <Radio value='NO'>
手动录制 手动录制
<span className="playback__text">讲师手动选择何时开始录制</span> <span className='playback__text'>讲师手动选择何时开始录制</span>
</Radio> </Radio>
</Col> </Col>
</Row> </Row>
</Radio.Group> </Radio.Group>
</div> </div>
</div> </div>
<div className="allow-tourist-join"> <div className='allow-tourist-join'>
<span className="label">观看设置:</span> <span className='label'>观看设置:</span>
<div className="content"> <div className='content'>
<div> <Switch checked={whetherVisitorsJoin === 'NO' ? true : false} onChange={this.whetherVisitorsJoinChange} />
<Switch checked={whetherVisitorsJoin==="YES"? true:false} onChange={this.whetherVisitorsJoinChange}/> <div class='instro-text'>{whetherVisitorsJoin === 'NO' ? '已开启,学员需绑定手机号才可观看' : '已关闭,学员无需绑定手机号即可观看'}</div>
</div>
<div>
<div class="instro-text">
<div>开启:允许未绑定手机号的学员进入直播间观看直播</div>
<div>关闭:仅限绑定了手机号的学员可以进入直播间观看直播</div>
</div>
</div>
</div> </div>
</div> </div>
<div className="warmup"> <div className='warmup'>
<span className="label">直播暖场图:</span> <span className='label'>直播暖场图:</span>
<div className="course-cover__wrap"> <div className='course-cover__wrap'>
<div className="img-content" style={ liveCourseWarmMedia.mediaUrl ? {background: '#000'} : {} }> <div className='img-content' style={liveCourseWarmMedia.mediaUrl ? { background: '#000' } : {}}>
<img src={liveCourseWarmMedia.mediaType === 'VIDEO' ? `${liveCourseWarmMedia.mediaUrl}?x-oss-process=video/snapshot,t_0,m_fast` : (liveCourseWarmMedia.mediaUrl ? liveCourseWarmMedia.mediaUrl : defaultCover )} /> <img
{ src={
liveCourseWarmMedia.mediaUrl && liveCourseWarmMedia.mediaType === 'VIDEO'
<div className="img-delete-wrap"> ? `${liveCourseWarmMedia.mediaUrl}?x-oss-process=video/snapshot,t_0,m_fast`
<img src="https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1600073872956.png" onClick={() => { : liveCourseWarmMedia.mediaUrl
this.props.onChange('liveCourseWarmMedia', {}); ? liveCourseWarmMedia.mediaUrl
}}/> : defaultCover
</div>
} }
</div> />
<div className="opt-btns"> {liveCourseWarmMedia.mediaUrl && (
<Button <div className='img-delete-wrap'>
disabled={!isEdit} <img
onClick={() => { src='https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1600073872956.png'
onClick={() => {
this.props.onChange('liveCourseWarmMedia', {});
}}
/>
</div>
)}
</div>
<div className='opt-btns'>
<Button
disabled={!isEdit}
onClick={() => {
this.setState({ this.setState({
showSelectFileModal: true, showSelectFileModal: true,
selectType:'WARMUP' selectType: 'WARMUP',
}) });
}}>上传图片/视频</Button> }}>
上传图片/视频
<div className="tips"> </Button>
<div>建议尺寸1280*720px或16:9。图片最大5M,支持jpg、jpeg和png;视频最大2G,</div>
<div>支持mp4。</div>
</div>
<Popover content={ <div className='tips'>
<div className="example-wrap"> <div>建议尺寸1280*720px或16:9。图片最大5M,支持jpg、jpeg和png;视频最大2G,</div>
<p className="title">直播间暖场图示例</p> <div>支持mp4。</div>
<p className="text">直播开始前,展示在直播间视频区域</p> </div>
<Popover
content={
<div className='example-wrap'>
<p className='title'>直播间暖场图示例</p>
<p className='text'>直播开始前,展示在直播间视频区域</p>
<img src='https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1599644482652.png'></img> <img src='https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1599644482652.png'></img>
</div> </div>
}> }>
<div className="checkExample">查看示例</div> <div className='checkExample'>查看示例</div>
</Popover> </Popover>
</div>
</div> </div>
</div>
</div> </div>
<div className='introduce'>
<div className="introduce"> <span className='label'>课程简介:</span>
<span className="label">直播课简介:</span> <div className='content'>
<div className="content"> <div className='intro-list'>
<div className="intro-list"> <div className='intro-list__item introduce-editor'>
<div className="intro-list__item introduce-editor"> {(!id || loadintroduce) && (
{(!id || loadintroduce) &&
<GraphicsEditor <GraphicsEditor
id="intro" id='intro'
isIntro={true} isIntro={true}
maxLimit={1000}
detail={{ detail={{
content: introduce content: introduce,
}}
onChange={(val) => {
this.changeIntro(val);
}} }}
onChange={(val) => { this.changeIntro(val) }}
/> />
} )}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* 选择暖场图文件弹窗 */} {/* 选择暖场图文件弹窗 */}
{ showSelectFileModal && {showSelectFileModal && (
<SelectPrepareFileModal <SelectPrepareFileModal
key="instro" key='instro'
operateType="select" operateType='select'
accept={selectType==="INTRO"?"image/jpeg,image/png,image/jpg":"video/mp4,image/jpeg,image/png,image/jpg"} accept={selectType === 'INTRO' ? 'image/jpeg,image/png,image/jpg' : 'video/mp4,image/jpeg,image/png,image/jpg'}
selectTypeList={ selectType==="INTRO" ? ['JPG', 'JPEG', 'PNG']: ['MP4', 'JPG', 'JPEG', 'PNG'] } selectTypeList={selectType === 'INTRO' ? ['JPG', 'JPEG', 'PNG'] : ['MP4', 'JPG', 'JPEG', 'PNG']}
tooltip={ selectType==="INTRO"?'支持文件类型:jpg、jpeg、png':'支持文件类型:jpg、jpeg、png、mp4'} tooltip={selectType === 'INTRO' ? '支持文件类型:jpg、jpeg、png' : '支持文件类型:jpg、jpeg、png、mp4'}
isOpen={showSelectFileModal} isOpen={showSelectFileModal}
onClose={() => { onClose={() => {
this.setState({ showSelectFileModal: false }) this.setState({ showSelectFileModal: false });
}} }}
onSelect={this.handleSelectVideo} onSelect={this.handleSelectVideo}
/> />
} )}
</div> </div>
) );
} }
} }
......
...@@ -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-05 11:00:28 * @LastEditTime: 2021-07-06 17:36:02
* @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 } = this.props; const { detail, onChange, isIntro, maxLimit, editorType } = this.props;
class ImageMenu extends BtnMenu { class ImageMenu extends BtnMenu {
constructor(editor) { constructor(editor) {
// data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述 // data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
...@@ -158,9 +158,7 @@ class GraphicsEditor extends React.Component { ...@@ -158,9 +158,7 @@ class GraphicsEditor extends React.Component {
const imageCount = ((html || '').match(/<img/g) || []).length; const imageCount = ((html || '').match(/<img/g) || []).length;
const textLength = this.editorInt.txt.text().replace(/\&nbsp\;/gi, ' ').length + videoCount + imageCount; const textLength = this.editorInt.txt.text().replace(/\&nbsp\;/gi, ' ').length + videoCount + imageCount;
this.setState({ textLength }, () => { this.setState({ textLength }, () => {
if (textLength > maxLimit) { Bus.trigger('editorLimit', textLength, editorType);
// message.warning('超过字数限定');
}
onChange(html, this.state.textLength); onChange(html, this.state.textLength);
}); });
}; };
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-05 10:07:47 * @Date: 2020-08-05 10:07:47
* @LastEditors: fusanqiasng * @LastEditors: yuananting
* @LastEditTime: 2021-06-16 18:16:14 * @LastEditTime: 2021-07-06 18:26:14
* @Description: 图文课新增/编辑页 * @Description: 图文课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -26,6 +26,7 @@ import User from '@/common/js/user'; ...@@ -26,6 +26,7 @@ import User from '@/common/js/user';
import _ from 'underscore'; import _ from 'underscore';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
import './AddGraphicsCourse.less'; import './AddGraphicsCourse.less';
import Bus from '@/core/bus';
const EDIT_BOX_KEY = Math.random(); const EDIT_BOX_KEY = Math.random();
const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryList' }; const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryList' };
...@@ -33,7 +34,7 @@ const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryL ...@@ -33,7 +34,7 @@ const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryL
//添加课程时课程默认的一些值 //添加课程时课程默认的一些值
const defaultShelfState = 'YES'; const defaultShelfState = 'YES';
const whetherVisitorsJoin = 'NO'; const whetherVisitorsJoin = 'NO';
const defaultCoverUrl = 'https://image.xiaomaiketang.com/xm/wFnpZtp2yB.png'; const defaultCover = 'https://image.xiaomaiketang.com/xm/wFnpZtp2yB.png';
let cutFlag = false; let cutFlag = false;
class AddGraphicsCourse extends React.Component { class AddGraphicsCourse extends React.Component {
...@@ -52,7 +53,7 @@ class AddGraphicsCourse extends React.Component { ...@@ -52,7 +53,7 @@ class AddGraphicsCourse extends React.Component {
introduce: '', introduce: '',
courseMediaId: null, // 图文课链接 courseMediaId: null, // 图文课链接
coverId: null, // 图文封面的recourceId coverId: null, // 图文封面的recourceId
coverUrl: defaultCoverUrl, // 图文课封面 coverUrl: defaultCover, // 图文课封面
studentList: [], // 上课学员列表 studentList: [], // 上课学员列表
shelfState: 'YES', //是否开启学院展示 shelfState: 'YES', //是否开启学院展示
selectedFileList: [], // 已经从资料云盘中勾选的文件 selectedFileList: [], // 已经从资料云盘中勾选的文件
...@@ -72,6 +73,11 @@ class AddGraphicsCourse extends React.Component { ...@@ -72,6 +73,11 @@ class AddGraphicsCourse extends React.Component {
if (pageType === 'edit') { if (pageType === 'edit') {
this.handleFetchScheudleDetail(id); this.handleFetchScheudleDetail(id);
} }
Bus.bind('editorLimit', (textLength, editorType) => {
this.setState({
[editorType]: textLength,
});
});
} }
initBus = () => { initBus = () => {
...@@ -393,11 +399,11 @@ class AddGraphicsCourse extends React.Component { ...@@ -393,11 +399,11 @@ class AddGraphicsCourse extends React.Component {
//过期判断 //过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) { if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({ Modal.warning({
title:"服务已到期", title: '服务已到期',
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买", content: '当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买',
okText: "我知道了" okText: '我知道了',
}) });
return return;
} }
const { id, coverId, pageType, courseName, courseMedia, introduce, categoryId, shelfState, whetherVisitorsJoin } = this.state; const { id, coverId, pageType, courseName, courseMedia, introduce, categoryId, shelfState, whetherVisitorsJoin } = this.state;
...@@ -466,6 +472,7 @@ class AddGraphicsCourse extends React.Component { ...@@ -466,6 +472,7 @@ class AddGraphicsCourse extends React.Component {
}; };
handleValidate = (courseName, courseMedia, categoryId) => { handleValidate = (courseName, courseMedia, categoryId) => {
const { graphicsCourseIntor, graphicsCourseContent } = this.state;
return new Promise((resolve) => { return new Promise((resolve) => {
if (!courseName) { if (!courseName) {
message.warning('请输入课程名称'); message.warning('请输入课程名称');
...@@ -482,6 +489,18 @@ class AddGraphicsCourse extends React.Component { ...@@ -482,6 +489,18 @@ class AddGraphicsCourse extends React.Component {
resolve(false); resolve(false);
return false; return false;
} }
if (graphicsCourseContent > 1000) {
message.warning('课程内容超过字数限定');
resolve(false);
return;
}
if (graphicsCourseIntor > 1000) {
message.warning('课程简介超过字数限定');
resolve(false);
return;
}
// const textMedia = scheduleMedia.filter((item) => item.mediaType === 'TEXT'); // const textMedia = scheduleMedia.filter((item) => item.mediaType === 'TEXT');
// for (let i = 0, len = textMedia.length; i < len; i++) { // for (let i = 0, len = textMedia.length; i < len; i++) {
// if (textMedia[i].mediaContentLength && textMedia[i].mediaContentLength.length > 1000) { // if (textMedia[i].mediaContentLength && textMedia[i].mediaContentLength.length > 1000) {
...@@ -494,6 +513,17 @@ class AddGraphicsCourse extends React.Component { ...@@ -494,6 +513,17 @@ class AddGraphicsCourse extends React.Component {
}); });
}; };
// 使用默认封面图
handleResetCoverUrl = () => {
const { coverUrl } = this.state;
const isDefaultCover = coverUrl === defaultCover;
// 如果已经是默认图的话,不做任何任何处理
if (isDefaultCover) return;
this.setState({ coverUrl: defaultCover, coverId: null }, () => {
message.success('已替换为默认图');
});
};
render() { render() {
const { const {
id, id,
...@@ -521,6 +551,8 @@ class AddGraphicsCourse extends React.Component { ...@@ -521,6 +551,8 @@ class AddGraphicsCourse extends React.Component {
const hasSelectedStu = studentList.length; const hasSelectedStu = studentList.length;
const courseWareIcon = FileVerifyMap[videoType] ? FileTypeIcon[FileVerifyMap[videoType].type] : FileTypeIcon[videoType]; const courseWareIcon = FileVerifyMap[videoType] ? FileTypeIcon[FileVerifyMap[videoType].type] : FileTypeIcon[videoType];
// 当前是否使用的是默认图片
const isDefaultCover = coverUrl === defaultCover;
return ( return (
<div className='page add-graphics-course-page'> <div className='page add-graphics-course-page'>
...@@ -547,19 +579,39 @@ class AddGraphicsCourse extends React.Component { ...@@ -547,19 +579,39 @@ class AddGraphicsCourse extends React.Component {
<div className='cover-url flex mt16'> <div className='cover-url flex mt16'>
<div className='label'>封面图:</div> <div className='label'>封面图:</div>
<div className='cover-url__wrap'> {/* <div className='cover-url__wrap'>
<div className='opt-btns'>
<Button
onClick={() => {
this.setState({
showSelectCoverModal: true,
});
}}>{`${pageType === 'add' && !coverUrl ? '上传' : '修改'}封面`}</Button>
<div className='tips'></div>
</div>
<div className='img-content'> <div className='img-content'>
<img src={coverUrl} /> <img src={coverUrl} />
</div> </div>
</div> */}
<div className='course-cover__wrap'>
<div className='opt-btns'> <div className='opt-btns'>
<Button <Button
onClick={() => { onClick={() => {
this.setState({ this.setState({
showSelectCoverModal: true, showSelectCoverModal: true,
}); });
}}>{`${pageType === 'add' && !coverUrl ? '上传' : '修改'}封面`}</Button> }}>
上传图片
</Button>
<span className={`default-btn ${isDefaultCover ? 'disabled' : ''}`} onClick={this.handleResetCoverUrl}>
使用默认图
</span>
<div className='tips'>建议尺寸1280*720px或16:9。封面图最大5M,支持jpg、jpeg和png。</div> <div className='tips'>建议尺寸1280*720px或16:9。封面图最大5M,支持jpg、jpeg和png。</div>
</div> </div>
<div className='img-content'>
{isDefaultCover && <span className='tag'>默认图</span>}
<img src={coverUrl} />
</div>
</div> </div>
</div> </div>
<div className='course-catalog required'> <div className='course-catalog required'>
......
.add-graphics-course-page { .add-graphics-course-page {
position:relative !important; position: relative !important;
.box{ .box {
margin-bottom:52px !important; margin-bottom: 52px !important;
} }
.ant-radio-group { .ant-radio-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.radio-item { .radio-item {
margin-bottom: 12px; margin-bottom: 12px;
...@@ -28,14 +27,14 @@ ...@@ -28,14 +27,14 @@
.form { .form {
margin-top: 16px; margin-top: 16px;
padding: 0 16px; padding: 0 16px;
.label{ .label {
display:inline-block; display: inline-block;
text-align:right; text-align: right;
width:85px; width: 85px;
} }
.required { .required {
position: relative; position: relative;
&::before { &::before {
position: absolute; position: absolute;
content: '*'; content: '*';
...@@ -43,14 +42,14 @@ ...@@ -43,14 +42,14 @@
left: 5px; left: 5px;
top: 6px; top: 6px;
} }
&.label::before { &.label::before {
top: 0; top: 0;
} }
} }
.course-catalog{ .course-catalog {
margin-bottom:16px; margin-bottom: 16px;
margin-top:16px; margin-top: 16px;
} }
.course-ware { .course-ware {
display: flex; display: flex;
...@@ -70,42 +69,101 @@ ...@@ -70,42 +69,101 @@
.flex { .flex {
display: flex; display: flex;
} }
.cover-url__wrap { .course-cover {
.img-content { margin-left: 14px;
width: 298px; display: flex;
height: 172px; margin-top: 24px;
img { &__wrap {
width: 100%; position: relative;
height: 100%;
object-fit: contain; .img-content {
border-radius: 4px; margin-top: 8px;
margin-right: 20px;
width: 299px;
height: 169px;
position: relative;
img {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 4px;
}
.tag {
border-radius: 2px;
background: #d6d6d6;
font-size: 12px;
height: 18px;
width: 52px;
text-align: center;
color: #fff;
position: absolute;
top: 8px;
left: 8px;
}
} }
}
.empty-img { .opt-btns {
width: 298px; .default-btn {
height: 172px; margin-left: 14px;
border: 1px dashed #EBEBEB; color: #2966ff;
border-radius: 4px; cursor: pointer;
padding: 12px;
color: #999; &.disabled {
padding: 52px 24px; color: #ccc;
text-align: center; cursor: not-allowed;
} }
}
.opt-btns {
margin-top: 8px; .ant-upload-list {
display: flex; display: none;
align-items: center; }
.tips { .tips {
margin-left: 12px; margin-top: 8px;
color: #999; color: #999;
}
} }
} }
} }
// .cover-url__wrap {
// .img-content {
// width: 298px;
// height: 172px;
// img {
// width: 100%;
// height: 100%;
// object-fit: contain;
// border-radius: 4px;
// }
// }
// .empty-img {
// width: 298px;
// height: 172px;
// border: 1px dashed #EBEBEB;
// border-radius: 4px;
// padding: 12px;
// color: #999;
// padding: 52px 24px;
// text-align: center;
// }
// .opt-btns {
// margin-top: 8px;
// display: flex;
// align-items: center;
// .tips {
// margin-left: 12px;
// color: #999;
// }
// }
// }
.select-student { .select-student {
align-items: center; align-items: center;
margin-left: 24px; margin-left: 24px;
...@@ -139,10 +197,10 @@ ...@@ -139,10 +197,10 @@
justify-content: flex-end; justify-content: flex-end;
padding-right: 72px; padding-right: 72px;
background: #fff; background: #fff;
border-top: 1px solid #E8E8E8; border-top: 1px solid #e8e8e8;
z-index: 999; z-index: 999;
.ant-btn { .ant-btn {
margin-left: 10px; margin-left: 10px;
} }
} }
} }
\ No newline at end of file
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-07-16 11:05:17 * @Date: 2020-07-16 11:05:17
* @Last Modified by: chenshu * @Last Modified by: chenshu
* @Last Modified time: 2021-04-06 16:17:57 * @Last Modified time: 2021-04-06 16:17:57
* @Description: 添加直播-简介 * @Description: 添加直播-简介
...@@ -17,55 +17,53 @@ import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepare ...@@ -17,55 +17,53 @@ import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepare
import { DISK_MAP } from '@/common/constants/academic/lessonEnum'; import { DISK_MAP } from '@/common/constants/academic/lessonEnum';
import { ImgCutModalNew } from '@/components'; import { ImgCutModalNew } from '@/components';
const { TextArea } = Input; const { TextArea } = Input;
class AddGraphicsIntro extends React.Component { class AddGraphicsIntro extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
showSelectFileModal: false, showSelectFileModal: false,
diskList: [], diskList: [],
selectType: null, selectType: null,
} };
} }
// 上传封面图 // 上传封面图
handleShowImgCutModal = (event) => { handleShowImgCutModal = (event) => {
const imageFile = event.target.files[0]; const imageFile = event.target.files[0];
if (!imageFile) return; if (!imageFile) return;
this.setState({ this.setState({
imageFile, imageFile,
showCutModal: true, showCutModal: true,
}); });
} };
// 选择暖场资源 // 选择暖场资源
handleSelectVideo = (file) => { handleSelectVideo = (file) => {
const { selectType } = this.state; const { selectType } = this.state;
this.setState({ this.setState({
showSelectFileModal: false showSelectFileModal: false,
}) });
const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file; const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file;
if(selectType === 'WARMUP'){ if (selectType === 'WARMUP') {
const liveCourseWarmMedia = { const liveCourseWarmMedia = {
contentType: 'WARMUP', contentType: 'WARMUP',
mediaType: folderFormat === 'MP4' ? 'VIDEO' : 'PICTURE', mediaType: folderFormat === 'MP4' ? 'VIDEO' : 'PICTURE',
mediaContent: resourceId, mediaContent: resourceId,
mediaUrl: ossUrl, mediaUrl: ossUrl,
mediaName: folderName, mediaName: folderName,
size: folderSize size: folderSize,
} };
this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia); this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia);
}else{ } else {
// 最多添加九图片 // 最多添加九图片
const { liveCourseMediaRequests } = this.props.data; const { liveCourseMediaRequests } = this.props.data;
const list = _.filter(liveCourseMediaRequests, (item) => { const list = _.filter(liveCourseMediaRequests, (item) => {
return item.mediaType == "PICTURE"; return item.mediaType == 'PICTURE';
}); });
if (list.length > 8) { if (list.length > 8) {
message.warning("最多添加9张图片"); message.warning('最多添加9张图片');
return; return;
} }
liveCourseMediaRequests.push({ liveCourseMediaRequests.push({
...@@ -78,121 +76,116 @@ class AddGraphicsIntro extends React.Component { ...@@ -78,121 +76,116 @@ class AddGraphicsIntro extends React.Component {
}); });
this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests); this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests);
} }
};
}
changeDetail = (value) => { changeDetail = (value) => {
this.props.onChange('courseMedia', value); this.props.onChange('courseMedia', value);
} };
changeIntro = (value) => { changeIntro = (value) => {
this.props.onChange('introduce', value); this.props.onChange('introduce', value);
} };
whetherVisitorsJoinChange = ()=>{ whetherVisitorsJoinChange = () => {
if(this.props.data.whetherVisitorsJoin==="NO"){ if (this.props.data.whetherVisitorsJoin === 'NO') {
this.props.onChange('whetherVisitorsJoin','YES') this.props.onChange('whetherVisitorsJoin', 'YES');
}else{ } else {
this.props.onChange('whetherVisitorsJoin','NO') this.props.onChange('whetherVisitorsJoin', 'NO');
} }
} };
shelfStateChange = ()=>{ shelfStateChange = () => {
if(this.props.data.shelfState==="NO"){ if (this.props.data.shelfState === 'NO') {
this.props.onChange('shelfState','YES') this.props.onChange('shelfState', 'YES');
}else{ } else {
this.props.onChange('shelfState','NO') this.props.onChange('shelfState', 'NO');
} }
} };
render() { render() {
const {data: { id, whetherVisitorsJoin, courseMedia, introduce, shelfState, loadcourseMedia, loadintroduce } } = this.props; const {
data: { id, whetherVisitorsJoin, courseMedia, introduce, shelfState, loadcourseMedia, loadintroduce },
} = this.props;
const { showSelectFileModal, selectType } = this.state; const { showSelectFileModal, selectType } = this.state;
return ( return (
<div className="add-graphic__intro-info"> <div className='add-graphic__intro-info'>
<div className="allow-tourist-join"> <div className='allow-tourist-join'>
<span className="label">观看设置:</span> <span className='label'>观看设置:</span>
<div className="content"> <div className='content'>
<div> <Switch checked={whetherVisitorsJoin === 'NO' ? true : false} onChange={this.whetherVisitorsJoinChange} />
<Switch checked={whetherVisitorsJoin==="YES"? true:false} onChange={this.whetherVisitorsJoinChange}/> <div className='desc'>{whetherVisitorsJoin === 'NO' ? '已开启,学员需绑定手机号才可观看' : '已关闭,学员无需绑定手机号即可观看'}</div>
</div>
<div>
<div className="desc">
<div>开启:允许未绑定手机号的学员观看</div>
<div>关闭:仅限绑定了手机号的学员可以进入观看图文课</div>
</div>
</div>
</div> </div>
</div> </div>
<div className="store-show"> <div className='store-show'>
<span className="label">学院展示:</span> <span className='label'>学院展示:</span>
<div className="content"> <div className='content'>
<Row> <Switch checked={shelfState === 'YES' ? true : false} onChange={this.shelfStateChange} />
<Col span={3}> <div className='desc'>{shelfState === 'YES' ? '已开启,课程将在该学院的学员课程列表中显示' : '已关闭,课程将在该学院的学员课程列表中隐藏'}</div>
<Switch checked={shelfState==="YES"? true:false} onChange={this.shelfStateChange}/>
</Col>
<Col span={21}>
<div className="desc">
<div>开启:图文课将在学员学院图文课列表中展示</div>
<div>关闭:图文课将在学员学院图文课列表中隐藏</div>
</div>
</Col>
</Row>
</div> </div>
</div> </div>
<div className="introduce required"> <div className='introduce required'>
<span className="label" style={{ marginTop: 5 }}>课程内容:</span> <span className='label' style={{ marginTop: 5 }}>
<div className="content"> 课程内容:
<div className="intro-list"> </span>
<div className="intro-list__item content-editor"> <div className='content'>
{(!id || loadcourseMedia) && <div className='intro-list'>
<div className='intro-list__item content-editor'>
{(!id || loadcourseMedia) && (
<GraphicsEditor <GraphicsEditor
id="content" id='content'
maxLimit={1000}
editorType={'graphicsCourseContent'}
detail={{ detail={{
content: courseMedia content: courseMedia,
}}
onChange={(val) => {
this.changeDetail(val);
}} }}
onChange={(val) => { this.changeDetail(val) }}
/> />
} )}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="introduce"> <div className='introduce'>
<span className="label">课程简介:</span> <span className='label'>课程简介:</span>
<div className="content"> <div className='content'>
<div className="intro-list"> <div className='intro-list'>
<div className="intro-list__item introduce-editor"> <div className='intro-list__item introduce-editor'>
{(!id || loadintroduce) && {(!id || loadintroduce) && (
<GraphicsEditor <GraphicsEditor
id="intro" id='intro'
isIntro={true} isIntro={true}
maxLimit={1000}
editorType={'graphicsCourseIntor'}
detail={{ detail={{
content: introduce content: introduce,
}}
onChange={(val) => {
this.changeIntro(val);
}} }}
onChange={(val) => { this.changeIntro(val) }}
/> />
} )}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* 选择暖场图文件弹窗 */} {/* 选择暖场图文件弹窗 */}
{ showSelectFileModal && {showSelectFileModal && (
<SelectPrepareFileModal <SelectPrepareFileModal
operateType="select" operateType='select'
accept={selectType==="INTRO"?"image/jpeg,image/png,image/jpg":"video/mp4,image/jpeg,image/png,image/jpg"} accept={selectType === 'INTRO' ? 'image/jpeg,image/png,image/jpg' : 'video/mp4,image/jpeg,image/png,image/jpg'}
selectTypeList={ selectType==="INTRO" ? ['JPG', 'JPEG', 'PNG']: ['MP4', 'JPG', 'JPEG', 'PNG'] } selectTypeList={selectType === 'INTRO' ? ['JPG', 'JPEG', 'PNG'] : ['MP4', 'JPG', 'JPEG', 'PNG']}
tooltip={ selectType==="INTRO"?'支持文件类型:jpg、jpeg、png':'支持文件类型:jpg、jpeg、png、mp4'} tooltip={selectType === 'INTRO' ? '支持文件类型:jpg、jpeg、png' : '支持文件类型:jpg、jpeg、png、mp4'}
isOpen={showSelectFileModal} isOpen={showSelectFileModal}
onClose={() => { onClose={() => {
this.setState({ showSelectFileModal: false }) this.setState({ showSelectFileModal: false });
}} }}
onSelect={this.handleSelectVideo} onSelect={this.handleSelectVideo}
/> />
} )}
</div> </div>
) );
} }
} }
......
...@@ -2,275 +2,277 @@ ...@@ -2,275 +2,277 @@
.w-e-full-screen-editor { .w-e-full-screen-editor {
background: #fff !important; background: #fff !important;
} }
.playback { .playback {
margin-bottom: 10px; margin-bottom: 10px;
.require { .require {
color: #EC4B35; color: #ec4b35;
}
&__text {
color: #999;
}
} }
.label{ &__text {
display:inline-block; color: #999;
text-align:right;
width:85px;
} }
.playback, }
.introduce { .label {
display: inline-block;
text-align: right;
width: 85px;
}
.playback,
.introduce {
display: flex;
flex-direction: row;
}
.allow-tourist-join {
display: flex;
.content {
display: flex; display: flex;
flex-direction: row;
} }
.allow-tourist-join{ .desc {
display:flex; margin-left: 16px;
.content{ font-size: 14px;
display:flex; color: #999;
} display: inline-block;
.desc{
margin-left:16px;
font-size:14px;
color:#999;
display:inline-block;
}
}
.store-show{
display:flex;
margin-top:16px;
margin-bottom:16px;
.desc{
margin-left:16px;
font-size:14px;
color:#999;
display:inline-block;
}
}
.radio {
display: block;
height: 30px;
line-height: 30px;
} }
.interactive-playback { }
.store-show {
display: flex;
margin-top: 16px;
margin-bottom: 16px;
.content {
display: flex; display: flex;
margin-bottom: 20px;
}
textarea.ant-input {
min-height: 80px;
} }
.desc {
.intro-list__item { margin-left: 16px;
display: flex; font-size: 14px;
margin-bottom: 16px; color: #999;
position: relative; display: inline-block;
&.picture {
width: 550px;
padding: 16px;
border: 1px solid #EEE;
border-radius: 4px;
.img__wrap {
width: 299px;
height: 168px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
.little-icon {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
right: -20px;
.iconfont {
width: 20px;
height: 20px;
line-height: 20px;
font-size: 20px;
color: #999;
margin-bottom: 4px;
cursor: pointer;
&.close {
margin-top: 8px;
background-image: url('https://image.xiaomaiketang.com/xm/eesMPhNP3e.png');
background-size: 100% 100%;
}
}
}
} }
}
.operate { .radio {
display: flex; display: block;
align-items: center; height: 30px;
justify-content: center; line-height: 30px;
}
.interactive-playback {
display: flex;
margin-bottom: 20px;
}
textarea.ant-input {
min-height: 80px;
}
.intro-list__item {
display: flex;
margin-bottom: 16px;
position: relative;
&.picture {
width: 550px; width: 550px;
height: 80px;
line-height: 80px;
padding: 16px; padding: 16px;
margin-top: 16px; border: 1px solid #eee;
border: 1px dashed #EBEBEB;
border-radius: 4px; border-radius: 4px;
.ant-upload { .img__wrap {
vertical-align: middle; width: 299px;
height: 168px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
} }
}
&__item {
display: flex; .little-icon {
flex-direction: column; display: flex;
flex-direction: column;
position: absolute;
top: 0;
right: -20px;
.iconfont {
width: 20px;
height: 20px;
line-height: 20px;
font-size: 20px;
color: #999;
margin-bottom: 4px;
cursor: pointer; cursor: pointer;
&:not(:last-child) { &.close {
margin-right: 82px; margin-top: 8px;
} background-image: url('https://image.xiaomaiketang.com/xm/eesMPhNP3e.png');
background-size: 100% 100%;
.iconfont {
font-size: 22px;
line-height: 22px;
color: #BFBFBF;
text-align: center;
}
.text {
color: #999;
line-height: 20px;
margin-top: 4px;
} }
} }
} }
}
.tips {
color: #999; .operate {
margin-top: 16px; display: flex;
margin-bottom: 8px; align-items: center;
} justify-content: center;
.checkExample { width: 550px;
width: 60px; height: 80px;
color: #FF7519; line-height: 80px;
cursor: pointer; padding: 16px;
margin-top: 16px;
border: 1px dashed #ebebeb;
border-radius: 4px;
.ant-upload {
vertical-align: middle;
} }
.warmup {
margin-bottom: 17px; &__item {
display: flex; display: flex;
flex-direction: column;
cursor: pointer;
&:not(:last-child) {
margin-right: 82px;
}
.iconfont {
font-size: 22px;
line-height: 22px;
color: #bfbfbf;
text-align: center;
}
.text {
color: #999;
line-height: 20px;
margin-top: 4px;
}
} }
.course-cover__wrap { }
display: flex;
flex-direction: row; .tips {
color: #999;
margin-top: 16px;
margin-bottom: 8px;
}
.checkExample {
width: 60px;
color: #ff7519;
cursor: pointer;
}
.warmup {
margin-bottom: 17px;
display: flex;
}
.course-cover__wrap {
display: flex;
flex-direction: row;
}
.img-content {
position: relative;
margin-right: 20px;
width: 300px;
height: 170px;
img {
width: 100%;
height: 100%;
object-fit: contain;
} }
.img-content { .img-delete-wrap {
position: relative; opacity: 0;
margin-right: 20px; position: absolute;
width: 300px; top: 0;
height: 170px; left: 0;
width: 100%;
height: 100%;
img { img {
width: 100%;
height: 100%;
object-fit: contain;
}
.img-delete-wrap {
opacity: 0;
position: absolute; position: absolute;
top: 0; left: 50%;
left: 0; top: 50%;
width: 100%; width: 40px;
height: 100%; height: 40px;
img { transform: translate(-50%, -50%);
position: absolute;
left: 50%;
top: 50%;
width: 40px;
height: 40px;
transform: translate(-50%, -50%);
}
&:hover {
opacity: 1;
cursor: pointer;
}
} }
} &:hover {
.opt-btns { opacity: 1;
.default-btn {
margin-left: 16px;
color: #FF7519;
cursor: pointer; cursor: pointer;
&.disabled {
color: #CCC;
cursor: not-allowed;
}
} }
} }
} }
.example-wrap { .opt-btns {
font-family: PingFangSC-Regular, PingFang SC; .default-btn {
text-align: center; margin-left: 16px;
.title { color: #ff7519;
margin-bottom: 6px; cursor: pointer;
font-size: 14px;
color: #333333; &.disabled {
} color: #ccc;
.text { cursor: not-allowed;
margin-bottom: 16px; }
font-size: 12px;
color: #999999;
}
img {
width: 180px;
} }
} }
.check-record-rule { }
width: 120px; .example-wrap {
color: #FF7519; font-family: PingFangSC-Regular, PingFang SC;
cursor: pointer; text-align: center;
z-index: 2; .title {
margin-bottom: 6px;
font-size: 14px;
color: #333333;
} }
.record-rule-wrap { .text {
text-align: left; margin-bottom: 16px;
ul { font-size: 12px;
margin-top: 10px; color: #999999;
padding-left: 34px; }
list-style-type: disc; img {
li { width: 180px;
color: #999; }
} }
} .check-record-rule {
.text { width: 120px;
color: #ff7519;
cursor: pointer;
z-index: 2;
}
.record-rule-wrap {
text-align: left;
ul {
margin-top: 10px;
padding-left: 34px;
list-style-type: disc;
li {
color: #999; color: #999;
} }
} }
.text {
.auto-send-class-report { color: #999;
.label { }
width: 57px; }
height: 12px;
font-size: 14px; .auto-send-class-report {
font-weight: 400; .label {
color: #666666; width: 57px;
line-height: 12px; height: 12px;
} font-size: 14px;
.open-text, .close-text { font-weight: 400;
width: 572px; color: #666666;
font-size: 14px; line-height: 12px;
font-weight: 400; }
color: #999999; .open-text,
line-height: 20px; .close-text {
margin-left: 100px; width: 572px;
margin-top: 5px; font-size: 14px;
} font-weight: 400;
.open-text { color: #999999;
margin-top: 8px; line-height: 20px;
} margin-left: 100px;
.close-text { margin-top: 5px;
margin-bottom: 16px; }
} .open-text {
} margin-top: 8px;
\ No newline at end of file }
.close-text {
margin-bottom: 16px;
}
}
...@@ -2,43 +2,32 @@ ...@@ -2,43 +2,32 @@
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-05 10:07:47 * @Date: 2020-08-05 10:07:47
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-06-07 15:06:26 * @LastEditTime: 2021-07-07 10:55:41
* @Description: 线下课新增/编辑页 * @Description: 线下课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
import React from 'react'; import React from 'react';
import { import { Button, Input, Radio, message, Modal, TreeSelect, Select, Switch, TimePicker, InputNumber, Tooltip } from 'antd';
Button,
Input,
Radio,
message,
Modal,
TreeSelect,
Select,
Switch,
TimePicker,
InputNumber,
Tooltip,
} from 'antd';
import $ from 'jquery'; import $ from 'jquery';
import RangePicker from "@/modules/common/DateRangePicker"; import RangePicker from '@/modules/common/DateRangePicker';
import ShowTips from "@/components/ShowTips"; import ShowTips from '@/components/ShowTips';
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from '@/components/Breadcrumbs';
import SelectStudent from '../modal/select-student'; import SelectStudent from '../modal/select-student';
import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal'; import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal';
import PreviewOfflineModal from './modal/PreviewOfflineModal'; import PreviewOfflineModal from './modal/PreviewOfflineModal';
import StoreService from "@/domains/store-domain/storeService"; import StoreService from '@/domains/store-domain/storeService';
import Service from '@/common/js/service'; import Service from '@/common/js/service';
import { randomString } from '@/domains/basic-domain/utils'; import { randomString } from '@/domains/basic-domain/utils';
import User from '@/common/js/user'; import User from '@/common/js/user';
import _ from "underscore"; import _ from 'underscore';
import moment from 'moment'; import moment from 'moment';
import Upload from '@/core/upload'; import Upload from '@/core/upload';
import GraphicsEditor from '../components/GraphicsEditor'; import GraphicsEditor from '../components/GraphicsEditor';
import MultipleDatePicker from '@/components/MultipleDatePicker'; import MultipleDatePicker from '@/components/MultipleDatePicker';
import './AddOfflineCourse.less'; import './AddOfflineCourse.less';
import Bus from '@/core/bus';
const { Option } = Select; const { Option } = Select;
const defaultCoverUrl = 'https://image.xiaomaiketang.com/xm/pxbWKsYA87.png'; const defaultCoverUrl = 'https://image.xiaomaiketang.com/xm/pxbWKsYA87.png';
...@@ -46,41 +35,40 @@ let cutFlag = false; ...@@ -46,41 +35,40 @@ let cutFlag = false;
const unitList = [ const unitList = [
{ key: 'HOUR', value: '小时' }, { key: 'HOUR', value: '小时' },
{ key: 'MINUTE', value: '分钟' }, { key: 'MINUTE', value: '分钟' },
] ];
class AddOfflineCourse extends React.Component { class AddOfflineCourse extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const courseId = getParameterByName("id"); const courseId = getParameterByName('id');
const pageType = getParameterByName("type"); const pageType = getParameterByName('type');
this.state = { this.state = {
courseId, // 线下课ID,编辑的时候从URL上带过来 courseId, // 线下课ID,编辑的时候从URL上带过来
pageType, // 页面类型: add->新建 edit->编辑 pageType, // 页面类型: add->新建 edit->编辑
imageFile: null, // 需要被截取的图片 imageFile: null, // 需要被截取的图片
courseName: null, // 线下课名称 courseName: null, // 线下课名称
courseMedia: '', courseMedia: '',
introduce: '', introduce: '',
coverId: null, // 线下封面的recourceId coverId: null, // 线下封面的recourceId
coverUrl: defaultCoverUrl, // 线下课封面 coverUrl: defaultCoverUrl, // 线下课封面
studentList: [], // 上课学员列表 studentList: [], // 上课学员列表
diskList: [], // 机构可见磁盘目录 diskList: [], // 机构可见磁盘目录
selectedFileList: [], // 已经从资料云盘中勾选的文件 selectedFileList: [], // 已经从资料云盘中勾选的文件
showCutModal: false, // 是否显示截图弹窗 showCutModal: false, // 是否显示截图弹窗
studentModal: false, studentModal: false,
categoryName:null, //分类名称 categoryName: null, //分类名称
categoryList: [], categoryList: [],
courseCatalogList:[], //分类列表 courseCatalogList: [], //分类列表
categoryId:null, //分类的Id值 categoryId: null, //分类的Id值
whetherVisitorsJoin: 'NO', // 是否允许游客加入 whetherVisitorsJoin: 'NO', // 是否允许游客加入
isContent: true, isContent: true,
teacherList: [], teacherList: [],
teacherQuery: { teacherQuery: {
size: 15, size: 15,
current: 1, current: 1,
nickName:null nickName: null,
}, },
calendarTime: [], calendarTime: [],
offlineCourseType: 'ALL_DAY_OFFLINE', offlineCourseType: 'ALL_DAY_OFFLINE',
...@@ -100,7 +88,7 @@ class AddOfflineCourse extends React.Component { ...@@ -100,7 +88,7 @@ class AddOfflineCourse extends React.Component {
isEditDisablie: false, isEditDisablie: false,
startTime: new Date().getTime() + 300000, // 批量开始时分 startTime: new Date().getTime() + 300000, // 批量开始时分
endTime: new Date().getTime() + 300000, // 批量结束时分 endTime: new Date().getTime() + 300000, // 批量结束时分
} };
} }
componentWillMount() { componentWillMount() {
...@@ -110,30 +98,35 @@ class AddOfflineCourse extends React.Component { ...@@ -110,30 +98,35 @@ class AddOfflineCourse extends React.Component {
if (pageType === 'edit') { if (pageType === 'edit') {
this.handleFetchScheudleDetail(courseId); this.handleFetchScheudleDetail(courseId);
} }
Bus.bind('editorLimit', (textLength) => {
this.setState({
textLength,
});
});
} }
initBus = () => { initBus = () => {
Bus.bind('offlineEditorImage', this.uploadImage) Bus.bind('offlineEditorImage', this.uploadImage);
} };
removeBus = () => { removeBus = () => {
Bus.unbind('offlineEditorImage', this.uploadImage) Bus.unbind('offlineEditorImage', this.uploadImage);
} };
uploadImage = () => { uploadImage = () => {
this.setState({ showSelectImageModal: true }) this.setState({ showSelectImageModal: true });
} };
//获取分类列表 //获取分类列表
getCourseCatalogList = ()=>{ getCourseCatalogList = () => {
Service.Hades('public/hades/queryCategoryTree', { source: 0, tenantId: User.getStoreId(), count: false, userId: User.getUserId() }).then((res) => { Service.Hades('public/hades/queryCategoryTree', { source: 0, tenantId: User.getStoreId(), count: false, userId: User.getUserId() }).then((res) => {
const { categoryList = [] } = res.result; const { categoryList = [] } = res.result;
this.setState({ this.setState({
categoryList, categoryList,
courseCatalogList: this.renderTreeNodes(categoryList), courseCatalogList: this.renderTreeNodes(categoryList),
}) });
}); });
} };
renderTreeNodes = (data) => { renderTreeNodes = (data) => {
let newTreeData = data.map((item) => { let newTreeData = data.map((item) => {
...@@ -150,17 +143,17 @@ class AddOfflineCourse extends React.Component { ...@@ -150,17 +143,17 @@ class AddOfflineCourse extends React.Component {
checkDetail = (courseId) => { checkDetail = (courseId) => {
return Service.Hades('public/hades/getOfflineCourseDetail', { return Service.Hades('public/hades/getOfflineCourseDetail', {
courseId courseId,
}).then((res) => { }).then((res) => {
const { courseState } = res.result; const { courseState } = res.result;
return courseState === 'UN_START'; return courseState === 'UN_START';
}); });
} };
// 获取线下课详情 // 获取线下课详情
handleFetchScheudleDetail = (courseId) => { handleFetchScheudleDetail = (courseId) => {
return Service.Hades('public/hades/getOfflineCourseDetail',{ return Service.Hades('public/hades/getOfflineCourseDetail', {
courseId courseId,
}).then((res) => { }).then((res) => {
const { result = {} } = res || {}; const { result = {} } = res || {};
const { const {
...@@ -196,25 +189,25 @@ class AddOfflineCourse extends React.Component { ...@@ -196,25 +189,25 @@ class AddOfflineCourse extends React.Component {
let coverId; let coverId;
let coverUrl = this.state.coverUrl; let coverUrl = this.state.coverUrl;
let hasIntro = false; let hasIntro = false;
courseMediaVOS.map((item) => { courseMediaVOS.map((item) => {
switch (item.contentType){ switch (item.contentType) {
case "COVER": case 'COVER':
coverId = item.mediaContent; coverId = item.mediaContent;
coverUrl = item.mediaUrl; coverUrl = item.mediaUrl;
break; break;
case "SCHEDULE": case 'SCHEDULE':
this.getTextDetail('courseMedia', item.mediaUrl); this.getTextDetail('courseMedia', item.mediaUrl);
break; break;
case "INTRO": case 'INTRO':
hasIntro = true; hasIntro = true;
this.getTextDetail('introduce', item.mediaUrl); this.getTextDetail('introduce', item.mediaUrl);
break; break;
default: default:
break; break;
} }
return item; return item;
}) });
this.setState({ this.setState({
loadintroduce: !hasIntro, loadintroduce: !hasIntro,
coverId, coverId,
...@@ -247,50 +240,43 @@ class AddOfflineCourse extends React.Component { ...@@ -247,50 +240,43 @@ class AddOfflineCourse extends React.Component {
signOutType, signOutType,
isEditDisablie: whetherHaveApply === 'YES', isEditDisablie: whetherHaveApply === 'YES',
}); });
}) });
} };
getTextDetail = (key, url) => { getTextDetail = (key, url) => {
$.ajax({ $.ajax({
data: {}, data: {},
type: 'GET', type: 'GET',
url, url,
contentType:'application/x-www-form-urlencoded; charset=UTF-8', contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
success: (res) => { success: (res) => {
this.setState({ [key]: res, [`load${key}`]: true }); this.setState({ [key]: res, [`load${key}`]: true });
} },
}) });
} };
handleGoBack = () => { handleGoBack = () => {
const { const { coverId, videoName, videoDuration, courseName, categoryId, whetherVisitorsJoin } = this.state;
coverId, if (videoName || videoDuration || categoryId || courseName || coverId || whetherVisitorsJoin !== whetherVisitorsJoin) {
videoName,
videoDuration,
courseName,
categoryId,
whetherVisitorsJoin
} = this.state;
if(videoName || videoDuration || categoryId || courseName || coverId || whetherVisitorsJoin !== whetherVisitorsJoin ){
Modal.confirm({ Modal.confirm({
title: '确认要返回吗?', title: '确认要返回吗?',
content: '返回后,本次编辑的内容将不被保存。', content: '返回后,本次编辑的内容将不被保存。',
okText: '确认返回', okText: '确认返回',
cancelText: '留在本页', cancelText: '留在本页',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>, icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => { onOk: () => {
window.RCHistory.push({ window.RCHistory.push({
pathname: `/offline-course`, pathname: `/offline-course`,
}); });
} },
}); });
}else{ } else {
window.RCHistory.push({ window.RCHistory.push({
pathname: `/offline-course`, pathname: `/offline-course`,
}); });
} }
} };
// 显示预览弹窗 // 显示预览弹窗
handleShowPreviewModal = () => { handleShowPreviewModal = () => {
const { const {
...@@ -338,30 +324,30 @@ class AddOfflineCourse extends React.Component { ...@@ -338,30 +324,30 @@ class AddOfflineCourse extends React.Component {
signOutStartTimeNum, signOutStartTimeNum,
signOutStartTimeUnit, signOutStartTimeUnit,
signOutEndTimeNum, signOutEndTimeNum,
signOutEndTimeUnit, signOutEndTimeUnit,
} };
const previewOfflineModal = ( const previewOfflineModal = (
<PreviewOfflineModal <PreviewOfflineModal
data={data} data={data}
close={() => { close={() => {
this.setState({ this.setState({
previewOfflineModal: null previewOfflineModal: null,
}) });
}} }}
/> />
); );
this.setState({ previewOfflineModal }); this.setState({ previewOfflineModal });
} };
handleSelectCover = (file)=> { handleSelectCover = (file) => {
this.uploadCoverImage(file); this.uploadCoverImage(file);
} };
//上传图片 //上传图片
uploadCoverImage = (imageFile) => { uploadCoverImage = (imageFile) => {
const { folderName } = imageFile; const { folderName } = imageFile;
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf(".")); const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf('.'));
const self = this; const self = this;
this.setState( this.setState(
{ {
...@@ -369,56 +355,54 @@ class AddOfflineCourse extends React.Component { ...@@ -369,56 +355,54 @@ class AddOfflineCourse extends React.Component {
}, },
() => { () => {
setTimeout(() => { setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal"); const okBtnDom = document.querySelector('#headPicModal');
const options = { const options = {
size: [500, 282], size: [500, 282],
ok: okBtnDom, ok: okBtnDom,
maxZoom: 3, maxZoom: 3,
style: { style: {
jpgFillColor: "transparent", jpgFillColor: 'transparent',
}, },
done: function (dataUrl) { done: function (dataUrl) {
clearTimeout(self.timer); clearTimeout(self.timer);
self.timer = setTimeout(() => { self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) { if (self.state.rotate != this.rotate() || self.state.scale != this.scale()) {
const _dataUrl = this.clip() const _dataUrl = this.clip();
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl); const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({ self.setState({
cutImageBlob, cutImageBlob,
dataUrl: _dataUrl, dataUrl: _dataUrl,
rotate: this.rotate(), rotate: this.rotate(),
scale: this.scale() scale: this.scale(),
}) });
} }
}, 500);
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl); const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({ self.setState({
cutImageBlob, cutImageBlob,
dataUrl dataUrl,
}) });
setTimeout(() => { setTimeout(() => {
cutFlag = false; cutFlag = false;
}, 2000); }, 2000);
}, },
fail: (failInfo) => { fail: (failInfo) => {
message.error("图片上传失败了,请重新上传"); message.error('图片上传失败了,请重新上传');
}, },
loadComplete: function (img) { loadComplete: function (img) {
setTimeout(() => { setTimeout(() => {
const _dataUrl = this.clip() const _dataUrl = this.clip();
self.setState({ self.setState({
dataUrl: _dataUrl, dataUrl: _dataUrl,
hasImgReady: true hasImgReady: true,
}) });
}, 100) }, 100);
}, },
}; };
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}` const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`;
if (!this.state.photoclip) { if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options); const _photoclip = new PhotoClip('#headPicModal', options);
_photoclip.load(imgUrl); _photoclip.load(imgUrl);
this.setState({ this.setState({
photoclip: _photoclip, photoclip: _photoclip,
...@@ -427,7 +411,6 @@ class AddOfflineCourse extends React.Component { ...@@ -427,7 +411,6 @@ class AddOfflineCourse extends React.Component {
this.state.photoclip.clear(); this.state.photoclip.clear();
this.state.photoclip.load(imgUrl); this.state.photoclip.load(imgUrl);
} }
}, 200); }, 200);
} }
); );
...@@ -435,53 +418,55 @@ class AddOfflineCourse extends React.Component { ...@@ -435,53 +418,55 @@ class AddOfflineCourse extends React.Component {
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
Upload.uploadBlobToOSS(blob, 'cover' + (new Date()).valueOf(),null,'signInfo').then((signInfo) => { Upload.uploadBlobToOSS(blob, 'cover' + new Date().valueOf(), null, 'signInfo').then((signInfo) => {
this.setState({ this.setState(
coverClicpPath:signInfo.fileUrl, {
coverId:signInfo.resourceId, coverClicpPath: signInfo.fileUrl,
visible: false coverId: signInfo.resourceId,
},()=>this.updateCover()) visible: false,
},
() => this.updateCover()
);
}); });
}; };
updateCover = () =>{ updateCover = () => {
const {coverClicpPath,coverId} = this.state const { coverClicpPath, coverId } = this.state;
this.setState({ this.setState({
showSelectCoverModal: false, showSelectCoverModal: false,
coverUrl:coverClicpPath, coverUrl: coverClicpPath,
coverId:coverId coverId: coverId,
}) });
} };
// base64转换成blob // base64转换成blob
convertBase64UrlToBlob = (urlData) => { convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]); const bytes = window.atob(urlData.split(',')[1]);
const ab = new ArrayBuffer(bytes.length); const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab); const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) { for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i); ia[i] = bytes.charCodeAt(i);
} }
return new Blob([ab], { type: "image/png" }); return new Blob([ab], { type: 'image/png' });
}; };
preSubmit = () => { preSubmit = () => {
//过期判断 //过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) { if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({ Modal.warning({
title:"服务已到期", title: '服务已到期',
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买", content: '当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买',
okText: "我知道了" okText: '我知道了',
}) });
return return;
} }
const { courseId } = this.state; const { courseId } = this.state;
if (courseId) { if (courseId) {
this.checkDetail(courseId).then(bool => bool ? this.handleSubmit() : message.warning('课程已开始,无法继续编辑')) this.checkDetail(courseId).then((bool) => (bool ? this.handleSubmit() : message.warning('课程已开始,无法继续编辑')));
} else { } else {
this.handleSubmit(); this.handleSubmit();
} }
} };
// 保存 // 保存
handleSubmit = () => { handleSubmit = () => {
...@@ -514,23 +499,23 @@ class AddOfflineCourse extends React.Component { ...@@ -514,23 +499,23 @@ class AddOfflineCourse extends React.Component {
signOutStartTimeUnit, signOutStartTimeUnit,
signOutEndTimeNum, signOutEndTimeNum,
signOutEndTimeUnit, signOutEndTimeUnit,
isMore, // isMore,
} = this.state; } = this.state;
let coverObj ={ let coverObj = {
contentType:'COVER', contentType: 'COVER',
mediaContent: coverId, mediaContent: coverId,
mediaType:'PICTURE', mediaType: 'PICTURE',
mediaUrl: coverUrl, mediaUrl: coverUrl,
} };
let scheduleMediaRequests = []; let scheduleMediaRequests = [];
if(coverId){ if (coverId) {
scheduleMediaRequests = [coverObj] scheduleMediaRequests = [coverObj];
} }
// 编辑且使用默认图时不传 // 编辑且使用默认图时不传
if (pageType === 'edit' && coverUrl === defaultCoverUrl) { if (pageType === 'edit' && coverUrl === defaultCoverUrl) {
scheduleMediaRequests = [] scheduleMediaRequests = [];
} }
const commonParams = { const commonParams = {
categoryId, categoryId,
...@@ -547,7 +532,7 @@ class AddOfflineCourse extends React.Component { ...@@ -547,7 +532,7 @@ class AddOfflineCourse extends React.Component {
startTime, startTime,
endTime, endTime,
calendarTime, calendarTime,
isMore, // isMore,
}; };
if (whetherSetApply === 'YES') { if (whetherSetApply === 'YES') {
...@@ -575,16 +560,21 @@ class AddOfflineCourse extends React.Component { ...@@ -575,16 +560,21 @@ class AddOfflineCourse extends React.Component {
// 校验必填字段:课程名称, 课程线下 // 校验必填字段:课程名称, 课程线下
this.handleValidate(commonParams).then((res) => { this.handleValidate(commonParams).then((res) => {
if (!res) return; if (!res) return;
Upload.uploadTextToOSS(introduce, `${randomString()}.txt`, (introduceId) => { Upload.uploadTextToOSS(
this.submitRemote({ introduce,
courseId, `${randomString()}.txt`,
pageType, (introduceId) => {
commonParams, this.submitRemote({
introduceId, courseId,
}); pageType,
}, () => message.warning('上传课程简介失败')); commonParams,
introduceId,
});
},
() => message.warning('上传课程简介失败')
);
}); });
} };
submitRemote = (data) => { submitRemote = (data) => {
const { courseId, pageType, commonParams, introduceId } = data; const { courseId, pageType, commonParams, introduceId } = data;
...@@ -592,75 +582,79 @@ class AddOfflineCourse extends React.Component { ...@@ -592,75 +582,79 @@ class AddOfflineCourse extends React.Component {
if (pageType === 'add') { if (pageType === 'add') {
Service.Hades('public/hades/createOfflineCourse', commonParams).then((res) => { Service.Hades('public/hades/createOfflineCourse', commonParams).then((res) => {
if (!res) return; if (!res) return;
message.success("新建成功"); message.success('新建成功');
window.RCHistory.push({ window.RCHistory.push({
pathname: `/offline-course`, pathname: `/offline-course`,
}); });
}) });
} else { } else {
const editParams = { const editParams = {
courseId:courseId, courseId: courseId,
...commonParams, ...commonParams,
} };
Service.Hades('public/hades/updateOfflineCourse', editParams).then((res) => { Service.Hades('public/hades/updateOfflineCourse', editParams).then((res) => {
if (!res) return; if (!res) return;
message.success("保存成功"); message.success('保存成功');
window.RCHistory.push({ window.RCHistory.push({
pathname: `/offline-course`, pathname: `/offline-course`,
}); });
}); });
} }
} };
handleValidate = (data) => { handleValidate = (data) => {
const { textLength } = this.state;
return new Promise((resolve) => { return new Promise((resolve) => {
if (!data.courseName) { if (!data.courseName) {
message.warning('请输入课程名称'); message.warning('请输入课程名称');
resolve(false); resolve(false);
} else if(!data.categoryId){ } else if (!data.categoryId) {
message.warning('请选择课程分类'); message.warning('请选择课程分类');
resolve(false); resolve(false);
} else if(!data.offlinePlace){ } else if (!data.offlinePlace) {
message.warning('请输入上课地点'); message.warning('请输入上课地点');
resolve(false); resolve(false);
} else if(!data.teacherId ){ } else if (!data.teacherId) {
message.warning('请选择讲师'); message.warning('请选择讲师');
resolve(false); resolve(false);
} else if(_.isEmpty(data.calendarTime)){ } else if (_.isEmpty(data.calendarTime)) {
message.warning('请选择上课日期'); message.warning('请选择上课日期');
resolve(false); resolve(false);
} else if(!data.startTime || !data.endTime){ } else if (!data.startTime || !data.endTime) {
message.warning('请选择上课时间'); message.warning('请选择上课时间');
resolve(false); resolve(false);
} else if(moment(moment(data.calendarTime[0]).format('YYYY-MM-DD') + moment(data.startTime).format(' HH:mm')).valueOf() < Date.now()){ } else if (moment(moment(data.calendarTime[0]).format('YYYY-MM-DD') + moment(data.startTime).format(' HH:mm')).valueOf() < Date.now()) {
message.warning('上课时间不能早于现在'); message.warning('上课时间不能早于现在');
resolve(false); resolve(false);
} else if(data.startTime >= data.endTime){ } else if (data.startTime >= data.endTime) {
message.warning('上课结束时间不能早于上课开始时间'); message.warning('上课结束时间不能早于上课开始时间');
resolve(false); resolve(false);
} else if(data.whetherSetApply === 'YES' && !data.startTimeApply){ } else if (data.whetherSetApply === 'YES' && !data.startTimeApply) {
message.warning('请选择报名时间'); message.warning('请选择报名时间');
resolve(false); resolve(false);
} else if(data.whetherSetApply === 'YES' && data.startTimeApply >= data.endTimeApply){ } else if (data.whetherSetApply === 'YES' && data.startTimeApply >= data.endTimeApply) {
message.warning('报名结束时间需大于报名开始时间'); message.warning('报名结束时间需大于报名开始时间');
resolve(false); resolve(false);
} else if(data.whetherSetApply === 'YES' && data.endTimeApply > moment(moment(data.calendarTime[0]).format('YYYY-MM-DD') + moment(data.endTime).format(' HH:mm:ss')).valueOf()){ } else if (
data.whetherSetApply === 'YES' &&
data.endTimeApply > moment(moment(data.calendarTime[0]).format('YYYY-MM-DD') + moment(data.endTime).format(' HH:mm:ss')).valueOf()
) {
message.warning('报名结束时间需小于上课开始时间'); message.warning('报名结束时间需小于上课开始时间');
resolve(false); resolve(false);
} else if(data.whetherSetSignIn === 'YES' && !data.signInTimeNum){ } else if (data.whetherSetSignIn === 'YES' && !data.signInTimeNum) {
message.warning('请输入签到时间'); message.warning('请输入签到时间');
resolve(false); resolve(false);
} else if(data.whetherSetSignOut === 'YES' && ((data.signOutType === 'START_LATER' && !data.signOutStartTimeNum) || !data.signOutEndTimeNum)){ } else if (data.whetherSetSignOut === 'YES' && ((data.signOutType === 'START_LATER' && !data.signOutStartTimeNum) || !data.signOutEndTimeNum)) {
message.warning('请输入签退时间'); message.warning('请输入签退时间');
resolve(false); resolve(false);
} else if (data.isMore) { } else if (textLength > 1000) {
message.warning('简介超过字数限定'); message.warning('课程简介超过字数限定');
resolve(false); resolve(false);
} else { } else {
resolve(true); resolve(true);
} }
}); });
} };
// 使用默认封面图 // 使用默认封面图
handleResetCoverUrl = () => { handleResetCoverUrl = () => {
...@@ -670,58 +664,63 @@ class AddOfflineCourse extends React.Component { ...@@ -670,58 +664,63 @@ class AddOfflineCourse extends React.Component {
if (isDefaultCover) return; if (isDefaultCover) return;
message.success('已替换为默认图'); message.success('已替换为默认图');
this.setState({ coverUrl: defaultCoverUrl }); this.setState({ coverUrl: defaultCoverUrl });
} };
// 滑动加载更多讲师列表 // 滑动加载更多讲师列表
handleScrollTeacherList = (e) => { handleScrollTeacherList = (e) => {
const { hasNext } = this.state; const { hasNext } = this.state;
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;
if (scrollToBottom && hasNext) { if (scrollToBottom && hasNext) {
const { teacherQuery } = this.state; const { teacherQuery } = this.state;
let _teacherQuery = teacherQuery; let _teacherQuery = teacherQuery;
_teacherQuery.current = _teacherQuery.current + 1 _teacherQuery.current = _teacherQuery.current + 1;
this.setState({ this.setState(
teacherQuery:{..._teacherQuery} {
},()=>{this.getTeacherList(_teacherQuery.current)}) teacherQuery: { ..._teacherQuery },
} },
} () => {
this.getTeacherList(_teacherQuery.current);
getTeacherList(current = 1, selectList){ }
const { teacherQuery,teacherList} = this.state; );
}
};
getTeacherList(current = 1, selectList) {
const { teacherQuery, teacherList } = this.state;
const _query = { const _query = {
...teacherQuery, ...teacherQuery,
current, current,
size:15 size: 15,
}; };
StoreService.getStoreUserBasicPage( _query).then((res) => { StoreService.getStoreUserBasicPage(_query).then((res) => {
const { result = {} } = res; const { result = {} } = res;
const { records = [], total = 0, hasNext } = result; const { records = [], total = 0, hasNext } = result;
const list = current > 1 ? teacherList.concat(records) : records; const list = current > 1 ? teacherList.concat(records) : records;
this.setState({ this.setState({
hasNext, hasNext,
teacherList: list, teacherList: list,
teacherQuery:{..._query} teacherQuery: { ..._query },
}) });
}); });
} }
changeIntro = (value, textLength) => { changeIntro = (value, textLength) => {
const isMore = textLength > 1000; // const isMore = textLength > 1000;
if (isMore) { // if (isMore) {
message.warning('内容过长,不能超过1000字'); // message.warning('内容过长,不能超过1000字');
} // }
this.setState({ introduce: value, isMore }); this.setState({ introduce: value });
} };
selectMultiDate = (calendarTime) => { selectMultiDate = (calendarTime) => {
const dateList = _.sortBy(calendarTime); const dateList = _.sortBy(calendarTime);
this.setState({ this.setState({
calendarTime: dateList, calendarTime: dateList,
}) });
} };
handleChangeDates = (dates) => { handleChangeDates = (dates) => {
const data = {}; const data = {};
...@@ -733,15 +732,15 @@ class AddOfflineCourse extends React.Component { ...@@ -733,15 +732,15 @@ class AddOfflineCourse extends React.Component {
data.endTimeApply = dates[1].startOf('minute').valueOf() + 59000; data.endTimeApply = dates[1].startOf('minute').valueOf() + 59000;
} }
this.setState(data); this.setState(data);
} };
whetherVisitorsJoinChange = ()=>{ whetherVisitorsJoinChange = () => {
if(this.state.whetherVisitorsJoin === "NO"){ if (this.state.whetherVisitorsJoin === 'NO') {
this.setState({ whetherVisitorsJoin: 'YES' }); this.setState({ whetherVisitorsJoin: 'YES' });
}else{ } else {
this.setState({ whetherVisitorsJoin: 'NO' }); this.setState({ whetherVisitorsJoin: 'NO' });
} }
} };
handleChangeCatalogList = (value, label) => { handleChangeCatalogList = (value, label) => {
this.setState({ categoryId: value, categoryName: label[0] }); this.setState({ categoryId: value, categoryName: label[0] });
...@@ -789,64 +788,66 @@ class AddOfflineCourse extends React.Component { ...@@ -789,64 +788,66 @@ class AddOfflineCourse extends React.Component {
} = this.state; } = this.state;
const isDefaultCover = coverUrl === defaultCoverUrl; const isDefaultCover = coverUrl === defaultCoverUrl;
return ( return (
<div className="page add-offline-course-page"> <div className='page add-offline-course-page'>
<Breadcrumbs <Breadcrumbs navList={pageType === 'add' ? '新建线下课' : '编辑线下课'} goBack={this.handleGoBack} />
navList={pageType === "add" ? "新建线下课" : "编辑线下课"}
goBack={this.handleGoBack}
/>
<div className="box"> <div className='box'>
<div className="show-tips"> <div className='show-tips'>
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利" /> <ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
</div> </div>
<div className="form"> <div className='form'>
<div className="basic-info__wrap"> <div className='basic-info__wrap'>
<div className="title">基本信息</div> <div className='title'>基本信息</div>
<div className="course-name"> <div className='course-name'>
<span className="label"><span className="require">*</span>课程名称:</span> <span className='label'>
<span className='require'>*</span>课程名称:
</span>
<Input <Input
value={courseName} value={courseName}
placeholder="请输入线下课的名称(40字以内)" placeholder='请输入线下课的名称(40字以内)'
maxLength={40} maxLength={40}
style={{ width: 240 }} style={{ width: 240 }}
onChange={(e) => { this.setState({ courseName: e.target.value }) }} onChange={(e) => {
this.setState({ courseName: e.target.value });
}}
/> />
</div> </div>
<div className="course-cover"> <div className='course-cover'>
<span className="label">封面图:</span> <span className='label'>封面图:</span>
<div className="course-cover__wrap"> <div className='course-cover__wrap'>
<div className='opt-btns'>
<div className="img-content"> <Button
{ onClick={() => {
isDefaultCover && <span className="tag">默认图</span> this.setState({
} showSelectCoverModal: true,
<img src={coverUrl} /> });
}}>
上传图片
</Button>
<span className={`default-btn ${isDefaultCover ? 'disabled' : ''}`} onClick={this.handleResetCoverUrl}>
使用默认图
</span>
<div className='tips'>建议尺寸1280*720px,图片支持jpg、jpeg、png格式。</div>
</div> </div>
<div className="opt-btns"> <div className='img-content'>
<Button onClick={() => { {isDefaultCover && <span className='tag'>默认图</span>}
this.setState({ <img src={coverUrl} />
showSelectCoverModal: true
})
}}>上传图片</Button>
<span
className={`default-btn ${isDefaultCover ? 'disabled' : ''}`}
onClick={this.handleResetCoverUrl}
>使用默认图</span>
<div className="tips">建议尺寸1280*720px,图片支持jpg、jpeg、png格式。</div>
</div> </div>
</div> </div>
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label special"><span className="require">*</span>课程分类:</span> <span className='label special'>
<span className='require'>*</span>课程分类:
</span>
<TreeSelect <TreeSelect
showSearch showSearch
treeNodeFilterProp="title" treeNodeFilterProp='title'
style={{ width: 240 }} style={{ width: 240 }}
dropdownStyle={{ maxHeight: 300, overflow: "auto" }} dropdownStyle={{ maxHeight: 300, overflow: 'auto' }}
treeData={courseCatalogList} treeData={courseCatalogList}
placeholder="请选择课程类型" placeholder='请选择课程类型'
allowClear allowClear
value={categoryId} value={categoryId}
treeDefaultExpandAll treeDefaultExpandAll
...@@ -855,184 +856,182 @@ class AddOfflineCourse extends React.Component { ...@@ -855,184 +856,182 @@ class AddOfflineCourse extends React.Component {
}} }}
/> />
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label special"><span className="require">*</span>上课地点:</span> <span className='label special'>
<span className='require'>*</span>上课地点:
</span>
<Input <Input
value={offlinePlace} value={offlinePlace}
maxLength={40} maxLength={40}
style={{ width: 240 }} style={{ width: 240 }}
placeholder="请输入上课地点(40字以内)" placeholder='请输入上课地点(40字以内)'
onChange={(e) => { onChange={(e) => {
this.setState({ offlinePlace: e.target.value }) this.setState({ offlinePlace: e.target.value });
}} }}
/> />
</div> </div>
<div className="course-catalog" id="teacher"> <div className='course-catalog' id='teacher'>
<span className="label special"><span className="require">* </span>讲师:</span> <span className='label special'>
<span className='require'>* </span>讲师:
</span>
<Select <Select
placeholder="请选择讲师" placeholder='请选择讲师'
value={teacherId} value={teacherId}
style={{ width: 240 }} style={{ width: 240 }}
showSearch showSearch
allowClear allowClear
filterOption={(input, option) => option} filterOption={(input, option) => option}
dropdownClassName="offline-dropdown-box" dropdownClassName='offline-dropdown-box'
onPopupScroll={this.handleScrollTeacherList} onPopupScroll={this.handleScrollTeacherList}
suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>} suffixIcon={
<span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
}
onChange={(value, option) => { onChange={(value, option) => {
if (option) { if (option) {
this.setState({ teacherId: value, teacherName: option.children }); this.setState({ teacherId: value, teacherName: option.children });
}else{ } else {
this.setState({ teacherId: value, teacherName: "" }); this.setState({ teacherId: value, teacherName: '' });
} }
}} }}
onSearch={(value) => { onSearch={(value) => {
let _teacherQuery = {...this.state.teacherQuery}; let _teacherQuery = { ...this.state.teacherQuery };
_teacherQuery.nickName = value _teacherQuery.nickName = value;
this.setState({ this.setState(
teacherQuery: _teacherQuery {
}, () => { teacherQuery: _teacherQuery,
this.getTeacherList() },
}) () => {
this.getTeacherList();
}
);
}} }}
onClear ={(value)=>{ onClear={(value) => {
this.setState({ this.setState(
teacherQuery:{ {
size: 15, teacherQuery: {
current: 1, size: 15,
nickName:null current: 1,
nickName: null,
},
},
() => {
this.getTeacherList();
} }
}, () => { );
this.getTeacherList() }}
}) getPopupContainer={() => document.getElementById('teacher')}>
}
}
getPopupContainer={() =>
document.getElementById("teacher")
}
>
{_.map(teacherList, (item, index) => { {_.map(teacherList, (item, index) => {
return ( return (
<Option value={item.id} key={item.id}>{item.nickName}</Option> <Option value={item.id} key={item.id}>
{item.nickName}
</Option>
); );
})} })}
</Select> </Select>
</div> </div>
<div className="allow-tourist-join"> <div className='allow-tourist-join'>
<span className="label">观看设置:</span> <span className='label'>观看设置:</span>
<div className="content"> <div className='content'>
<div> <Switch checked={whetherVisitorsJoin === 'NO' ? true : false} onChange={this.whetherVisitorsJoinChange} />
<Switch <div className='desc'>{whetherVisitorsJoin === 'NO' ? '已开启,学员需绑定手机号才可观看' : '已关闭,学员无需绑定手机号即可观看'}</div>
checked={whetherVisitorsJoin === "YES" ? true : false}
onChange={this.whetherVisitorsJoinChange}
/>
</div>
<div>
<div className="desc">
<div>开启:允许未绑定手机号的学员观看</div>
<div>关闭:仅限绑定了手机号的学员可以进入观看线下课</div>
</div>
</div>
</div> </div>
</div> </div>
<div className="introduce"> <div className='introduce'>
<span className="label">课程简介:</span> <span className='label'>课程简介:</span>
<div className="content"> <div className='content'>
<div className="intro-list"> <div className='intro-list'>
<div className="intro-list__item introduce-editor"> <div className='intro-list__item introduce-editor'>
{(!courseId || loadintroduce) && {(!courseId || loadintroduce) && (
<GraphicsEditor <GraphicsEditor
id="intro" id='intro'
isIntro={true} isIntro={true}
maxLimit={1000} maxLimit={1000}
detail={{ detail={{
content: introduce content: introduce,
}} }}
onChange={(val, textLength) => { onChange={(val) => {
this.changeIntro(val, textLength) this.changeIntro(val);
}} }}
/> />
} )}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="title" style={{ marginTop: 24 }}>课程设置</div> <div className='title' style={{ marginTop: 24 }}>
<div className="day"> 课程设置
<span className="label"> </div>
<span className="require">*</span> <div className='day'>
上课日期: <span className='label'>
</span> <span className='require'>*</span>
上课日期:
</span>
<div> <div>
<div className='select-day'> <div className='select-day'>
已选 <span className="mark-day">{isLongArr(calendarTime) ? calendarTime.length : 0}</span> 已选 <span className='mark-day'>{isLongArr(calendarTime) ? calendarTime.length : 0}</span>
</div> </div>
<MultipleDatePicker <MultipleDatePicker disabled={isEditDisablie} selectDateList={calendarTime} onSelect={this.selectMultiDate} canSelectTodayBefore={false} />
disabled={isEditDisablie}
selectDateList={calendarTime}
onSelect={this.selectMultiDate}
canSelectTodayBefore={false}
/>
</div> </div>
</div> </div>
<div className="hour" id="hour"> <div className='hour' id='hour'>
<span className="label"><span className="require">*</span>上课时间:</span> <span className='label'>
<span className='require'>*</span>上课时间:
</span>
<TimePicker <TimePicker
disabled={isEditDisablie} disabled={isEditDisablie}
className="time-picker" className='time-picker'
format="HH:mm" format='HH:mm'
value={startTime ? moment(startTime) : null} value={startTime ? moment(startTime) : null}
placeholder="开始时间" placeholder='开始时间'
showNow={false} showNow={false}
style={{ width: 100, minWidth: 100}} style={{ width: 100, minWidth: 100 }}
onSelect={(time) => { onSelect={(time) => {
this.setState({ startTime: time }); this.setState({ startTime: time });
}} }}
getPopupContainer={() => getPopupContainer={() => document.getElementById('hour')}
document.getElementById("hour") />
} &nbsp;&nbsp;~&nbsp;&nbsp;
/>&nbsp;&nbsp;~&nbsp;&nbsp;
<TimePicker <TimePicker
disabled={isEditDisablie} disabled={isEditDisablie}
className="time-picker" className='time-picker'
format="HH:mm" format='HH:mm'
value={endTime ? moment(endTime) : null} value={endTime ? moment(endTime) : null}
placeholder="结束时间" placeholder='结束时间'
showNow={false} showNow={false}
style={{ width: 100, minWidth: 100 }} style={{ width: 100, minWidth: 100 }}
onSelect={(time) => { onSelect={(time) => {
this.setState({ endTime: time }); this.setState({ endTime: time });
}} }}
getPopupContainer={() => getPopupContainer={() => document.getElementById('hour')}
document.getElementById("hour")
}
/> />
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label"><span className="require">* </span>学员上课方式:</span> <span className='label'>
<span className='require'>* </span>学员上课方式:
</span>
<Radio.Group <Radio.Group
style={{ display: 'inline-block' }} style={{ display: 'inline-block' }}
value={offlineCourseType} value={offlineCourseType}
onChange={(e) => { onChange={(e) => {
this.setState({ offlineCourseType: e.target.value }); this.setState({ offlineCourseType: e.target.value });
}} }}
className="mt5" className='mt5'
disabled={isEditDisablie} disabled={isEditDisablie}>
> <Radio value='ALL_DAY_OFFLINE' className='mr-16'>
<Radio value="ALL_DAY_OFFLINE" className="mr-16"> <span style={{ color: '#333' }}>所选日期都要上课</span>
<span style={{ color: "#333" }}>所选日期都要上课</span>
</Radio> </Radio>
<Radio value="ANY_DAY_POFFLINE" className="mr-16"> <Radio value='ANY_DAY_POFFLINE' className='mr-16'>
<span style={{ color: "#333" }}>选择任意1天上课</span> <span style={{ color: '#333' }}>选择任意1天上课</span>
</Radio> </Radio>
</Radio.Group> </Radio.Group>
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label">课程报名:</span> <span className='label'>课程报名:</span>
<div className="switch-box"> <div className='switch-box'>
<div className="switch-item" key="1"> <div className='switch-item' key='1'>
<Switch <Switch
disabled={isEditDisablie} disabled={isEditDisablie}
checked={whetherSetApply === 'YES'} checked={whetherSetApply === 'YES'}
...@@ -1045,79 +1044,118 @@ class AddOfflineCourse extends React.Component { ...@@ -1045,79 +1044,118 @@ class AddOfflineCourse extends React.Component {
}); });
}} }}
/> />
<span className="switch-tip">开启后可设置课程报名时间,获取报名数据</span> <span className='switch-tip'>开启后可设置课程报名时间,获取报名数据</span>
</div> </div>
{whetherSetApply === 'YES' && <div className="switch-item" key="2"> {whetherSetApply === 'YES' && (
<span className="switch-label">报名日期:</span> <div className='switch-item' key='2'>
<RangePicker <span className='switch-label'>报名日期:</span>
id="course_date_picker" <RangePicker
showTime={{ showTime: 'HH:mm' }} id='course_date_picker'
allowClear={false} showTime={{ showTime: 'HH:mm' }}
value={startTimeApply ? [moment(startTimeApply), moment(endTimeApply)] : null } allowClear={false}
format={"YYYY-MM-DD HH:mm"} value={startTimeApply ? [moment(startTimeApply), moment(endTimeApply)] : null}
onChange={(dates) => { this.handleChangeDates(dates) }} format={'YYYY-MM-DD HH:mm'}
renderExtraFooter={() => calendarTime[0] ? <div style={{ position: 'absolute', bottom: 8, cursor: 'pointer' }}> onChange={(dates) => {
<span this.handleChangeDates(dates);
onClick={() => this.setState({ startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).subtract(1, 'days').valueOf(), endTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000 })} }}
style={{ renderExtraFooter={() =>
color: '#2966FF', calendarTime[0] ? (
border: '1px solid #2966FF', <div style={{ position: 'absolute', bottom: 8, cursor: 'pointer' }}>
padding: '2px 8px', <span
borderRadius: '2px', onClick={() =>
marginRight: 8, this.setState({
}} startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`)
>上课前1天</span> .subtract(1, 'days')
<span .valueOf(),
onClick={() => this.setState({ startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).subtract(2, 'days').valueOf(), endTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000 })} endTimeApply:
style={{ moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000,
color: '#2966FF', })
border: '1px solid #2966FF', }
padding: '2px 8px', style={{
borderRadius: '2px', color: '#2966FF',
marginRight: 8, border: '1px solid #2966FF',
}} padding: '2px 8px',
>上课前2天</span> borderRadius: '2px',
<span marginRight: 8,
onClick={() => this.setState({ startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).subtract(3, 'days').valueOf(), endTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000 })} }}>
style={{ 上课前1天
color: '#2966FF', </span>
border: '1px solid #2966FF', <span
padding: '2px 8px', onClick={() =>
borderRadius: '2px', this.setState({
marginRight: 8, startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`)
}} .subtract(2, 'days')
>上课前3天</span> .valueOf(),
</div> : null} endTimeApply:
/> moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000,
</div>} })
{whetherSetApply === 'YES' && <div className="switch-item" key="3"> }
<span className="switch-label"> style={{
报名人数 color: '#2966FF',
<Tooltip title="报名一旦开始,报名人数不支持减少"> border: '1px solid #2966FF',
<span style={{ margin: '0 4px', color: '#999' }} className="icon iconfont">&#xe7c4;</span> padding: '2px 8px',
</Tooltip> borderRadius: '2px',
:最多 marginRight: 8,
</span> }}>
<InputNumber 上课前2天
value={quota} </span>
min={oldQuta || 1} <span
max={100000} onClick={() =>
precision={0} this.setState({
style={{ margin: '0 4px', width: 90 }} startTimeApply: moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`)
disabled={oldQuta < 0} .subtract(3, 'days')
onChange={(value) => { .valueOf(),
this.setState({ quota: value }) endTimeApply:
}} moment(`${moment(calendarTime[0]).format('YYYY-MM-DD')} ${moment(startTime).format('HH:mm')}`).valueOf() - 1000,
/> })
<span className="switch-label"></span> }
<span className="switch-tip">未填写时默认为不限制</span> style={{
</div>} color: '#2966FF',
border: '1px solid #2966FF',
padding: '2px 8px',
borderRadius: '2px',
marginRight: 8,
}}>
上课前3天
</span>
</div>
) : null
}
/>
</div>
)}
{whetherSetApply === 'YES' && (
<div className='switch-item' key='3'>
<span className='switch-label'>
报名人数
<Tooltip title='报名一旦开始,报名人数不支持减少'>
<span style={{ margin: '0 4px', color: '#999' }} className='icon iconfont'>
&#xe7c4;
</span>
</Tooltip>
:最多
</span>
<InputNumber
value={quota}
min={oldQuta || 1}
max={100000}
precision={0}
style={{ margin: '0 4px', width: 90 }}
disabled={oldQuta < 0}
onChange={(value) => {
this.setState({ quota: value });
}}
/>
<span className='switch-label'></span>
<span className='switch-tip'>未填写时默认为不限制</span>
</div>
)}
</div> </div>
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label">考勤签到:</span> <span className='label'>考勤签到:</span>
<div className="switch-box"> <div className='switch-box'>
<div className="switch-item" key="1"> <div className='switch-item' key='1'>
<Switch <Switch
checked={whetherSetSignIn === 'YES'} checked={whetherSetSignIn === 'YES'}
onChange={(value) => { onChange={(value) => {
...@@ -1126,64 +1164,68 @@ class AddOfflineCourse extends React.Component { ...@@ -1126,64 +1164,68 @@ class AddOfflineCourse extends React.Component {
signInType: 'START_AGO', signInType: 'START_AGO',
signInTimeNum: null, signInTimeNum: null,
signInTimeUnit: 'MINUTE', signInTimeUnit: 'MINUTE',
}) });
}} }}
/> />
<span className="switch-tip">开启后可设置获取签到考勤数据</span> <span className='switch-tip'>开启后可设置获取签到考勤数据</span>
</div> </div>
{whetherSetSignIn === 'YES' && <div className="switch-item" key="2"> {whetherSetSignIn === 'YES' && (
<span className="switch-label">签到时间:</span> <div className='switch-item' key='2'>
<Radio.Group <span className='switch-label'>签到时间:</span>
style={{ display: 'inline-block' }} <Radio.Group
value={signInType} style={{ display: 'inline-block' }}
onChange={(e) => { value={signInType}
this.setState({ signInType: e.target.value }); onChange={(e) => {
}} this.setState({ signInType: e.target.value });
className="mt5" }}
> className='mt5'>
<Radio value="START_AGO" className="mr-16"> <Radio value='START_AGO' className='mr-16'>
<span style={{ color: "#333" }}>课程开始前</span> <span style={{ color: '#333' }}>课程开始前</span>
</Radio> </Radio>
<Radio value="END_AGO" className="mr-16"> <Radio value='END_AGO' className='mr-16'>
<span style={{ color: "#333" }}>课程结束前</span> <span style={{ color: '#333' }}>课程结束前</span>
</Radio> </Radio>
</Radio.Group> </Radio.Group>
</div>} </div>
{whetherSetSignIn === 'YES' && <div className="switch-item" key="3"> )}
<span className="switch-label">课程{signInType === 'START_AGO' ? '开始' : '结束'}</span> {whetherSetSignIn === 'YES' && (
<InputNumber <div className='switch-item' key='3'>
value={signInTimeNum} <span className='switch-label'>课程{signInType === 'START_AGO' ? '开始' : '结束'}</span>
min={1} <InputNumber
max={signInTimeUnit === 'MINUTE' ? 1440 : 24} value={signInTimeNum}
precision={0} min={1}
style={{ margin: '0 4px', width: 90 }} max={signInTimeUnit === 'MINUTE' ? 1440 : 24}
onChange={(value) => { precision={0}
this.setState({ signInTimeNum: value }) style={{ margin: '0 4px', width: 90 }}
}} onChange={(value) => {
/> this.setState({ signInTimeNum: value });
<Select }}
style={{ width: 72, marginRight: 4 }} />
value={signInTimeUnit} <Select
onChange={(value) => { style={{ width: 72, marginRight: 4 }}
const data = { signInTimeUnit: value } value={signInTimeUnit}
if (value === 'HOUR' && signInTimeNum > 24) { onChange={(value) => {
data.signInTimeNum = 24; const data = { signInTimeUnit: value };
} if (value === 'HOUR' && signInTimeNum > 24) {
this.setState(data); data.signInTimeNum = 24;
}} }
> this.setState(data);
{unitList.map(item => ( }}>
<Option value={item.key} key={item.key}>{item.value}</Option> {unitList.map((item) => (
))} <Option value={item.key} key={item.key}>
</Select> {item.value}
<span className="switch-label">内可签到</span> </Option>
</div>} ))}
</Select>
<span className='switch-label'>内可签到</span>
</div>
)}
</div> </div>
</div> </div>
<div className="course-catalog"> <div className='course-catalog'>
<span className="label">考勤签退:</span> <span className='label'>考勤签退:</span>
<div className="switch-box"> <div className='switch-box'>
<div className="switch-item" key="1"> <div className='switch-item' key='1'>
<Switch <Switch
checked={whetherSetSignOut === 'YES'} checked={whetherSetSignOut === 'YES'}
onChange={(value) => { onChange={(value) => {
...@@ -1194,135 +1236,143 @@ class AddOfflineCourse extends React.Component { ...@@ -1194,135 +1236,143 @@ class AddOfflineCourse extends React.Component {
signOutStartTimeUnit: 'MINUTE', signOutStartTimeUnit: 'MINUTE',
signOutEndTimeNum: null, signOutEndTimeNum: null,
signOutEndTimeUnit: 'MINUTE', signOutEndTimeUnit: 'MINUTE',
}) });
}} }}
/> />
<span className="switch-tip">开启后可设置获取签退考勤数据</span> <span className='switch-tip'>开启后可设置获取签退考勤数据</span>
</div> </div>
{whetherSetSignOut === 'YES' && <div className="switch-item" key="2"> {whetherSetSignOut === 'YES' && (
<span className="switch-label">签退时间:</span> <div className='switch-item' key='2'>
<Radio.Group <span className='switch-label'>签退时间:</span>
style={{ display: 'inline-block' }} <Radio.Group
value={signOutType} style={{ display: 'inline-block' }}
onChange={(e) => { value={signOutType}
this.setState({ signOutType: e.target.value }); onChange={(e) => {
}} this.setState({ signOutType: e.target.value });
className="mt5" }}
> className='mt5'>
<Radio value="START_LATER" className="mr-16"> <Radio value='START_LATER' className='mr-16'>
<span style={{ color: "#333" }}>课程开始后</span> <span style={{ color: '#333' }}>课程开始后</span>
</Radio> </Radio>
<Radio value="END_LATER" className="mr-16"> <Radio value='END_LATER' className='mr-16'>
<span style={{ color: "#333" }}>课程结束后</span> <span style={{ color: '#333' }}>课程结束后</span>
</Radio> </Radio>
</Radio.Group> </Radio.Group>
</div>} </div>
{whetherSetSignOut === 'YES' && <div className="switch-item" key="3"> )}
<span className="switch-label">课程{signOutType === 'START_LATER' ? '开始' : '结束'}</span> {whetherSetSignOut === 'YES' && (
{signOutType === 'START_LATER' && <InputNumber <div className='switch-item' key='3'>
value={signOutStartTimeNum} <span className='switch-label'>课程{signOutType === 'START_LATER' ? '开始' : '结束'}</span>
min={1} {signOutType === 'START_LATER' && (
max={signOutStartTimeUnit === 'MINUTE' ? 1440 : 24} <InputNumber
precision={0} value={signOutStartTimeNum}
style={{ margin: '0 4px', width: 90 }} min={1}
onChange={(value) => { max={signOutStartTimeUnit === 'MINUTE' ? 1440 : 24}
this.setState({ signOutStartTimeNum: value }) precision={0}
}} style={{ margin: '0 4px', width: 90 }}
/>} onChange={(value) => {
{signOutType === 'START_LATER' && <Select this.setState({ signOutStartTimeNum: value });
style={{ width: 72, marginRight: 4 }} }}
value={signOutStartTimeUnit} />
onChange={(value) => { )}
const data = { signOutStartTimeUnit: value } {signOutType === 'START_LATER' && (
if (value === 'HOUR' && signOutStartTimeNum > 24) { <Select
data.signOutStartTimeNum = 24; style={{ width: 72, marginRight: 4 }}
} value={signOutStartTimeUnit}
this.setState(data); onChange={(value) => {
}} const data = { signOutStartTimeUnit: value };
> if (value === 'HOUR' && signOutStartTimeNum > 24) {
{unitList.map(item => ( data.signOutStartTimeNum = 24;
<Option value={item.key} key={item.key}>{item.value}</Option> }
))} this.setState(data);
</Select>} }}>
{signOutType === 'START_LATER' && <span className="switch-label">就可签退,截止签退时间为下课后</span>} {unitList.map((item) => (
<InputNumber <Option value={item.key} key={item.key}>
value={signOutEndTimeNum} {item.value}
min={1} </Option>
max={signOutEndTimeUnit === 'MINUTE' ? 1440 : 24} ))}
precision={0} </Select>
style={{ margin: '0 4px', width: 90 }} )}
onChange={(value) => { {signOutType === 'START_LATER' && <span className='switch-label'>就可签退,截止签退时间为下课后</span>}
this.setState({ signOutEndTimeNum: value }) <InputNumber
}} value={signOutEndTimeNum}
/> min={1}
<Select max={signOutEndTimeUnit === 'MINUTE' ? 1440 : 24}
style={{ width: 72, marginRight: 4 }} precision={0}
value={signOutEndTimeUnit} style={{ margin: '0 4px', width: 90 }}
onChange={(value) => { onChange={(value) => {
const data = { signOutEndTimeUnit: value } this.setState({ signOutEndTimeNum: value });
if (value === 'HOUR' && signOutEndTimeNum > 24) { }}
data.signOutEndTimeNum = 24; />
} <Select
this.setState(data); style={{ width: 72, marginRight: 4 }}
}} value={signOutEndTimeUnit}
> onChange={(value) => {
{unitList.map(item => ( const data = { signOutEndTimeUnit: value };
<Option value={item.key} key={item.key}>{item.value}</Option> if (value === 'HOUR' && signOutEndTimeNum > 24) {
))} data.signOutEndTimeNum = 24;
</Select> }
{signOutType !== 'START_LATER' && this.setState(data);
<span className="switch-label">内可签退</span> }}>
} {unitList.map((item) => (
</div>} <Option value={item.key} key={item.key}>
{item.value}
</Option>
))}
</Select>
{signOutType !== 'START_LATER' && <span className='switch-label'>内可签退</span>}
</div>
)}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="footer"> <div className='footer'>
<Button onClick={this.handleGoBack}>取消</Button> <Button onClick={this.handleGoBack}>取消</Button>
<Button onClick={this.handleShowPreviewModal}>预览</Button> <Button onClick={this.handleShowPreviewModal}>预览</Button>
<Button type="primary" onClick={_.debounce(() => this.preSubmit(), 3000, true)}>保存</Button> <Button type='primary' onClick={_.debounce(() => this.preSubmit(), 3000, true)}>
保存
</Button>
</div> </div>
{showSelectCoverModal && {showSelectCoverModal && (
<SelectPrepareFileModal <SelectPrepareFileModal
key="basic" key='basic'
operateType="select" operateType='select'
multiple={false} multiple={false}
accept="image/jpeg,image/png,image/jpg" accept='image/jpeg,image/png,image/jpg'
selectTypeList={['JPG', 'JPEG', 'PNG']} selectTypeList={['JPG', 'JPEG', 'PNG']}
tooltip='支持文件类型:jpg、jpeg、png' tooltip='支持文件类型:jpg、jpeg、png'
isOpen={showSelectCoverModal} isOpen={showSelectCoverModal}
onClose={() => { onClose={() => {
this.setState({ showSelectCoverModal: false }) this.setState({ showSelectCoverModal: false });
}} }}
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
} )}
<Modal <Modal
title="设置图片" title='设置图片'
width={1080} width={1080}
visible={visible} visible={visible}
maskClosable={false} maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>} closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
onCancel={() => { onCancel={() => {
this.setState({ visible: false }); this.setState({ visible: false });
}} }}
zIndex={10001} zIndex={10001}
footer={[ footer={[
<Button <Button
key="back" key='back'
onClick={() => { onClick={() => {
this.setState({ visible: false }); this.setState({ visible: false });
}} }}>
>
重新上传 重新上传
</Button>, </Button>,
<Button <Button
key="submit" key='submit'
type="primary" type='primary'
disabled={!hasImgReady} disabled={!hasImgReady}
onClick={() => { onClick={() => {
if (!cutFlag) { if (!cutFlag) {
...@@ -1330,39 +1380,36 @@ class AddOfflineCourse extends React.Component { ...@@ -1330,39 +1380,36 @@ class AddOfflineCourse extends React.Component {
this.refs.hiddenBtn.click(); this.refs.hiddenBtn.click();
} }
this.getSignature(cutImageBlob); this.getSignature(cutImageBlob);
}} }}>
>
确定 确定
</Button>, </Button>,
]} ]}>
> <div className='clip-box'>
<div className="clip-box">
<div <div
id="headPicModal" id='headPicModal'
ref="headPicModal" ref='headPicModal'
style={{ style={{
width: "500px", width: '500px',
height: "430px", height: '430px',
marginBottom: 0, marginBottom: 0,
}} }}></div>
></div> <div id='clipBtn' style={{ display: 'none' }} ref='hiddenBtn'></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div> <div className='preview-img'>
<div className="preview-img"> <div className='title'>效果预览</div>
<div className="title">效果预览</div> <div id='preview-url-box' style={{ width: 500, height: 282 }}>
<div id="preview-url-box" style={{width:500,height:282}}> <img src={this.state.dataUrl} style={{ width: '100%' }} alt='' />
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
</div> </div>
<div className="tip-box"> <div className='tip-box'>
<div className="tip">温馨提示</div> <div className='tip'>温馨提示</div>
<div className="tip">①预览效果图时可能存在延迟,单击左侧图片刷新即可</div> <div className='tip'>①预览效果图时可能存在延迟,单击左侧图片刷新即可</div>
<div className="tip">②设置图片时双击可旋转图片,滚动可放大或缩小图片</div> <div className='tip'>②设置图片时双击可旋转图片,滚动可放大或缩小图片</div>
</div> </div>
</div> </div>
</div> </div>
</Modal> </Modal>
{ this.state.previewOfflineModal } {this.state.previewOfflineModal}
</div> </div>
) );
} }
} }
......
.add-offline-course-page { .add-offline-course-page {
position:relative !important; position: relative !important;
.box{ .box {
margin-bottom:52px !important; margin-bottom: 52px !important;
} }
.ant-radio-group { .ant-radio-group {
display: flex; display: flex;
...@@ -39,18 +39,18 @@ ...@@ -39,18 +39,18 @@
position: relative; position: relative;
padding-left: 14px; padding-left: 14px;
&::before { &::before {
content: ""; content: '';
position: absolute; position: absolute;
left: 0px; left: 0px;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
width: 4px; width: 4px;
height: 10px; height: 10px;
background: #2966FF; background: #2966ff;
} }
} }
} }
.label{ .label {
display: inline-block; display: inline-block;
text-align: right; text-align: right;
width: 120px; width: 120px;
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
} }
.required { .required {
position: relative; position: relative;
&::before { &::before {
position: absolute; position: absolute;
content: '*'; content: '*';
...@@ -69,14 +69,14 @@ ...@@ -69,14 +69,14 @@
left: 5px; left: 5px;
top: 6px; top: 6px;
} }
&.label::before { &.label::before {
top: 0; top: 0;
} }
} }
.course-catalog { .course-catalog {
margin-bottom:16px; margin-bottom: 16px;
margin-top:16px; margin-top: 16px;
display: flex; display: flex;
.switch-box { .switch-box {
.switch-item { .switch-item {
...@@ -110,19 +110,19 @@ ...@@ -110,19 +110,19 @@
.select-day { .select-day {
margin-bottom: 4px; margin-bottom: 4px;
.mark-day { .mark-day {
color: #2966FF; color: #2966ff;
} }
} }
} }
.allow-tourist-join{ .allow-tourist-join {
display:flex; display: flex;
margin-bottom:16px; margin-bottom: 16px;
.desc { .desc {
color:#999; color: #999;
margin-left:12px; margin-left: 12px;
} }
.content{ .content {
display:flex; display: flex;
} }
} }
.course-ware { .course-ware {
...@@ -146,62 +146,59 @@ ...@@ -146,62 +146,59 @@
.course-cover { .course-cover {
display: flex; display: flex;
margin-top: 16px; margin-top: 16px;
&__wrap { &__wrap {
position: relative; position: relative;
.tag { .img-content {
border-radius: 2px; margin-top: 8px;
background: #D6D6D6; margin-right: 20px;
font-size: 12px; width: 299px;
height: 18px; height: 169px;
width: 52px; position: relative;
text-align: center;
color: #FFF; img {
position: absolute; width: 100%;
top: 8px; height: 100%;
left: 8px; object-fit: contain;
} border-radius: 4px;
} }
.course-cover__wrap { .tag {
display: flex; border-radius: 2px;
flex-direction: row; background: #d6d6d6;
} font-size: 12px;
height: 18px;
.img-content { width: 52px;
margin-right: 20px; text-align: center;
width: 299px; color: #fff;
height: 169px; position: absolute;
top: 8px;
img { left: 8px;
width: 100%;
height: 100%;
object-fit: contain;
}
}
.opt-btns {
.default-btn {
margin-left: 16px;
color: #2966FF;
cursor: pointer;
&.disabled {
color: #CCC;
cursor: not-allowed;
} }
} }
.ant-upload-list { .opt-btns {
display: none; .default-btn {
margin-left: 14px;
color: #2966ff;
cursor: pointer;
&.disabled {
color: #ccc;
cursor: not-allowed;
}
}
.ant-upload-list {
display: none;
}
.tips {
margin-top: 8px;
color: #999;
}
} }
} }
.tips {
margin-top: 8px;
color: #999;
}
} }
.select-student { .select-student {
...@@ -236,7 +233,7 @@ ...@@ -236,7 +233,7 @@
justify-content: flex-end; justify-content: flex-end;
padding-right: 72px; padding-right: 72px;
background: #fff; background: #fff;
border-top: 1px solid #E8E8E8; border-top: 1px solid #e8e8e8;
z-index: 999; z-index: 999;
.ant-btn { .ant-btn {
margin-left: 10px; margin-left: 10px;
...@@ -250,4 +247,4 @@ ...@@ -250,4 +247,4 @@
position: absolute; position: absolute;
left: 20000px; left: 20000px;
} }
} }
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-07-05 10:48:08 * @Date: 2021-07-05 10:48:08
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-07-05 11:01:19 * @LastEditTime: 2021-07-06 19:49:14
* @Description: 描述一下咯 * @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
...@@ -284,19 +284,13 @@ class BasicInfo extends React.Component { ...@@ -284,19 +284,13 @@ class BasicInfo extends React.Component {
<div className='wether-use'> <div className='wether-use'>
<span className='label'>是否启用:</span> <span className='label'>是否启用:</span>
<div className='content'> <div className='content'>
<div> <Switch
<Switch checked={enableState === 'YES' ? true : false}
checked={enableState === 'YES' ? true : false} onChange={() => {
onChange={() => { this.enableStateChange();
this.enableStateChange(); }}
}} />
/> <div className='instro-text'>{enableState === 'YES' ? '已开启,培训计划可正常分享给学员学习' : '已关闭,培训计划暂不进行分享和学习'}</div>
</div>
<div>
<div className='instro-text'>
<div>{enableState === 'YES' ? '已开启,培训计划可正常分享给学员学习' : '已关闭,培训计划暂不进行分享和学习'}</div>
</div>
</div>
</div> </div>
</div> </div>
<div className='view-range'> <div className='view-range'>
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
} }
.opt-btns { .opt-btns {
.default-btn { .default-btn {
margin: 0 8px; margin-left: 14px;
color: #2966ff; color: #2966ff;
cursor: pointer; cursor: pointer;
&.disabled { &.disabled {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-07-05 10:50:30 * @Date: 2021-07-05 10:50:30
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-07-06 14:57:09 * @LastEditTime: 2021-07-06 15:16:47
* @Description: 描述一下咯 * @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
...@@ -269,7 +269,7 @@ class UserLearnDetailModal extends React.Component { ...@@ -269,7 +269,7 @@ class UserLearnDetailModal extends React.Component {
</div> </div>
); );
}, },
rowExpandable: (record) => record.courseChapterList && record.courseChapterList.length !== 0, rowExpandable: (record) => record.courseChapterList && record.courseChapterList.length > 1,
}} }}
/> />
</div> </div>
......
import React, { useState, useRef, useEffect, useContext } from 'react' import React, { useState, useRef, useEffect, useContext } from 'react';
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from '@/components/Breadcrumbs';
import { Form, Alert, Input, Button, InputNumber, DatePicker, Switch, Radio, message, Modal, Tooltip } from 'antd'; import { Form, Alert, Input, Button, InputNumber, DatePicker, Switch, Radio, message, Modal, Tooltip } from 'antd';
import { Route, withRouter } from 'react-router-dom'; import { Route, withRouter } from 'react-router-dom';
import User from "@/common/js/user"; import User from '@/common/js/user';
import moment from 'moment' import moment from 'moment';
import Service from "@/common/js/service"; import Service from '@/common/js/service';
import _ from 'underscore' import _ from 'underscore';
import GraphicsEditor from '../../course-manage/components/GraphicsEditor'; import GraphicsEditor from '../../course-manage/components/GraphicsEditor';
import SelectPaperModal from './SelectPaperModal' import SelectPaperModal from './SelectPaperModal';
import PreviewModal from './PreviewModal' import PreviewModal from './PreviewModal';
import ShowTips from "@/components/ShowTips"; import ShowTips from '@/components/ShowTips';
import './AddExam.less'; import './AddExam.less';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
function AddExam(props: any) { function AddExam(props: any) {
const paperInfoInit: any = { passScore: 60 }; const paperInfoInit: any = { passScore: 60 };
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
const [paperInfo, setPaperInfo] = useState(paperInfoInit); const [paperInfo, setPaperInfo] = useState(paperInfoInit);
const [paperId, setPaperId] = useState(''); const [paperId, setPaperId] = useState('');
const [passRate, setPassRate] = useState(60);//及格线 const [passRate, setPassRate] = useState(60); //及格线
const [examStartTime, setStartTime] = useState(''); const [examStartTime, setStartTime] = useState('');
const [examEndTime, setExamEndTime] = useState(''); const [examEndTime, setExamEndTime] = useState('');
const [examName, setExamName] = useState(''); const [examName, setExamName] = useState('');
const [needPhone, setNeedPhone] = useState('DO_NOT_NEED_PHONE_VERIFY'); const [needPhone, setNeedPhone] = useState('DO_NOT_NEED_PHONE_VERIFY');
const [needOptionDisorder, setNeedOptionDisorder] = useState('OPTION_SORT'); const [needOptionDisorder, setNeedOptionDisorder] = useState('OPTION_SORT');
const [resultContent, setResultContent] = useState('PASS_AND_SCORE'); const [resultContent, setResultContent] = useState('PASS_AND_SCORE');
const [answerAnalysis, setAnswerAnalysis] = useState('RIGHT_OR_WRONG'); const [answerAnalysis, setAnswerAnalysis] = useState('RIGHT_OR_WRONG');
const [resultShow, setResultShow] = useState('IMMEDIATELY'); const [resultShow, setResultShow] = useState('IMMEDIATELY');
const [examDesc, setExamDesc] = useState(''); const [examDesc, setExamDesc] = useState('');
const [passScore, setPassScore] = useState(100); const [passScore, setPassScore] = useState(100);
const [desclen, setDescLen] = useState(0); const [desclen, setDescLen] = useState(0);
const [check, setCheck] = useState(false); const [check, setCheck] = useState(false);
const [getData, setGetData] = useState(false); const [getData, setGetData] = useState(false);
const [preview, setPreview] = useState(false); const [preview, setPreview] = useState(false);
const [examTotal, setExamTotal] = useState(0); const [examTotal, setExamTotal] = useState(0);
const request = useRef(false); const request = useRef(false);
const { match } = props; const { match } = props;
const [examDuration, setExamDuration] = useState(undefined); const [examDuration, setExamDuration] = useState(undefined);
useEffect(() => { useEffect(() => {
switch (props.type) { switch (props.type) {
case "copy": // 考试列表-复制考试进入 case 'copy': // 考试列表-复制考试进入
case "edit": // 考试列表-编辑考试进入 case 'edit': // 考试列表-编辑考试进入
queryExamDetail(); queryExamDetail();
break; break;
case "organizeExam": // 试卷列表-组织考试进入 case 'organizeExam': // 试卷列表-组织考试进入
case "newPaperToAddExam": // 组卷页面-新建保存试卷并组织考试 case 'newPaperToAddExam': // 组卷页面-新建保存试卷并组织考试
case "editPaperToAddExam": // 组卷页面-编辑保存试卷并组织考试 case 'editPaperToAddExam': // 组卷页面-编辑保存试卷并组织考试
setGetData(true); setGetData(true);
setPaperInfo(props.paperInfo); setPaperInfo(props.paperInfo);
break; break;
} }
}, []) }, []);
useEffect(() => { useEffect(() => {
setPaperId(paperInfo.paperId) setPaperId(paperInfo.paperId);
setPassRate(paperInfo.passRate) setPassRate(paperInfo.passRate);
}, [paperInfo.paperId]);
}, [paperInfo.paperId])
useEffect(() => {
useEffect(() => { setPassScore(Math.round((((paperInfo.totalScore || 0) * (passRate || 0)) as any) / 100));
setPassScore(Math.round((paperInfo.totalScore || 0) * (passRate || 0) as any / 100)) setExamTotal(paperInfo.singleChoiceCnt + paperInfo.multiChoiceCnt + paperInfo.judgeCnt + paperInfo.gapFillingCnt + paperInfo.indefiniteChoiceCnt || 0);
setExamTotal(paperInfo.singleChoiceCnt + paperInfo.multiChoiceCnt + paperInfo.judgeCnt + paperInfo.gapFillingCnt + paperInfo.indefiniteChoiceCnt || 0) }, [paperInfo.paperId, passRate]);
}, [paperInfo.paperId, passRate])
function disabledDate(current: any) {
function disabledDate(current: any) { // Can not select days before today and today
// Can not select days before today and today return current && current < moment().startOf('day');
return current && current < moment().startOf('day'); }
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) as any);
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 (!paperId) {
message.warning('请选择试卷');
return;
}
if (!passRate) {
message.warning('请输入及格线');
return;
} }
function queryExamDetail() { if (!examStartTime || !examEndTime) {
Service.Hades("public/hades/queryExamDetail", { message.warning('请选择考试起止时间');
examId: match.params.id, return;
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 as any)
setAnswerAnalysis(result.answerAnalysis)
setNeedOptionDisorder(result.needOptionDisorder)
setPassScore(result.passScore)
setResultContent(result.resultContent)
setResultShow(result.resultShow)
setGetData(true)
})
} }
function handleSave() { if (Number(examStartTime) < moment().valueOf()) {
if (request.current) { message.warning('开始时间不能早于现在');
return return;
} }
setCheck(true); if (!examDuration) {
const param = { message.warning('请输入考试时长');
return;
}
if (examStartTime + (examDuration as any) * 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: any, type: any) {
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');
},
});
}
let title = '';
switch (props.type) {
case 'add':
case 'organizeExam':
case 'newPaperToAddExam':
case 'editPaperToAddExam':
title = '新建考试';
break;
case 'edit':
title = '编辑考试';
break;
case 'copy':
title = '复制考试';
break;
default:
break;
}
return (
<div className='page examPage'>
<Breadcrumbs navList={title} goBack={handleGoBack} />
<div className='box'>
<div className='show-tips'>
<ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
</div>{' '}
<div className='form'>
<div className='title'>基本信息</div>
<Form labelCol={{ span: 3 }} wrapperCol={{ span: 14 }} layout='horizontal'>
<Form.Item
label='考试名称'
validateStatus={check && (!examName ? '请输入考试名称' : examName.length > 40 && '考试名称最多40字') ? 'error' : ''}
help={check && (!examName ? '请输入考试名称' : examName.length > 40 && '考试名称最多40字')}
required>
<Input
placeholder='请输入考试名称(40字以内)'
maxLength={40}
value={examName}
onChange={(e) => {
setExamName(e.target.value);
}}
style={{ width: 320 }}
/>
</Form.Item>
<Form.Item label='选择试卷' validateStatus={check && !paperId ? 'error' : ''} help={check && !paperId && '请选择试卷'} required>
<Button
onClick={() => {
setShowModal(true);
}}>
{paperInfo.paperId ? '重新选择' : '选择试卷'}
</Button>
{paperInfo.paperId && (
<div className='paperTitle'>
<img src='https://image.xiaomaiketang.com/xm/pY5imEhjzw.png' alt='' /> {paperInfo.paperName}
</div>
)}
{paperInfo.paperId && (
<div className='table'>
<div className='header'>
<div className='item'>单选题</div>
<div className='item'>多选题</div>
<div className='item'>判断题</div>
<div className='item'>填空题</div>
<div className='item long'>不定项选择题</div>
<div className='item'>合计</div>
</div>
<div className='body-list'>
<div className='item'>{paperInfo.singleChoiceCnt || 0}</div>
<div className='item'>{paperInfo.multiChoiceCnt || 0}</div>
<div className='item'>{paperInfo.judgeCnt || 0}</div>
<div className='item'>{paperInfo.gapFillingCnt || 0}</div>
<div className='item long'>{paperInfo.indefiniteChoiceCnt || 0}</div>
<div className='item'>{examTotal}</div>
</div>
<div className='body-list'>
<div className='item'>{paperInfo.singleChoiceScore || 0}</div>
<div className='item'>{paperInfo.multiChoiceScore || 0}</div>
<div className='item'>{paperInfo.judgeScore || 0}</div>
<div className='item'>{paperInfo.gapFillingScore || 0}</div>
<div className='item long'>{paperInfo.indefiniteChoiceScore || 0}</div>
<div className='item'>{paperInfo.totalScore || 0}</div>
</div>
</div>
)}
</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: any) => {
setPassRate(parseInt(value));
}}
style={{ width: 100 }}
/>
<span style={{ marginLeft: 4 }}>%</span>
<span style={{ marginLeft: 16, color: '#999' }}>
{` 总分(${paperInfo.totalScore || 0})*及格线(${passRate || 0}%)=及格分数(${passScore})`}
</span>
</Form.Item>
<Form.Item
label='考试有效期'
validateStatus={check && !examStartTime ? 'error' : ''}
help={check && !examStartTime && '请选择考试起止时间'}
required>
<RangePicker
style={{ width: 320 }}
showTime={{ defaultValue: [moment().add(5, 'minutes'), moment().add(5, 'minutes')] }}
ranges={{
近七天: [moment().add(5, 'minute'), moment().add(6, 'day').endOf('day')],
1个月: [moment().add(5, 'minute'), moment().add(1, 'month').endOf('day')],
3个月: [moment().add(5, 'minute'), moment().add(3, 'month').endOf('day')],
}}
disabledDate={disabledDate}
value={[examStartTime ? moment(Number(examStartTime)) : null, examEndTime ? moment(Number(examEndTime)) : null]}
disabledTime={disabledRangeTime}
format='YYYY/MM/DD HH:mm'
onChange={(date: any) => {
setStartTime(date && date[0]?.valueOf());
setExamEndTime(date && date[1]?.valueOf());
}}
/>
</Form.Item>
<Form.Item label='考试时长' validateStatus={check && !examDuration ? 'error' : ''} help={check && !examDuration && '请输入考试时长'} required>
<InputNumber
value={examDuration}
max={1440}
min={1}
onChange={(value: any) => {
setExamDuration(parseInt(value) as any);
}}
style={{ width: 100 }}
/>
<span style={{ marginLeft: 4 }}>分钟</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个字'}>
{(getData || props.type === 'add') && (
<GraphicsEditor
maxLimit={1000}
isIntro={true}
detail={{
content: examDesc,
}}
onChange={(val: any, len: any) => {
setExamDesc(val);
setDescLen(len);
}}
/>
)}
</Form.Item>
<div className='title' style={{ marginTop: 40 }}>
考试设置
</div>
<Form.Item label='身份验证' required>
<div style={{ display: 'flex', marginLeft: 4 }}>
<Switch
checked={needPhone == 'NEED_PHONE_VERIFY'}
onChange={(val) => {
setNeedPhone(val ? 'NEED_PHONE_VERIFY' : 'DO_NOT_NEED_PHONE_VERIFY');
}}></Switch>
<div style={{ position: 'relative', left: 8, color: '#999' }}>
{needPhone == 'NEED_PHONE_VERIFY' ? '已开启,学员需绑定手机号才可参与考试' : '已关闭,学员无需绑定手机号即可参与考试'}
</div>
</div>
</Form.Item>
<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: any) => {
setResultShow(e.target.value);
}}
value={resultShow}>
<Radio value={'IMMEDIATELY'}>交卷后立即显示考试结果</Radio>
<Radio value={'AFTER_EXAM_END'}>到达考试截止日期才显示结果</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label=' 考试结果内容' required>
<Radio.Group
onChange={(e: any) => {
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: any) => {
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>
</div>
{showModal && (
<SelectPaperModal
onSelect={(info: any) => {
setPaperInfo(info);
}}
paperInfo={paperInfo}
close={() => {
setShowModal(false);
}}></SelectPaperModal>
)}
<div className='footer shrink-footer'>
<Button onClick={handleGoBack}>取消</Button>
<Button
onClick={() => {
setPreview(true);
}}>
预览
</Button>
<Button type='primary' onClick={handleSave}>
保存
</Button>
</div>
{preview && (
<PreviewModal
info={{
paperId, paperId,
startTime: examStartTime, startTime: examStartTime,
endTime: examEndTime, endTime: examEndTime,
...@@ -119,384 +514,17 @@ function AddExam(props: any) { ...@@ -119,384 +514,17 @@ function AddExam(props: any) {
resultContent, resultContent,
answerAnalysis, answerAnalysis,
resultShow, resultShow,
examDuration: (examDuration || 0) * 60 * 1000, examDuration,
passScore, passScore,
tenantId: User.getStoreId(), examTotal,
userId: User.getStoreUserId(), totalScore: paperInfo.totalScore,
source: 0, }}
examId: '' onClose={() => {
} setPreview(false);
}}></PreviewModal>
if (!param.examName) { )}
message.warning('请输入考试名称');
return
}
if (param.examName && param.examName.length > 40) {
message.warning('考试名称最多40字');
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 as any) * 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: any, type: any) {
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")
}
})
}
let title = '';
switch (props.type) {
case 'add':
case "organizeExam":
case "newPaperToAddExam":
case "editPaperToAddExam":
title = '新建考试';
break;
case 'edit':
title = '编辑考试';
break;
case 'copy':
title = '复制考试';
break;
default:
break;
}
return <div className="page examPage">
<Breadcrumbs navList={title} goBack={handleGoBack} />
<div className="box">
<div className="show-tips">
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利" />
</div> <div className="form">
<div className="title">基本信息</div>
<Form
labelCol={{ span: 3 }}
wrapperCol={{ span: 14 }}
layout="horizontal"
>
<Form.Item label="考试名称"
validateStatus={(check && (!examName ? '请输入考试名称' : (examName.length > 40) && '考试名称最多40字')) ? 'error' : ''}
help={check && (!examName ? '请输入考试名称' : (examName.length > 40) && '考试名称最多40字')}
required>
<Input placeholder='请输入考试名称(40字以内)' maxLength={40} value={examName} onChange={(e) => {
setExamName(e.target.value)
}} style={{ width: 320 }} />
</Form.Item>
<Form.Item label="选择试卷"
validateStatus={(check && !paperId) ? 'error' : ''}
help={check && !paperId && '请选择试卷'}
required>
<Button onClick={() => { setShowModal(true) }} >{paperInfo.paperId ? '重新选择' : '选择试卷'}</Button>
{
paperInfo.paperId && <div className="paperTitle"><img src="https://image.xiaomaiketang.com/xm/pY5imEhjzw.png" alt="" /> {paperInfo.paperName}</div>
}
{
paperInfo.paperId && <div className="table">
<div className="header">
<div className="item">单选题</div>
<div className="item">多选题</div>
<div className="item">判断题</div>
<div className="item">填空题</div>
<div className="item long">不定项选择题</div>
<div className="item">合计</div>
</div>
<div className="body-list">
<div className="item">{paperInfo.singleChoiceCnt || 0}</div>
<div className="item">{paperInfo.multiChoiceCnt || 0}</div>
<div className="item">{paperInfo.judgeCnt || 0}</div>
<div className="item">{paperInfo.gapFillingCnt || 0}</div>
<div className="item long">{paperInfo.indefiniteChoiceCnt || 0}</div>
<div className="item">{examTotal}</div>
</div>
<div className="body-list">
<div className="item">{paperInfo.singleChoiceScore || 0}</div>
<div className="item">{paperInfo.multiChoiceScore || 0}</div>
<div className="item">{paperInfo.judgeScore || 0}</div>
<div className="item">{paperInfo.gapFillingScore || 0}</div>
<div className="item long">{paperInfo.indefiniteChoiceScore || 0}</div>
<div className="item">{paperInfo.totalScore || 0}</div>
</div>
</div>
}
</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: any) => { setPassRate(parseInt(value)) }} style={{ width: 100 }} />
<span style={{ marginLeft: 4 }}>%
</span>
<span style={{ marginLeft: 16, color: "#999" }}>
{` 总分(${paperInfo.totalScore || 0})*及格线(${passRate || 0}%)=及格分数(${passScore})`}</span>
</Form.Item>
<Form.Item label="考试有效期"
validateStatus={(check && !examStartTime) ? 'error' : ''}
help={check && !examStartTime && '请选择考试起止时间'}
required>
<RangePicker
style={{ width: 320 }}
showTime={{ defaultValue: [moment().add(5, 'minutes'), moment().add(5, 'minutes')] }}
ranges={{
'近七天': [moment().add(5, 'minute'), moment().add(6, 'day').endOf('day')],
'近1个月': [moment().add(5, 'minute'), moment().add(1, 'month').endOf('day')],
'近3个月': [moment().add(5, 'minute'), moment().add(3, 'month').endOf('day')],
}}
disabledDate={disabledDate}
value={[
examStartTime ? moment(Number(examStartTime)) : null,
examEndTime ? moment(Number(examEndTime)) : null
]}
disabledTime={disabledRangeTime}
format="YYYY/MM/DD HH:mm"
onChange={(date: any) => {
setStartTime(date && date[0]?.valueOf());
setExamEndTime(date && date[1]?.valueOf());
}}
/>
</Form.Item>
<Form.Item label="考试时长"
validateStatus={(check && !examDuration) ? 'error' : ''}
help={check && !examDuration && '请输入考试时长'}
required>
<InputNumber value={examDuration} max={1440} min={1} onChange={(value: any) => { setExamDuration(parseInt(value) as any) }} style={{ width: 100 }} />
<span style={{ marginLeft: 4 }}>分钟
</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个字'}
>
{
(getData || (props.type === 'add')) && <GraphicsEditor
maxLimit={1000}
isIntro={true}
detail={{
content: examDesc
}}
onChange={(val: any, len: any) => { setExamDesc(val); setDescLen(len) }}
/>
}
</Form.Item>
<div className="title" style={{ marginTop: 40 }}>考试设置</div>
<Form.Item label="身份验证" required>
<div style={{ display: 'flex', marginLeft: 4, }}>
<Switch style={{ position: 'relative', top: 6 }}
checked={needPhone == 'NEED_PHONE_VERIFY'}
onChange={(val) => { setNeedPhone(val ? 'NEED_PHONE_VERIFY' : 'DO_NOT_NEED_PHONE_VERIFY') }}
></Switch>
<div style={{ position: 'relative', top: 3, left: 8, color: "#999" }}><p>开启:需要绑定手机号的学员才能参加考试</p>
<p>关闭:微信/企业微信登陆直接参加考试</p></div>
</div>
</Form.Item>
<Form.Item label="选项乱序" required>
<div style={{ display: 'flex', marginLeft: 4, }}>
<Switch style={{ position: 'relative', top: 6 }}
checked={needOptionDisorder == 'OPTION_RANDOM'}
onChange={(val) => { setNeedOptionDisorder(val ? 'OPTION_RANDOM' : 'OPTION_SORT') }}
></Switch>
<div style={{ position: 'relative', top: 3, left: 8, color: "#999" }}><p>开启:选择题的选项随机排序</p>
<p>关闭:选择题按题目原有顺序展示</p></div>
</div>
</Form.Item>
<Form.Item label="考试结果查看" required>
<Radio.Group onChange={(e: any) => { setResultShow(e.target.value) }} value={resultShow}>
<Radio value={'IMMEDIATELY'}>交卷后立即显示考试结果</Radio>
<Radio value={'AFTER_EXAM_END'}>到达考试截止日期才显示结果</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label=" 考试结果内容" required>
<Radio.Group onChange={(e: any) => { 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: any) => { 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>
</div>
{
showModal && <SelectPaperModal onSelect={(info: any) => {
setPaperInfo(info)
}} paperInfo={paperInfo} close={() => { setShowModal(false) }}></SelectPaperModal>
}
<div className="footer shrink-footer">
<Button onClick={handleGoBack}>取消</Button>
<Button onClick={() => { setPreview(true) }}>预览</Button>
<Button type="primary" onClick={handleSave}>保存</Button>
</div>
{
preview && <PreviewModal
info={{
paperId,
startTime: examStartTime,
endTime: examEndTime,
examName,
passRate: passRate / 100,
examStartTime,
examEndTime,
examDesc,
needPhone,
needOptionDisorder,
resultContent,
answerAnalysis,
resultShow,
examDuration,
passScore,
examTotal,
totalScore: paperInfo.totalScore
}}
onClose={() => { setPreview(false) }}></PreviewModal>
}
</div> </div>
);
} }
export default withRouter(AddExam);
export default withRouter(AddExam);
\ No newline at end of file
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