Commit da9c44e2 by yuananting

fix:解决合并课程改造分支的冲突

parents 806f97d6 bfb8d3c0
......@@ -13,8 +13,9 @@
}
.container {
overflow: scroll;
height: 100%;;
overflow-y: auto;
overflow-x: hidden;
height: 100%;
.course-cover, .course-url {
width: 100%;
......
......@@ -133,12 +133,13 @@ const FileTypeIcon = {
PPTX: "https://image.xiaomaiketang.com/xm/847pFAdYGW.png",
PDF: "https://image.xiaomaiketang.com/xm/rrEJMNkhTG.png",
MP3: "https://image.xiaomaiketang.com/xm/ykjnSWDyQ6.png",
MP4: "https://image.xiaomaiketang.com/xm/whSYMTdR57.png",
MP4: "https://image.xiaomaiketang.com/xm/TKwbQGYDBR.png",
JPG: "https://image.xiaomaiketang.com/xm/XRkX8JBTPs.png",
JPEG: "https://image.xiaomaiketang.com/xm/XRkX8JBTPs.png",
PNG: "https://image.xiaomaiketang.com/xm/XRkX8JBTPs.png",
GIF: "https://image.xiaomaiketang.com/xm/XRkX8JBTPs.png",
BMP: "https://image.xiaomaiketang.com/xm/XRkX8JBTPs.png",
VIDEO: "https://image.xiaomaiketang.com/xm/TKwbQGYDBR.png"
};
const UploadIcon = "https://image.xiaomaiketang.com/xm/4DXNrZWWsd.png";
......
.ant-popover .ant-popover-content .ant-popover-inner {
.contact-widget-popover .ant-popover-content .ant-popover-inner {
box-shadow: 0px 2px 20px 0px rgba(0, 0, 0, 0.06);
.ant-popover-inner-content {
padding: 0;
......
......@@ -27,6 +27,7 @@ function Content() {
export default function ContactWidget(props:ContactWidgetProps) {
return <Popover
className="contact-widget-popover"
placement={props.placement}
arrowPointAtCenter
content={Content}
......
......@@ -1156,3 +1156,15 @@ window.XMShowClassName = (date, itemName) => {
}
return 'new-icon'
}
// 格式化时间段为时分
window.formatDuration = function (time) {
const diff = Math.floor(time % 3600);
let hours = Math.floor(time / 3600);
let mins = Math.floor(diff / 60);
let seconds = Math.floor(time % 60);
hours = hours < 10 ? ("0" + hours) : hours;
mins = mins < 10 ? ("0" + mins) : mins;
seconds = seconds < 10 ? ("0" + seconds) : seconds;
return hours + ":" + mins + ":" + seconds;
};
\ No newline at end of file
/*
* @Author: yuananting
* @Date: 2021-03-03 15:13:12
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-22 14:31:46
* @LastEditors: yuananting
* @LastEditTime: 2021-07-09 16:58:34
* @Description: 助学工具接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -88,3 +88,7 @@ export function batchQueryQuestionDetails(params: object) {
export function queryQuestionPageListWithContent(params: object) {
return Service.Hades('public/hades/queryQuestionPageListWithContent', params);
}
export function queryCategoryTreeByPackage(params: object) {
return Service.Hades('public/externalHades/queryPackageCategory', params);
}
\ No newline at end of file
/*
* @Author: wufan
* @Date: 2020-12-12 11:57:10
* @LastEditors: zangsuyun
* @LastEditTime: 2021-03-22 13:54:20
* @LastEditors: wufan
* @LastEditTime: 2021-07-05 15:07:13
* @Description: Description
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -57,7 +57,7 @@ export function getLiveCloudCourseBasePage(params: object) {
return Service.Hades("public/courseCloud/getLiveCloudCourseBasePage", params);
}
//视频课相关接口
//线上课相关接口
export function changeVideoShelfState(params: object) {
return Service.Hades("public/hades/changeVideoShelfState", params);
}
......@@ -73,6 +73,9 @@ export function editVideoSchedule(params: object) {
export function userWatchInfo(params: object) {
return Service.Hades("public/hades/userWatchInfo", params);
}
export function lineDetailWatchInfo(params: object) {
return Service.Hades("public/hades/lineDetailWatchInfo", params);
}
export function videoScheduleDetail(params: object) {
return Service.Hades("public/hades/videoScheduleDetail", params);
}
......
......@@ -81,7 +81,7 @@ class KnowledgeAPI {
exportPicLearnSync = (params: object) => {
return Service.Hades("public/knowledge/exportPicLearnSync", params);
}
// 视频课观看记录导出
// 线上课观看记录导出
exportVideoLearnSync = (params: object) => {
return Service.Hades("public/knowledge/exportVideoLearnSync", params);
}
......
/*
* @Author: zhangleyuan
* @Date: 2021-02-21 16:08:38
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-09 12:54:31
* @LastEditors: yuananting
* @LastEditTime: 2021-07-06 11:26:27
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import Service from "@/common/js/service";
import Service from '@/common/js/service';
export function getTrainingPlanPage(params: object) {
return Service.Hades("public/hades/getTrainingPlanPage", params);
return Service.Hades('public/hades/getTrainingPlanPage', params);
}
export function createTrainingPlan(params: object) {
return Service.Hades("public/hades/createTrainingPlan", params);
return Service.Hades('public/hades/createTrainingPlan', params);
}
export function updateStateTrainingPlan(params: object) {
return Service.Hades("public/hades/updateStateTrainingPlan", params);
return Service.Hades('public/hades/updateStateTrainingPlan', params);
}
export function getTrainingPlanDetail(params: object) {
return Service.Hades("public/hades/getTrainingPlanDetail", params);
return Service.Hades('public/hades/getTrainingPlanDetail', params);
}
export function updateTrainingPlan(params: object) {
return Service.Hades("public/hades/updateTrainingPlan", params);
return Service.Hades('public/hades/updateTrainingPlan', params);
}
export function deleteTrainingPlan(params: object) {
return Service.Hades("public/hades/deleteTrainingPlan", params);
return Service.Hades('public/hades/deleteTrainingPlan', params);
}
export function getPlanUserRecordPage(params: object) {
return Service.Hades("public/hades/getPlanUserRecordPage", params);
return Service.Hades('public/hades/getPlanUserRecordPage', params);
}
export function getPlanCustomerRecordPage(params: object) {
return Service.Hades("public/hades/getPlanCustomerRecordPage", params);
return Service.Hades('public/hades/getPlanCustomerRecordPage', params);
}
export function getPlanCustomerDetail(params: object) {
return Service.Hades("public/hades/getPlanCustomerDetail", params);
return Service.Hades('public/customerHades/getPlanCustomerDetail', params);
}
export function getPlanCustomerAboutUser(params: object) {
return Service.Hades("public/hades/getPlanCustomerAboutUser", params);
return Service.Hades('public/hades/getPlanCustomerAboutUser', params);
}
export function removePlanCustomer(params: object) {
return Service.Hades("public/hades/removePlanCustomer", params);
return Service.Hades('public/hades/removePlanCustomer', params);
}
export function getStorePlanAll(params: object) {
return Service.Hades("public/hades/getStorePlanAll", params);
return Service.Hades('public/hades/getStorePlanAll', params);
}
export function getTrainingCourseAutoCancel(params: object) {
return Service.Hades("public/hades/getTrainingCourseAutoCancel", params);
return Service.Hades('public/hades/getTrainingCourseAutoCancel', params);
}
/*
* @Author: yuananting
* @Date: 2021-03-11 11:34:37
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-06-16 09:56:46
* @LastEditors: yuananting
* @LastEditTime: 2021-07-09 16:54:54
* @Description: 助学工具接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import {
queryExternalCategoryTree,
queryCategoryTreeByPackage,
queryCategoryTree,
addCategory,
delCategory,
......@@ -38,6 +39,11 @@ export default class AidToolService {
return queryExternalCategoryTree(parmas);
}
// 课程分类树(按课程包)
static queryCategoryTreeByPackage(params: any) {
return queryCategoryTreeByPackage(params);
}
// 获取题目分类树
static queryCategoryTree(params: any) {
return queryCategoryTree(params);
......
......@@ -9,7 +9,8 @@
import {
fetchLecturerData, getCategoryTree, knowledgeMediaCoursePage, fetchUserData, exportStudentCourseData, exportPlayBackCourseData, fetchPlaybackList, createLiveCloudCourse, getLiveCloudCoursePage,
getLiveCloudCourseDetail, updateLiveCloudCourse, turnOnOrOffLiveCloudCourse, delLiveCloudCourse, changeVideoShelfState, createVideoSchedule, delVideoSchedule,
editVideoSchedule, userWatchInfo, videoSchedulePage, videoScheduleDetail, videoWatchInfo, getQrcode, getLiveCloudCourseBasePage, videoScheduleBasePage, relatedCourseToPlan
editVideoSchedule, userWatchInfo, videoSchedulePage, videoScheduleDetail, videoWatchInfo, getQrcode, getLiveCloudCourseBasePage, videoScheduleBasePage, relatedCourseToPlan,
lineDetailWatchInfo
} from '@/data-source/course/request-api';
export default class courseService {
......@@ -85,6 +86,9 @@ export default class courseService {
static videoWatchInfo(params: any) {
return videoWatchInfo(params);
}
static lineDetailWatchInfo(params: any) {
return lineDetailWatchInfo(params);
}
static getLiveCloudCourseBasePage(params: any) {
return getLiveCloudCourseBasePage(params);
}
......
......@@ -19,7 +19,7 @@
/>
<meta
name="keywords"
content="小麦企学院,企业培训,员工培训,企业大学,企业内训,企业外训,培训计划,培训素材,企培,企训,资料云盘,培训课程,培训任务,直播课,视频课,图文课,线下课,知识库,作业,考试,排行榜,培训类别管理,定制培训计划,管理数据,学习数据,企学院,资料共享,培训数字化,数字化培训,培训工具,在线培训,线上培训,培训saas,培训管理,企业微信培训,对客培训,客户培训,直播培训,互联网培训,新员工培训,管理培训,管理者培训,工人培训,制造业培训,餐饮培训,服务业培训,零售培训,门店培训,工厂培训,车间培训,培训补贴,人事培训,财务培训,职场培训,企业学院平台,教育企业学院,教育企业平台,教育平台学院,企业学习,酷学院,小鹅通,企业学院,云学堂,时代光华,云课堂,魔学院,云大学,米知云,授课学堂"
content="小麦企学院,企业培训,员工培训,企业大学,企业内训,企业外训,培训计划,培训素材,企培,企训,资料云盘,培训课程,培训任务,直播课,线上课,图文课,线下课,知识库,作业,考试,排行榜,培训类别管理,定制培训计划,管理数据,学习数据,企学院,资料共享,培训数字化,数字化培训,培训工具,在线培训,线上培训,培训saas,培训管理,企业微信培训,对客培训,客户培训,直播培训,互联网培训,新员工培训,管理培训,管理者培训,工人培训,制造业培训,餐饮培训,服务业培训,零售培训,门店培训,工厂培训,车间培训,培训补贴,人事培训,财务培训,职场培训,企业学院平台,教育企业学院,教育企业平台,教育平台学院,企业学习,酷学院,小鹅通,企业学院,云学堂,时代光华,云课堂,魔学院,云大学,米知云,授课学堂"
/>
<!-- <link rel="apple-touch-icon" href="../src/common/images/logo.png" /> -->
<link rel="shortcut icon" href="https://image.xiaomaiketang.com/xm/c4KiP2epBP.png" />
......
......@@ -2,41 +2,25 @@
.label {
width: 100px;
text-align: right;
display:inline-block;
display: inline-block;
.require {
color: #EC4B35;
color: #ec4b35;
}
}
.course-cover {
margin-left: 14px;
display: flex;
margin-top: 16px;
margin-top: 24px;
&__wrap {
position: relative;
.tag {
border-radius: 2px;
background: #D6D6D6;
font-size: 12px;
height: 18px;
width: 52px;
text-align: center;
color: #FFF;
position: absolute;
top: 8px;
left: 8px;
}
}
.course-cover__wrap {
display: flex;
flex-direction: row;
}
.img-content {
margin-top: 8px;
margin-right: 20px;
width: 299px;
height: 169px;
position: relative;
img {
width: 100%;
......@@ -44,17 +28,28 @@
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;
}
}
.opt-btns {
.default-btn {
margin-left: 16px;
color: #2966FF;
margin-left: 14px;
color: #2966ff;
cursor: pointer;
&.disabled {
color: #CCC;
color: #ccc;
cursor: not-allowed;
}
}
......@@ -62,21 +57,21 @@
.ant-upload-list {
display: none;
}
}
.tips {
margin-top: 8px;
color: #999;
}
}
.course-catalog{
margin:20px 0 0 14px;
}
}
.course-catalog {
margin: 20px 0 0 14px;
}
}
.ant-cascader-menu-item-active:not(.ant-cascader-menu-item-disabled){
font-weight:normal !important;
color:#2966FF !important;
.ant-cascader-menu-item-active:not(.ant-cascader-menu-item-disabled) {
font-weight: normal !important;
color: #2966ff !important;
}
#imgCutModalNew {
width: 500px;
......
.graphics-editor-container {
border: 1px solid #E8E8E8;
border: 1px solid #e8e8e8;
border-radius: 4px;
width: 702px;
height: 510px;
......@@ -23,7 +23,7 @@
.w-e-toolbar {
background-color: #fff !important;
border: none !important;
border-bottom: 1px solid #E8E8E8 !important;
border-bottom: 1px solid #e8e8e8 !important;
}
.w-e-text-container {
......@@ -38,19 +38,26 @@
color: #666;
z-index: 1;
}
.editor-warning {
position: absolute;
right: 0;
color: red;
}
.w-e-full-screen-editor {
.w-e-text-container {
height: ~'calc(100vh - 109px)' !important;
}
}
&.introduce {
height: 200px;
height: 240px;
.w-e-text-container {
height: ~'calc(100% - 69px)' !important;
}
}
&.warning{
&.warning {
border-color: red;
}
}
......@@ -27,6 +27,7 @@ import _ from 'underscore';
import Upload from '@/core/upload';
import ImgClipModal from '@/components/ImgClipModal'
import './AddGraphicsCourse.less';
import Bus from '@/core/bus';
const EDIT_BOX_KEY = Math.random();
const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryList' };
......@@ -34,7 +35,7 @@ const fieldNames = { label: 'categoryName', value: 'id', children: 'sonCategoryL
//添加课程时课程默认的一些值
const defaultShelfState = 'YES';
const whetherVisitorsJoin = 'NO';
const defaultCoverUrl = 'https://image.xiaomaiketang.com/xm/wFnpZtp2yB.png';
const defaultCover = 'https://image.xiaomaiketang.com/xm/wFnpZtp2yB.png';
let cutFlag = false;
class AddGraphicsCourse extends React.Component {
......@@ -53,7 +54,7 @@ class AddGraphicsCourse extends React.Component {
introduce: '',
courseMediaId: null, // 图文课链接
coverId: null, // 图文封面的recourceId
coverUrl: defaultCoverUrl, // 图文课封面
coverUrl: defaultCover, // 图文课封面
studentList: [], // 上课学员列表
shelfState: 'YES', //是否开启学院展示
selectedFileList: [], // 已经从资料云盘中勾选的文件
......@@ -73,6 +74,11 @@ class AddGraphicsCourse extends React.Component {
if (pageType === 'edit') {
this.handleFetchScheudleDetail(id);
}
Bus.bind('editorLimit', (editorTextLength, editorType) => {
this.setState({
[editorType]: editorTextLength,
});
});
}
initBus = () => {
......@@ -314,11 +320,11 @@ class AddGraphicsCourse extends React.Component {
//过期判断
if (User.getExpirationTime() && moment().valueOf() > Number(User.getExpirationTime())) {
Modal.warning({
title:"服务已到期",
content: "当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买",
okText: "我知道了"
})
return
title: '服务已到期',
content: '当前企业购买的小麦企学院服务已到期,如需继续使用学院功能,请尽快续费购买',
okText: '我知道了',
});
return;
}
const { id, coverId, pageType, courseName, courseMedia, introduce, categoryId, shelfState, whetherVisitorsJoin } = this.state;
......@@ -387,6 +393,7 @@ class AddGraphicsCourse extends React.Component {
};
handleValidate = (courseName, courseMedia, categoryId) => {
const { graphicsCourseIntor, graphicsCourseContent } = this.state;
return new Promise((resolve) => {
if (!courseName) {
message.warning('请输入课程名称');
......@@ -403,6 +410,18 @@ class AddGraphicsCourse extends React.Component {
resolve(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');
// for (let i = 0, len = textMedia.length; i < len; i++) {
// if (textMedia[i].mediaContentLength && textMedia[i].mediaContentLength.length > 1000) {
......@@ -415,6 +434,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() {
const {
id,
......@@ -442,6 +472,8 @@ class AddGraphicsCourse extends React.Component {
const hasSelectedStu = studentList.length;
const courseWareIcon = FileVerifyMap[videoType] ? FileTypeIcon[FileVerifyMap[videoType].type] : FileTypeIcon[videoType];
// 当前是否使用的是默认图片
const isDefaultCover = coverUrl === defaultCover;
return (
<div className='page add-graphics-course-page'>
......@@ -468,19 +500,39 @@ class AddGraphicsCourse extends React.Component {
<div className='cover-url flex mt16'>
<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'>
<img src={coverUrl} />
</div>
</div> */}
<div className='course-cover__wrap'>
<div className='opt-btns'>
<Button
onClick={() => {
this.setState({
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>
<div className='img-content'>
{isDefaultCover && <span className='tag'>默认图</span>}
<img src={coverUrl} />
</div>
</div>
</div>
<div className='course-catalog required'>
......
.add-graphics-course-page {
position:relative !important;
.box{
margin-bottom:52px !important;
position: relative !important;
.box {
margin-bottom: 52px !important;
}
.ant-radio-group {
display: flex;
flex-direction: column;
.radio-item {
margin-bottom: 12px;
......@@ -28,10 +27,10 @@
.form {
margin-top: 16px;
padding: 0 16px;
.label{
display:inline-block;
text-align:right;
width:85px;
.label {
display: inline-block;
text-align: right;
width: 85px;
}
.required {
position: relative;
......@@ -48,9 +47,9 @@
top: 0;
}
}
.course-catalog{
margin-bottom:16px;
margin-top:16px;
.course-catalog {
margin-bottom: 16px;
margin-top: 16px;
}
.course-ware {
display: flex;
......@@ -71,10 +70,20 @@
display: flex;
}
.cover-url__wrap {
.course-cover {
margin-left: 14px;
display: flex;
margin-top: 24px;
&__wrap {
position: relative;
.img-content {
width: 298px;
height: 172px;
margin-top: 8px;
margin-right: 20px;
width: 299px;
height: 169px;
position: relative;
img {
width: 100%;
......@@ -82,29 +91,78 @@
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;
.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 {
margin-top: 8px;
display: flex;
align-items: center;
.default-btn {
margin-left: 14px;
color: #2966ff;
cursor: pointer;
&.disabled {
color: #ccc;
cursor: not-allowed;
}
}
.ant-upload-list {
display: none;
}
.tips {
margin-left: 12px;
margin-top: 8px;
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 {
align-items: center;
......@@ -139,7 +197,7 @@
justify-content: flex-end;
padding-right: 72px;
background: #fff;
border-top: 1px solid #E8E8E8;
border-top: 1px solid #e8e8e8;
z-index: 999;
.ant-btn {
margin-left: 10px;
......
......@@ -17,17 +17,15 @@ import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepare
import { DISK_MAP } from '@/common/constants/academic/lessonEnum';
import { ImgCutModalNew } from '@/components';
const { TextArea } = Input;
class AddGraphicsIntro extends React.Component {
constructor(props) {
super(props);
this.state = {
showSelectFileModal: false,
diskList: [],
selectType: null,
}
};
}
// 上传封面图
......@@ -38,34 +36,34 @@ class AddGraphicsIntro extends React.Component {
imageFile,
showCutModal: true,
});
}
};
// 选择暖场资源
handleSelectVideo = (file) => {
const { selectType } = this.state;
this.setState({
showSelectFileModal: false
})
showSelectFileModal: false,
});
const { ossUrl, resourceId, folderName, folderFormat, folderSize } = file;
if(selectType === 'WARMUP'){
if (selectType === 'WARMUP') {
const liveCourseWarmMedia = {
contentType: 'WARMUP',
mediaType: folderFormat === 'MP4' ? 'VIDEO' : 'PICTURE',
mediaContent: resourceId,
mediaUrl: ossUrl,
mediaName: folderName,
size: folderSize
}
size: folderSize,
};
this.props.onChange('liveCourseWarmMedia', liveCourseWarmMedia);
}else{
} else {
// 最多添加九图片
const { liveCourseMediaRequests } = this.props.data;
const list = _.filter(liveCourseMediaRequests, (item) => {
return item.mediaType == "PICTURE";
return item.mediaType == 'PICTURE';
});
if (list.length > 8) {
message.warning("最多添加9张图片");
message.warning('最多添加9张图片');
return;
}
liveCourseMediaRequests.push({
......@@ -78,121 +76,116 @@ class AddGraphicsIntro extends React.Component {
});
this.props.onChange('liveCourseMediaRequests', liveCourseMediaRequests);
}
}
};
changeDetail = (value) => {
this.props.onChange('courseMedia', value);
}
};
changeIntro = (value) => {
this.props.onChange('introduce', value);
}
};
whetherVisitorsJoinChange = ()=>{
if(this.props.data.whetherVisitorsJoin==="NO"){
this.props.onChange('whetherVisitorsJoin','YES')
}else{
this.props.onChange('whetherVisitorsJoin','NO')
}
whetherVisitorsJoinChange = () => {
if (this.props.data.whetherVisitorsJoin === 'NO') {
this.props.onChange('whetherVisitorsJoin', 'YES');
} else {
this.props.onChange('whetherVisitorsJoin', 'NO');
}
};
shelfStateChange = ()=>{
if(this.props.data.shelfState==="NO"){
this.props.onChange('shelfState','YES')
}else{
this.props.onChange('shelfState','NO')
}
shelfStateChange = () => {
if (this.props.data.shelfState === 'NO') {
this.props.onChange('shelfState', 'YES');
} else {
this.props.onChange('shelfState', 'NO');
}
};
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;
return (
<div className="add-video__intro-info">
<div className="allow-tourist-join">
<span className="label">观看设置:</span>
<div className="content">
<div>
<Switch checked={whetherVisitorsJoin==="YES"? true:false} onChange={this.whetherVisitorsJoinChange}/>
</div>
<div>
<div className="desc">
<div>开启:允许未绑定手机号的学员观看</div>
<div>关闭:仅限绑定了手机号的学员可以进入观看图文课</div>
</div>
</div>
</div>
</div>
<div className="store-show">
<span className="label">学院展示:</span>
<div className="content">
<Row>
<Col span={3}>
<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 className="introduce required">
<span className="label" style={{ marginTop: 5 }}>课程内容:</span>
<div className="content">
<div className="intro-list">
<div className="intro-list__item content-editor">
{(!id || loadcourseMedia) &&
<div className='add-graphic__intro-info'>
<div className='allow-tourist-join'>
<span className='label'>观看设置:</span>
<div className='content'>
<Switch checked={whetherVisitorsJoin === 'NO' ? true : false} onChange={this.whetherVisitorsJoinChange} />
<div className='desc'>{whetherVisitorsJoin === 'NO' ? '已开启,学员需绑定手机号才可观看' : '已关闭,学员无需绑定手机号即可观看'}</div>
</div>
</div>
<div className='store-show'>
<span className='label'>学院展示:</span>
<div className='content'>
<Switch checked={shelfState === 'YES' ? true : false} onChange={this.shelfStateChange} />
<div className='desc'>{shelfState === 'YES' ? '已开启,课程将在该学院的学员课程列表中显示' : '已关闭,课程将在该学院的学员课程列表中隐藏'}</div>
</div>
</div>
<div className='introduce required'>
<span className='label' style={{ marginTop: 5 }}>
课程内容:
</span>
<div className='content'>
<div className='intro-list'>
<div className='intro-list__item content-editor'>
{(!id || loadcourseMedia) && (
<GraphicsEditor
id="content"
id='content'
maxLimit={1000}
editorType={'graphicsCourseContent'}
detail={{
content: courseMedia
content: courseMedia,
}}
onChange={(val) => {
this.changeDetail(val);
}}
onChange={(val) => { this.changeDetail(val) }}
/>
}
)}
</div>
</div>
</div>
</div>
<div className="introduce">
<span className="label">课程简介:</span>
<div className="content">
<div className="intro-list">
<div className="intro-list__item introduce-editor">
{(!id || loadintroduce) &&
<div className='introduce'>
<span className='label'>课程简介:</span>
<div className='content'>
<div className='intro-list'>
<div className='intro-list__item introduce-editor'>
{(!id || loadintroduce) && (
<GraphicsEditor
id="intro"
id='intro'
isIntro={true}
maxLimit={1000}
editorType={'graphicsCourseIntor'}
detail={{
content: introduce
content: introduce,
}}
onChange={(val) => {
this.changeIntro(val);
}}
onChange={(val) => { this.changeIntro(val) }}
/>
}
)}
</div>
</div>
</div>
</div>
{/* 选择暖场图文件弹窗 */}
{ showSelectFileModal &&
{showSelectFileModal && (
<SelectPrepareFileModal
operateType="select"
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'] }
tooltip={ selectType==="INTRO"?'支持文件类型:jpg、jpeg、png':'支持文件类型:jpg、jpeg、png、mp4'}
operateType='select'
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']}
tooltip={selectType === 'INTRO' ? '支持文件类型:jpg、jpeg、png' : '支持文件类型:jpg、jpeg、png、mp4'}
isOpen={showSelectFileModal}
onClose={() => {
this.setState({ showSelectFileModal: false })
this.setState({ showSelectFileModal: false });
}}
onSelect={this.handleSelectVideo}
/>
}
)}
</div>
)
);
}
}
......
.add-video__intro-info {
.add-graphic__intro-info {
.w-e-full-screen-editor {
background: #fff !important;
}
.playback {
margin-bottom: 10px;
.require {
color: #EC4B35;
color: #ec4b35;
}
&__text {
color: #999;
}
}
.label{
display:inline-block;
text-align:right;
width:85px;
.label {
display: inline-block;
text-align: right;
width: 85px;
}
.playback,
.introduce {
display: flex;
flex-direction: row;
}
.allow-tourist-join{
display:flex;
.content{
display:flex;
.allow-tourist-join {
display: flex;
.content {
display: flex;
}
.desc{
margin-left:16px;
font-size:14px;
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;
.store-show {
display: flex;
margin-top: 16px;
margin-bottom: 16px;
.content {
display: flex;
}
.desc {
margin-left: 16px;
font-size: 14px;
color: #999;
display: inline-block;
}
}
.radio {
......@@ -59,13 +62,13 @@
.intro-list__item {
display: flex;
margin-bottom: 16px;
margin-bottom: 32px;
position: relative;
&.picture {
width: 550px;
padding: 16px;
border: 1px solid #EEE;
border: 1px solid #eee;
border-radius: 4px;
.img__wrap {
......@@ -114,7 +117,7 @@
line-height: 80px;
padding: 16px;
margin-top: 16px;
border: 1px dashed #EBEBEB;
border: 1px dashed #ebebeb;
border-radius: 4px;
.ant-upload {
......@@ -133,7 +136,7 @@
.iconfont {
font-size: 22px;
line-height: 22px;
color: #BFBFBF;
color: #bfbfbf;
text-align: center;
}
......@@ -152,7 +155,7 @@
}
.checkExample {
width: 60px;
color: #FF7519;
color: #ff7519;
cursor: pointer;
}
.warmup {
......@@ -197,21 +200,19 @@
}
}
.opt-btns {
.default-btn {
margin-left: 16px;
color: #FF7519;
color: #ff7519;
cursor: pointer;
&.disabled {
color: #CCC;
color: #ccc;
cursor: not-allowed;
}
}
}
}
.example-wrap {
}
.example-wrap {
font-family: PingFangSC-Regular, PingFang SC;
text-align: center;
.title {
......@@ -227,14 +228,14 @@
img {
width: 180px;
}
}
.check-record-rule {
}
.check-record-rule {
width: 120px;
color: #FF7519;
color: #ff7519;
cursor: pointer;
z-index: 2;
}
.record-rule-wrap {
}
.record-rule-wrap {
text-align: left;
ul {
margin-top: 10px;
......@@ -247,9 +248,9 @@
.text {
color: #999;
}
}
}
.auto-send-class-report {
.auto-send-class-report {
.label {
width: 57px;
height: 12px;
......@@ -258,7 +259,8 @@
color: #666666;
line-height: 12px;
}
.open-text, .close-text {
.open-text,
.close-text {
width: 572px;
font-size: 14px;
font-weight: 400;
......@@ -273,4 +275,4 @@
.close-text {
margin-bottom: 16px;
}
}
\ No newline at end of file
}
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:12:45
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-15 20:01:05
* @Description: 视频课-列表模块
* @LastEditors: wufan
* @LastEditTime: 2021-07-05 10:23:10
* @Description: 线上课-列表模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import User from '@/common/js/user';
......@@ -333,7 +333,7 @@ class GraphicsCourseList extends React.Component {
// 删除视频课
handleDeleteGraphicsCourse = (scheduleId) => {
Modal.confirm({
title: '你确定要删除此视频课吗?',
title: '你确定要删除此线上课吗?',
content: '删除后,学员将不能进行观看。',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
okText: '确定',
......
......@@ -3,7 +3,7 @@
* @Date: 2020-08-05 10:12:15
* @LastEditors: zhangleyuan
* @LastEditTime: 2020-12-26 16:07:27
* @Description: 视频课-操作模块
* @Description: 线上课-操作模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
......
......@@ -17,13 +17,13 @@ class GraphicsCourse extends React.Component {
courseType: 'PICTURE',
storeId:User.getStoreId()
},
dataSource: [], // 视频课列表
totalCount: 0, // 视频课数据总条数
dataSource: [], // 线上课列表
totalCount: 0, // 线上课数据总条数
}
}
componentWillMount() {
// 获取视频课列表
// 获取线上课列表
this.handleFetchScheduleList();
}
......@@ -76,7 +76,7 @@ class GraphicsCourse extends React.Component {
{/* 操作模块 */}
<GraphicsCourseOpt />
{/* 视频课列表模块 */}
{/* 线上课列表模块 */}
<GraphicsCourseList
query={query}
dataSource={dataSource}
......
......@@ -13,7 +13,8 @@
}
.container {
overflow: scroll;
overflow-y: auto;
overflow-x: hidden;
height: 100%;;
.course-cover, .course-url {
......@@ -30,6 +31,16 @@
color: #000;
}
.title__category {
color: #999999;
margin-top: 4px;
margin-bottom: 4px;
}
.title__chapter {
color: #999999;
margin-bottom: 4px;
}
.title__inst-name {
color: #666;
font-size: 12px;
......
......@@ -116,11 +116,7 @@ class ShareLiveModal extends React.Component {
}
break;
case 'videoClass': // 视频课
coverImgSrc =
coverUrl ||
(courseDivision === 'internal'
? `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast&anystring=anystring`
: 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png');
coverImgSrc = coverUrl || 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png';
break;
case 'graphicsClass': // 图文课
coverImgSrc = coverUrl || 'https://image.xiaomaiketang.com/xm/wFnpZtp2yB.png';
......
.add-offline-course-page {
position:relative !important;
.box{
margin-bottom:52px !important;
position: relative !important;
.box {
margin-bottom: 52px !important;
}
.ant-radio-group {
display: flex;
......@@ -39,18 +39,18 @@
position: relative;
padding-left: 14px;
&::before {
content: "";
content: '';
position: absolute;
left: 0px;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 10px;
background: #2966FF;
background: #2966ff;
}
}
}
.label{
.label {
display: inline-block;
text-align: right;
width: 120px;
......@@ -75,8 +75,8 @@
}
}
.course-catalog {
margin-bottom:16px;
margin-top:16px;
margin-bottom: 16px;
margin-top: 16px;
display: flex;
.switch-box {
.switch-item {
......@@ -110,19 +110,19 @@
.select-day {
margin-bottom: 4px;
.mark-day {
color: #2966FF;
color: #2966ff;
}
}
}
.allow-tourist-join{
display:flex;
margin-bottom:16px;
.allow-tourist-join {
display: flex;
margin-bottom: 16px;
.desc {
color:#999;
margin-left:12px;
color: #999;
margin-left: 12px;
}
.content{
display:flex;
.content {
display: flex;
}
}
.course-ware {
......@@ -150,45 +150,41 @@
&__wrap {
position: relative;
.tag {
border-radius: 2px;
background: #D6D6D6;
font-size: 12px;
height: 18px;
width: 52px;
text-align: center;
color: #FFF;
position: absolute;
top: 8px;
left: 8px;
}
}
.course-cover__wrap {
display: flex;
flex-direction: row;
}
.img-content {
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;
}
}
.opt-btns {
.default-btn {
margin-left: 16px;
color: #2966FF;
margin-left: 14px;
color: #2966ff;
cursor: pointer;
&.disabled {
color: #CCC;
color: #ccc;
cursor: not-allowed;
}
}
......@@ -196,13 +192,14 @@
.ant-upload-list {
display: none;
}
}
.tips {
margin-top: 8px;
color: #999;
}
}
}
}
.select-student {
align-items: center;
......@@ -236,7 +233,7 @@
justify-content: flex-end;
padding-right: 72px;
background: #fff;
border-top: 1px solid #E8E8E8;
border-top: 1px solid #e8e8e8;
z-index: 999;
.ant-btn {
margin-left: 10px;
......
......@@ -109,7 +109,7 @@ class AddGraphicsIntro extends React.Component {
const {data: { id, whetherVisitorsJoin, courseMedia, introduce, shelfState, loadcourseMedia, loadintroduce } } = this.props;
const { showSelectFileModal, selectType } = this.state;
return (
<div className="add-video__intro-info">
<div className="add-offline__intro-info">
<div className="allow-tourist-join">
<span className="label">观看设置:</span>
<div className="content">
......
.add-video__intro-info {
.add-offline__intro-info {
.w-e-full-screen-editor {
background: #fff !important;
}
......
......@@ -3,7 +3,7 @@
* @Date: 2020-08-05 10:12:45
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-11 16:44:42
* @Description: 视频课-列表模块
* @Description: 线上课-列表模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import Service from '@/common/js/service';
......@@ -29,7 +29,7 @@ class OfflineCourseList extends React.Component {
constructor(props) {
super(props);
this.state = {
id: '', // 视频课ID
id: '', // 线上课ID
studentIds: [],
};
}
......
......@@ -3,7 +3,7 @@
* @Date: 2020-08-05 10:12:15
* @LastEditors: zhangleyuan
* @LastEditTime: 2020-12-26 16:07:27
* @Description: 视频课-操作模块
* @Description: 线上课-操作模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
......
......@@ -14,7 +14,7 @@ const hasExportPermission = (type) => {
return Permission.hasInteractiveExport();
}
// 视频课导出权限
// 线上课导出权限
if (type === 'videoClass') {
return Permission.hasVideoClassExport();
}
......
......@@ -49,14 +49,16 @@
}
}
.course-catalog{
margin-bottom:16px;
margin-top:16px;
margin-bottom:24px;
margin-top:24px;
}
.course-ware {
display: flex;
align-items: center;
margin-bottom: 4px;
&__index {
width: 18px;
}
&__img {
width: 24px;
margin-right: 4px;
......@@ -75,7 +77,19 @@
.img-content {
width: 298px;
height: 172px;
position: relative;
.tag {
border-radius: 2px;
background: rgba(51, 51, 51, .2);
font-size: 12px;
height: 18px;
width: 52px;
text-align: center;
color: #FFF;
position: absolute;
top: 8px;
left: 8px;
}
img {
width: 100%;
height: 100%;
......@@ -85,7 +99,7 @@
.empty-img {
width: 298px;
height: 172px;
border: 1px dashed #EBEBEB;
border: 1px dashed #ebebeb;
border-radius: 4px;
padding: 12px;
color: #999;
......@@ -94,12 +108,17 @@
}
.opt-btns {
margin-top: 8px;
display: flex;
align-items: center;
margin-bottom: 8px;
.span {
color: #ccc;
margin-left: 8px;
}
.defalut-span {
cursor: pointer;
color: #2966ff;
}
.tips {
margin-left: 12px;
margin-top: 8px;
color: #999;
}
}
......@@ -115,18 +134,97 @@
color: #333;
}
}
.upload-video {
display: flex;
margin-bottom: 24px;
.sub-content {
margin-left: 85px;
margin-top: 4px;
.btn-wrap {
display: flex;
.course-ware--empty {
margin-left: 12px;
line-height: 28px;
}
}
.tips {
margin-left: 4px;
margin-top: 8px;
color: #999;
}
}
}
.course-chapter-list-wrap {
position: relative;
margin-left: 85px;
border: 1px solid #E8E8E8;
width: 630px;
.course-chapter-total {
height: 40px;
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
z-index: 10;
background: #fff;
width: 628px;
text-indent: 16px;
line-height: 40px;
}
.course-chapter-list {
max-height: 245px;
min-height: 130px;
overflow-y: auto ;
border-radius: 4px;
padding: 16px;
.course-ware {
display: flex;
align-items: center;
margin-bottom: 22px;
color: #666;
&:last-child {
margin-bottom: 0px;
}
&__img {
width: 24px;
margin-right: 8px;
margin-left: 16px;
}
&__name {
color: #333;
width: 353px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin-right: 24px;
}
.course-chapter__opt {
display: flex;
color: #2966ff;
width: 168px;
.line {
color: #bfbfbf;
margin-left: 4px;
margin-right: 4px;
}
.delete,
.rename,
.up,
.down {
cursor: pointer;
}
.up,
.down {
&.disabled {
color: rgba(204, 204, 204, 1);
}
}
}
}
}
}
}
.footer {
position: fixed;
left: 196px;
......@@ -138,10 +236,25 @@
justify-content: flex-end;
padding-right: 72px;
background: #fff;
border-top: 1px solid #E8E8E8;
border-top: 1px solid #ccc2c2;
z-index: 999;
.ant-btn {
margin-left: 10px;
}
}
}
.chapter-popover {
.ant-popover-message-title {
padding-left: 0;
}
.ant-form-item {
margin-bottom: 0!important;
}
.ant-form-item-explain.ant-form-item-explain-error {
height: 24px;
}
.ant-form-item-explain {
min-height: 0;
}
}
\ No newline at end of file
.video-course-detail {
padding-bottom: 40px;
.box {
.course-detail {
display: flex;
border-bottom: 2px solid #E8E8E8;
padding-bottom: 24px;
&__img {
margin-right: 24px;
width: 300px;
height: 170px;
background: #FFFFFF;
border-radius: 4px;
img {
width: 300px;
height: 170px;
border-radius: 4px;
}
}
&__info {
display: flex;
flex-direction: column;
.info__title {
font-size: 20px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 28px;
margin-bottom: 16px;
}
.info__category, .info__chapterNum {
width: 100%;
height: 20px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 20px;
margin-bottom: 10px;
}
}
}
.course-chapter {
padding-top: 24px;
&__total {
width: 100%;
height: 20px;
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 20px;
margin-bottom: 24px;
}
&__list {
.course-ware {
display: flex;
height: 20px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 20px;
margin-bottom: 28px;
cursor: pointer;
&:hover {
color: #2966FF;
}
&__index {
width: 20px;
}
&__img {
width: 20px;
height: 20px;
margin-left: 16px;
margin-right: 8px;
}
&__name {
width: 700px;
}
}
}
}
}
}
\ No newline at end of file
/*
* @Author: wufan
* @Date: 2020-04-28 18:05:30
* @LastEditors: yuananting
* @LastEditTime: 2021-07-09 16:21:13
* @Description: 线上课课程课节详情
*/
import React,{useEffect, useState} from 'react';
import Breadcrumbs from "@/components/Breadcrumbs";
import { withRouter } from "react-router-dom";
import CourseService from '@/domains/course-domain/CourseService'
import underscore from 'underscore';
import ScanFileModal from '@/modules/resource-disk/modal/ScanFileModal.jsx';
import './VideoCourseDetail.less';
declare var getParameterByName: any;
declare var window: any;
function VideoCourseDetail(){
const courseId = window.getParameterByName("courseId"); // 课程ID
const [coverUrl,setCoverUrl] = useState("");
const [videoName,setVideoName] = useState("");
const [scheduleVideoUrl,setScheduleVideoUrl] = useState("");
const [courseChapterList,setCourseChapterList] = useState<Array<Object>>([]);
const [courseName,setCourseName] = useState("");
const [categoryName,setCategoryName] = useState("");
const [scanFileModal,setScanFileModal] = useState<any>(null);
useEffect(()=>{
handleFetchScheudleDetail(courseId);
},[courseId])
// 获取线上课详情
function handleFetchScheudleDetail(courseId: string){
CourseService.videoScheduleDetail({
courseId
}).then((res) => {
const { result = {} } = res || {}
const { courseName, courseMediaVOS, categoryName, courseChapterVOList =[] } = result
let coverId:string = "";
let coverUrl: string = "";
let videoType: string = "";
let videoDuration: number = 0;
let videoName: string = "";
let scheduleVideoUrl: string = "";
courseMediaVOS.map((item:any) => {
switch (item.contentType) {
case 'COVER':
coverId = item.mediaContent
coverUrl = item.mediaUrl
break
case 'SCHEDULE':
videoDuration = item.videoDuration
videoName = item.mediaName
scheduleVideoUrl = item.mediaUrl
videoType = item.mediaType
break
default:
break
}
return item
})
setCoverUrl(coverUrl);
setVideoName(videoName);
setScheduleVideoUrl(scheduleVideoUrl);
setCourseName(courseName);
setCategoryName(categoryName);
setCourseChapterList(courseChapterVOList);
})
}
function handleScanFileModal(fileType: string = "MP4", fileObj: Object){
const scanFileModal = (
<ScanFileModal
fileType={fileType}
item={fileObj}
close={() => {
setScanFileModal(null);
}}
/>
);
setScanFileModal(scanFileModal)
}
return <div className="page video-course-detail">
<Breadcrumbs
navList="课程详情"
goBack={() => {
window.RCHistory.goBack();
}}
/>
<div className="box">
<div className="course-detail">
<div className='course-detail__img'>
{/* 如果视频和封面都没有上传的话, 那么就显示缺省, 如果上传了视频, 那么封面图就默认显示视频的第一帧, 如果上传了封面图, 那么就显示上传的封面图 */}
<img src={coverUrl || `https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png`} alt='' />
</div>
<div className="course-detail__info">
<div className="info__title">{courseName}</div>
<div className="info__category">{`分类:${categoryName}`}</div>
<div className="info__chapterNum">{`课节数量:${courseChapterList.length}`}</div>
</div>
</div>
<div className="course-chapter">
<div className="course-chapter__total">{`共${courseChapterList.length}个课节`}</div>
<div className="course-chapter__list">
<If condition={courseChapterList.length > 0}>
{
underscore.map(courseChapterList,(item: any,index: number) => {
return <div className='course-ware' onClick={()=>{handleScanFileModal("MP4",item)}} key={index}>
<div className="course-ware__index">{index < 9 ? `0${index + 1 } ` : index + 1}</div>
<img className='course-ware__img' src='https://image.xiaomaiketang.com/xm/TKwbQGYDBR.png' alt='' />
<div className='course-ware__name'>{item.name}</div>
</div>
})
}
</If>
</div>
</div>
</div>
{scanFileModal}
</div>
}
export default withRouter(VideoCourseDetail);
\ No newline at end of file
......@@ -117,12 +117,18 @@ class AddVideoIntro extends React.Component {
<span className="label">观看设置:</span>
<div className="content">
<div>
<Switch checked={whetherVisitorsJoin==="YES"? true:false} onChange={this.whetherVisitorsJoinChange}/>
<Switch checked={whetherVisitorsJoin==="NO"? true:false} onChange={this.whetherVisitorsJoinChange}/>
</div>
<div>
<div className="desc">
<div>开启:允许未绑定手机号的学员观看</div>
<div>关闭:仅限绑定了手机号的学员可以进入观看视频</div>
<Choose>
<When condition={whetherVisitorsJoin==="NO"}>
<div>已开启,学员需绑定手机号才可观看</div>
</When>
<Otherwise>
<div>已关闭,学员无需绑定手机号即可观看</div>
</Otherwise>
</Choose>
</div>
</div>
</div>
......@@ -136,20 +142,27 @@ class AddVideoIntro extends React.Component {
</Col>
<Col span={21}>
<div className="desc">
<div>开启:此视频将在学员学院的视频列表中出现</div>
<div>关闭:此视频将在学员学院的视频列表中隐藏</div>
<Choose>
<When condition={shelfState==="YES"}>
<div>已开启,课程将在该学院的学员课程列表中显示</div>
</When>
<Otherwise>
<div>已关闭,课程将在该学院的学员课程列表中隐藏</div>
</Otherwise>
</Choose>
</div>
</Col>
</Row>
</div>
</div>
<div className="introduce">
<span className="label">视频课简介:</span>
<span className="label">课程简介:</span>
<div className="content">
<div className="intro-list">
<div className="intro-list__item introduce-editor">
{(!id || loadintroduce) &&
<GraphicsEditor
maxLimit={1000}
id="intro"
isIntro={true}
detail={{
......
......@@ -32,14 +32,17 @@
}
.store-show{
display:flex;
margin-top:16px;
margin-bottom:16px;
margin-top:24px;
margin-bottom:24px;
.desc{
margin-left:16px;
margin-left:11px;
font-size:14px;
color:#999;
display:inline-block;
}
.content {
width: 400px;
}
}
.radio {
display: block;
......
import React from 'react';
import './ChapterList.less';
function ChapterList(props){
const { courseChapterList } = props;
return <div className='chapter-list-component'>
<If condition={courseChapterList.length > 0}>
{
_.map(courseChapterList,(item,index) => {
return <div className='course-ware'>
<div className='course-ware__index'>{index < 9 ? `0${index + 1 } ` : `${index + 1 } `}</div>
<div className="course-ware__detail">
<div className='course-ware__detail__name'>{item.mediaName}</div>
<div className='course-ware__detail__duration'>{window.formatDuration(item.videoDuration)}</div>
</div>
</div>
})
}
</If>
</div>
}
export default ChapterList;
\ No newline at end of file
.chapter-list-component {
.course-ware {
display: flex;
padding: 10px 0;
border-bottom: 1px dashed #EEEEEE;
&:last-child {
border-bottom: none;
}
&__index {
width: 18px;
height: 18px;
font-size: 13px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #999999;
line-height: 18px;
}
&__detail {
display: flex;
flex-direction: column;
width: calc(~'100% - 18px');
&__name {
width: 267px;
font-size: 13px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 21px;
margin-bottom: 4px;
}
&__duration {
color: #999999;
}
}
}
}
\ No newline at end of file
import User from "@/common/js/user";
import CourseService from "@/domains/course-domain/CourseService";
import React from "react";
import { Modal } from "antd";
import "./LearningDetailModal.less";
class LearningDetailModal extends React.Component {
constructor(props) {
super(props);
this.state = {
courseChapterList: [],
};
}
componentDidMount() {
this.handleFetchDataList();
}
// 获取观看视频数据列表
handleFetchDataList = () => {
const { courseId, data } = this.props;
const params = {
courseId,
storeId: User.getStoreId(),
storeCustomerId: data.storeCustomerId,
};
CourseService.lineDetailWatchInfo(params).then((res) => {
const { result = [] } = res;
this.setState({
courseChapterList: result
});
});
};
render() {
const { title, onClose, onOk } = this.props;
const { courseChapterList } = this.state;
return (
<Modal
footer={null}
visible={true}
title={title}
width={680}
maskClosable={false}
closeIcon={
<span className="icon iconfont modal-close-icon">&#xe6ef;</span>
}
onCancel={onClose}
onOk={onOk}
className="learning-detail-modal"
>
<div className="course-chapter__list">
<If condition={courseChapterList.length > 0}>
{_.map(courseChapterList, (item, index) => {
return (
<div className="course-wrap">
<div className="course-ware" key={index}>
<div className="course-ware__index">{`${
index < 9 ? `0${index + 1}` : index + 1
} `}</div>
<img
className="course-ware__img"
src="https://image.xiaomaiketang.com/xm/TKwbQGYDBR.png"
alt=""
/>
<div className="course-ware__name">{item.courseChapterName && item.courseChapterName.replace('.MP4','')}</div>
</div>
<div className={`progress ${item.progress === 100 ? 'finish' :''}`}>{`${item.progress === 100 ? '已完成' : `${item.progress}%`}`}</div>
</div>
);
})}
</If>
</div>
</Modal>
);
}
}
export default LearningDetailModal;
.learning-detail-modal {
.course-chapter {
padding-top: 24px;
&__list {
.course-wrap {
display: flex;
justify-content: space-between;
.course-ware {
display: flex;
height: 20px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 20px;
margin-bottom: 28px;
&__index {
width: 20px;
}
&__img {
width: 20px;
height: 20px;
margin-left: 12px;
margin-right: 8px;
}
&__name {
width: 500px;
}
}
.progress {
&.finish {
color: RGBA(101, 202, 168, 1);
}
}
}
}
}
}
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:11:57
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-05-28 20:14:37
* @Description: 视频课-搜索模块
* @LastEditors: yuananting
* @LastEditTime: 2021-07-12 14:14:47
* @Description: 线上课-搜索模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
......@@ -56,22 +56,17 @@ class VideoCourseFilter extends React.Component {
this.getTeacherList()
this.queryCategoryTree()
}
// 查询分类树
queryCategoryTree = (categoryName = '') => {
let query = {
bizType: 'QUESTION',
source: 2,
categoryName,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
count: false,
queryCategoryTree = ()=> {
const params= {
storeId:User.getStoreId()
}
AidToolService.queryExternalCategoryTree(query).then((res) => {
const { categoryList = [] } = res.result
console.log(this.renderTreeNodes(categoryList))
this.setState({
categoryList: this.renderTreeNodes(categoryList),
categoryName,
StoreService.getStoreDetail(params).then((res) => {
const { result = {} } = res;
result.coursePackageId && AidToolService.queryCategoryTreeByPackage({coursePackageId: result.coursePackageId}).then((res) => {
const { result = [] } = res;
this.setState({categoryList: this.renderTreeNodes(result)})
})
})
}
......@@ -192,10 +187,10 @@ class VideoCourseFilter extends React.Component {
<Row type='flex' justify='space-between' align='top'>
<div className='search-condition'>
<div className='search-condition__item'>
<span className='search-name'>视频课名称:</span>
<span className='search-name'>线上课名称:</span>
<Search
value={courseName}
placeholder='搜索视频课名称'
placeholder='搜索线上课名称'
onChange={(e) => {
this.handleChangeQuery('courseName', e.target.value)
}}
......
import { Dropdown, message, Modal, Switch, Tooltip } from 'antd';
import User from '@/common/js/user';
import { PageControl } from '@/components';
import { LIVE_SHARE } from '@/domains/course-domain/constants';
import CourseService from '@/domains/course-domain/CourseService';
import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal';
import React from 'react';
import RelatedPlanModal from '../../modal/RelatedPlanModal';
import WatchDataModal from '../modal/WatchDataModal';
import React from "react"
import { Modal, message, Tooltip, Switch, Dropdown } from "antd"
import _ from "underscore"
import { PageControl } from "@/components"
import { LIVE_SHARE } from "@/domains/course-domain/constants"
import { Route, withRouter } from 'react-router-dom';
import ShareLiveModal from "@/modules/course-manage/modal/ShareLiveModal"
import CourseService from "@/domains/course-domain/CourseService"
import RelatedPlanModal from "../../modal/RelatedPlanModal"
import User from "@/common/js/user"
import VideoCourseDetail from '../VideoCourseDetail';
import WatchData from "./WatchData";
import { XMTable } from '@/components';
import college from '@/common/lottie/college';
import './VideoCourseList.less';
import "./VideoCourseList.less"
class VideoCourseList extends React.Component {
constructor(props) {
......@@ -32,21 +35,20 @@ class VideoCourseList extends React.Component {
}
}
// 跳转课程详情页
handleLinkToCourseDetail = (courseId) => {
const { match } = this.props;
window.RCHistory.push(`${match.url}/video-course-detail?courseId=${courseId}`)
}
// 观看数据弹窗
handleShowWatchDataModal = (record) => {
const watchDataModal = (
<WatchDataModal
type='videoCourseList'
data={record}
close={() => {
this.setState({
watchDataModal: null,
});
}}
/>
);
this.setState({ watchDataModal });
};
handleShowWatchDataModal = (item) => {
const { match } = this.props;
window.RCHistory.push({
pathname: `${match.url}/course-data?courseName=${item.courseName}&courseId=${item.id}`
})
}
// 请求表头
parseColumns = () => {
......@@ -54,23 +56,21 @@ class VideoCourseList extends React.Component {
const { ShelfLoading } = this.state;
const columns = [
{
title: '视频课',
key: 'scheduleName',
dataIndex: 'scheduleName',
title: "线上课",
key: "scheduleName",
dataIndex: "scheduleName",
width: 321,
fixed: 'left',
render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record;
const { coverUrl } = record;
return (
<div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img
className='course-cover'
src={
coverUrl ||
(type === 'internal' ? `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast` : 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png')
coverUrl || 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png'
}
alt=''
alt='封面图'
/>
<Choose>
<When condition={record.courseName.length > 25}>
......@@ -99,9 +99,9 @@ class VideoCourseList extends React.Component {
</If>
</span>
),
key: 'categoryName',
dataIndex: 'categoryName',
width: 200,
key: "categoryName",
dataIndex: "categoryName",
width: 150,
render: (val, record) => {
return (
<Choose>
......@@ -119,21 +119,15 @@ class VideoCourseList extends React.Component {
},
},
{
title: '创建人',
key: 'createName',
dataIndex: 'createName',
title: '课节数',
key: 'chapterNum',
dataIndex: 'chapterNum',
className: "chapterNum",
width: 100,
render: (val) => {
return (
<div>
{val && (
<Tooltip title={val}>
<div>{val.length > 4 ? `${val.slice(0, 4)}...` : val}</div>
</Tooltip>
)}
</div>
);
},
align: 'right',
render: (val,item) => {
return <div onClick={()=>this.handleLinkToCourseDetail(item.id)}>{val || 1}</div>
}
},
{
title: (
......@@ -170,13 +164,31 @@ class VideoCourseList extends React.Component {
},
},
{
title: '观看学员数',
width: 110,
key: 'watchUserCount',
dataIndex: 'watchUserCount',
title: "观看学员数",
width: 150,
key: "watchUserCount",
dataIndex: "watchUserCount",
align: 'right',
render: (val, item) => {
return <div className='watchUserCount'>{val || 0}</div>;
return <div className='watchUserCount' onClick={() => this.handleShowWatchDataModal(item)}>{val || 0}</div>
}
},
{
title: "创建人",
key: "createName",
dataIndex: "createName",
width: 100,
render: (val) => {
return (
<div>
{val && (
<Tooltip title={val}>
<div>{val.length > 4 ? `${val.slice(0, 4)}...` : val}</div>
</Tooltip>
)}
</div>
)
}
},
{
title: '创建时间',
......@@ -230,22 +242,17 @@ class VideoCourseList extends React.Component {
title: '操作',
key: 'operate',
dataIndex: 'operate',
width: 210,
width: 160,
fixed: 'right',
render: (val, record) => {
return (
<div className='operate'>
<div className='operate__item' onClick={() => this.handleShowWatchDataModal(record)}>
观看数据
</div>
<If condition={type === 'internal'}>
<span className='operate__item split'> | </span>
<If condition={type === "internal"}>
<div className='operate__item' onClick={() => this.handleShowShareModal(record)}>
分享
</div>
</If>
<span className='operate__item split'> | </span>
</If>
<Dropdown overlay={this.renderMoreOperate(record)}>
<span className='more-operate'>
<span className='operate-text'>更多</span>
......@@ -260,7 +267,7 @@ class VideoCourseList extends React.Component {
},
];
type !== 'internal' && columns.splice(2, 1);
type !== 'internal' && columns.splice(5, 1);
return columns;
};
......@@ -327,7 +334,7 @@ class VideoCourseList extends React.Component {
});
};
// 删除视频
// 删除线上
handleDeleteVideoCourse = (scheduleId) => {
Modal.confirm({
title: '你确定要删除此视频课吗?',
......@@ -369,7 +376,7 @@ class VideoCourseList extends React.Component {
data={shareData}
type='videoClass'
courseDivision={type}
title='视频课'
title='线上课'
close={() => {
this.setState({
shareLiveModal: null,
......@@ -446,7 +453,7 @@ class VideoCourseList extends React.Component {
);
};
render() {
const { dataSource = [], totalCount, query, type } = this.props;
const { dataSource = [], totalCount, query, type, match } = this.props;
const { current, size } = query;
const { RelatedPlanModalVisible, selectPlanList, selectCourseId } = this.state;
return (
......@@ -465,16 +472,6 @@ class VideoCourseList extends React.Component {
scroll={{ x: 1500 }}
className='video-list-table'
/>
{/* <Table
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
onChange={this.handleChangeTable}
pagination={false}
scroll={{ x: 1500 }}
bordered
className='video-list-table'
/> */}
<div className='box-footer'>
<PageControl
......@@ -499,9 +496,11 @@ class VideoCourseList extends React.Component {
)}
{this.state.shareLiveModal}
{this.state.watchDataModal}
<Route path={`${match.url}/video-course-detail`} component={VideoCourseDetail} />
<Route path={`${match.url}/course-data`} component={WatchData} />
</div>
);
}
}
export default VideoCourseList;
export default withRouter(VideoCourseList);
......@@ -24,11 +24,17 @@
}
}
}
.chapterNum {
color: #2966ff;
cursor: pointer;
}
}
}
.watchUserCount {
text-align: right;
padding: 16px;
color: #2966ff;
cursor: pointer;
}
.operate-text {
color: #2966FF;
......
......@@ -3,7 +3,7 @@
* @Date: 2020-08-05 10:12:15
* @LastEditors: zhangleyuan
* @LastEditTime: 2020-12-26 16:07:27
* @Description: 视频课-操作模块
* @Description: 线上课-操作模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
......@@ -22,7 +22,7 @@ export default function VideoCourseOpt() {
RCHistory.push('/create-video-course?type=add');
}}
className="mr12"
>新建视频</Button>
>新建线上</Button>
</div>
);
}
import User from "@/common/js/user";
import college from "@/common/lottie/college";
import { PageControl, XMTable } from "@/components";
import Breadcrumbs from "@/components/Breadcrumbs";
import CourseService from "@/domains/course-domain/CourseService";
import { Input, Select } from "antd";
import React from "react";
import { withRouter } from "react-router-dom";
import LearningDetailModal from "./LearningDetailModal";
import "./WatchData.less";
const { Search } = Input;
class WatchData extends React.Component {
constructor(props) {
super(props);
const courseId = window.getParameterByName("courseId");
const courseName = window.getParameterByName("courseName");
this.state = {
courseName,
courseId,
visible: true,
dataSource: [],
query: {
current: 1,
size: 10,
sourceEnum: null,
},
totalCount: 0,
learningDetailModal: null,
};
}
componentDidMount() {
this.handleFetchDataList();
}
// 获取观看视频数据列表
handleFetchDataList = () => {
const { query, courseId } = this.state;
const params = {
...query,
courseId: courseId,
storeId: User.getStoreId(),
};
CourseService.videoWatchInfo(params).then((res) => {
const { result = {} } = res;
const { records = [], total = 0 } = result;
this.setState({
dataSource: records,
totalCount: Number(total),
});
});
};
parseColumns = () => {
const SOURCENUM = {
WE_CHAT: "微信",
WORK_WE_CHAT: "企业微信",
};
const columns = [
{
title: "观看学员",
key: "name",
dataIndex: "name",
render: (val, item) => {
return (
<div className="watcher">
<div className="watcher__name">{val}</div>
<div className={`watcher__type ${item.sourceEnum}`}>
@{SOURCENUM[item.sourceEnum]}
</div>
</div>
);
},
},
{
title: "手机号",
key: "phone",
dataIndex: "phone",
},
{
title: "首次观看时间",
key: "firstWatch",
dataIndex: "firstWatch",
render: (val) => {
return formatDate("YYYY-MM-DD H:i", val);
},
},
{
title: "学习进度",
key: "progress",
dataIndex: "progress",
render: (val) => {
return <span>{val}%</span>;
},
},
{
title: "操作",
key: "operate",
dataIndex: "operate",
render: (val, record) => {
return (
<div className="operate">
<div
className="operate__item"
onClick={() => this.ShowLearningDetailModal(record)}
>
学习详情
</div>
</div>
);
},
},
];
return columns;
};
// 显示学员数据详情弹窗
ShowLearningDetailModal = (item) => {
const learningDetailModal = (
<LearningDetailModal
data={item}
title="学习详情"
onClose={() => {
this.setState({
learningDetailModal: null
});
}}
courseId={this.state.courseId}
/>
);
this.setState({ learningDetailModal });
};
// 搜索学员姓名或手机号
handleChangNickname = (value) => {
const isPhone = (value || "").match(/^\d+$/);
const { query } = this.state;
let _query = { ...query };
if (isPhone) {
_query.phone = value;
_query.nickName = null;
} else {
_query.nickName = value;
_query.phone = null;
}
_query.current = 1;
this.setState(
{
query: _query,
},
this.handleFetchDataList
);
};
// 搜索学员类型
handleWatchTypeSelect = (sourceEnum) => {
const { query } = this.state;
let _query = { ...query };
_query.sourceEnum = sourceEnum;
this.setState(
{
query: _query,
},
this.handleFetchDataList
);
};
render() {
const { dataSource, totalCount, query, courseName } = this.state;
const { current, size, sourceEnum } = query;
return (
<div className="page video-course-watch-data">
<Breadcrumbs
navList="观看学员数据"
goBack={() => {
window.RCHistory.goBack();
}}
/>
<div className="box">
<div className="box-header">
<div className="course-detail">
<div className="detail__line"></div>
<div className="detail__name">{courseName}</div>
</div>
<div className="filter-box">
<div className="watcher-name">
<span className="label">学员:</span>
<Search
placeholder="搜索学员姓名/手机号"
style={{ width: 200 }}
onChange={(e) => {
this.handleChangNickname(e.target.value);
}}
onSearch={() => {
this.handleFetchDataList();
}}
enterButton={<span className="icon iconfont">&#xe832;</span>}
/>
</div>
<div className="watch-type">
<span className="label">学员类型:</span>
<Select
showSearch
style={{ width: 200 }}
allowClear
onPopupScroll={this.handleFetchMore}
placeholder="请选择学员类型"
value={sourceEnum}
onChange={this.handleWatchTypeSelect}
>
<Select.Option tyle={{ textAlign: "center" }} value="WE_CHAT">
微信
</Select.Option>
<Select.Option
tyle={{ textAlign: "center" }}
value="WORK_WE_CHAT"
>
企业微信
</Select.Option>
</Select>
</div>
</div>
</div>
<XMTable
renderEmpty={{
image: college,
description: "暂无数据",
}}
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
pagination={false}
bordered
className="video-list-table"
/>
<div className="box-footer">
<PageControl
current={current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = { ...query, current: page + 1 };
this.setState(
{
query: _query,
},
() => {
this.handleFetchDataList();
}
);
}}
/>
</div>
</div>
{this.state.learningDetailModal}
</div>
);
}
}
export default withRouter(WatchData);
.video-course-watch-data {
.watcher {
display: flex;
&__type {
margin-left: 8px;
&.WE_CHAT {
color:rgba(28, 172, 27, 1);
}
&.WORK_WE_CHAT {
color: rgba(41, 102, 255, 1);
}
}
}
.filter-box {
display: flex;
margin-bottom: 4px;
.watcher-name {
margin-right: 40px;
}
}
.course-detail {
display: flex;
align-items: center;
margin-bottom: 16px;
.detail__line {
width: 4px;
height: 12px;
background-image: linear-gradient(#2966ff 83.5%, #0acca4 16.5%);
margin-right: 8px;
}
.detail__name {
width: 880px;
height: 26px;
font-size: 19px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 26px;
}
}
}
\ No newline at end of file
......@@ -15,18 +15,18 @@ class VideoCourse extends React.Component {
current: 1,
storeId: User.getStoreId(),
},
dataSource: [], // 视频课列表
totalCount: 0, // 视频课数据总条数
dataSource: [], // 线上课列表
totalCount: 0, // 线上课数据总条数
currentTabKey: 'internal',
};
}
componentWillMount() {
// 获取视频课列表
// 获取线上课列表
this.handleFetchScheduleList();
}
// 获取视频课列表
// 获取线上课列表
handleFetchScheduleList = (_query = {}) => {
const { currentTabKey } = this.state;
const query = {
......@@ -81,7 +81,7 @@ class VideoCourse extends React.Component {
const { dataSource, totalCount, query, currentTabKey } = this.state;
return (
<div className='page video-course-page'>
<div className='content-header'>视频</div>
<div className='content-header'>线上</div>
<div className='box'>
<Tabs onChange={this.currenTabChange} activeKey={currentTabKey}>
......@@ -95,7 +95,7 @@ class VideoCourse extends React.Component {
<If condition={currentTabKey === 'internal'}>
<VideoCourseOpt />
</If>
{/* 视频课列表模块 */}
{/* 线上课列表模块 */}
<VideoCourseList
type={currentTabKey}
query={query}
......
......@@ -3,7 +3,7 @@
* @Date: 2020-05-19 11:01:31
* @Last Modified by: 吴文洁
* @Last Modified time: 2020-05-25 16:50:47
* @Description 余额异常弹窗
* @Description 学员观看数据弹窗
*/
import User from '@/common/js/user';
import { PageControl, XMTable } from '@/components';
......@@ -128,65 +128,52 @@ class WatchDataModal extends React.Component {
const { visible, size, dataSource, totalCount, query } = this.state;
return (
<Modal
title='视频课观看数据'
title="线上课观看数据"
visible={visible}
footer={null}
onCancel={this.onClose}
maskClosable={false}
className='watch-data-modal'
className="watch-data-modal"
closable={true}
width={800}
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}>
<div className='search-container'>
<Search
placeholder='搜索学员姓名/手机号'
style={{ width: 200 }}
onChange={(e) => {
this.handleChangNickname(e.target.value);
}}
onSearch={() => {
this.handleFetchDataList();
}}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div className="search-container">
<Search placeholder="搜索学员姓名/手机号" style={{ width: 200 }} onChange={(e) => { this.handleChangNickname(e.target.value)}} onSearch={ () => { this.handleFetchDataList()}} enterButton={<span className="icon iconfont">&#xe832;</span>}/>
</div>
<div>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.id}
rowKey={record => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
pagination={false}
bordered
/>
{dataSource.length > 0 && (
<div className='box-footer'>
{dataSource.length >0 &&
<div className="box-footer">
<PageControl
current={query.current - 1}
pageSize={size}
total={totalCount}
size='small'
size="small"
toPage={(page) => {
const _query = { ...query, current: page + 1 };
this.setState(
{
query: _query,
},
() => {
this.handleFetchDataList();
}
);
const _query = {...query, current: page + 1};
this.setState({
query:_query
},()=>{ this.handleFetchDataList()})
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
)}
}
</div>
</Modal>
);
)
}
}
......
......@@ -343,7 +343,7 @@ class Home extends React.Component {
</div>
</div>
<div className='course-item'>
<div className='course-title'>视频</div>
<div className='course-title'>线上</div>
<div className='data'>
<span className='course-number'>{videoCourseNum}</span>
{incVideoCourseNum > 0 && <span className='icon iconfont'>&#xe635;</span>}
......@@ -409,9 +409,8 @@ class Home extends React.Component {
</span>
<span
className={`tab${scheduleType === 'VOICE' ? ' selected' : ''}`}
onClick={() => this.setState({ scheduleType: 'VOICE' }, () => this.getHotCourse())}>
视频课
</span>
onClick={() => this.setState({ scheduleType: 'VOICE' }, () => this.getHotCourse())}
>线上课</span>
<span
className={`tab${scheduleType === 'PICTURE' ? ' selected' : ''}`}
onClick={() => this.setState({ scheduleType: 'PICTURE' }, () => this.getHotCourse())}>
......
......@@ -55,7 +55,7 @@ const ENUM = {
CourseTypeEnum: {
LIVE: "直播课",
VOICE: "视频课",
VOICE: "线上课",
PICTURE: "图文课",
FOLDER: "学习资料",
},
......
......@@ -65,10 +65,11 @@ class KnowledgeBaseFilter extends React.Component {
<Row type="flex" justify="space-between" align="top">
<div className="search-condition">
<div className="search-condition__item">
<span className="search-name">课程名称:</span>
<span className="search-name">资料名称:</span>
<Search
value={name}
placeholder="搜索课程名称"
// allowClear
placeholder="搜索资料名称"
onChange={(e) => {
this.handleChangeQuery("name", e.target.value, false);
}}
......@@ -79,33 +80,6 @@ class KnowledgeBaseFilter extends React.Component {
enterButton={<span className="icon iconfont">&#xe832;</span>}
/>
</div>
<div className="search-condition__item">
<span className="shelf-status">课程类型:</span>
<Select
style={{ width: "calc(100% - 84px)" }}
placeholder="请选择课程类型"
allowClear={true}
value={type}
onChange={(value) => {
this.handleChangeQuery("type", value);
}}
suffixIcon={
<span
className="icon iconfont"
style={{ fontSize: "12px", color: "#BFBFBF" }}
>
&#xe835;
</span>
}
>
{Reflect.ownKeys(ENUM.CourseTypeEnum).map((item) => {
return (
<Option key={item} value={item}>{ENUM.CourseTypeEnum[item]}</Option>
);
})}
</Select>
</div>
</div>
<div className="reset-fold-area">
<Tooltip title="清空筛选">
......
......@@ -9,6 +9,7 @@
import React from "react";
import { Modal, message, Tooltip, Switch, Dropdown, Button } from "antd";
import { FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum';
import { Route, withRouter } from "react-router-dom";
import { PageControl, XMTable } from "@/components";
import { LIVE_SHARE_MAP } from "@/common/constants/academic/cloudClass";
......@@ -181,161 +182,20 @@ class KnowledgeBaseList extends React.Component {
const { current, size } = query
const columns = [
{
title: "课程名称",
title: "名称",
key: "name",
dataIndex: "name",
width: 391,
fixed: "left",
render: (val, record) => {
const { coverUrl, mediaCourseUrl, courseDivision } = record.source
let hasCover = false
const type = record.type
return (
<div>
{type === "LIVE" && (
<div className='record__item'>
{record.source &&
record.source.courseMediaVOS.map((item, index) => {
if (item.contentType === "COVER") {
hasCover = true
return <img className='course-cover' key={index} src={item.mediaUrl} />
}
})}
{!hasCover && (
<img
className="course-cover"
src={"https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png"}
/>
)}
<div>
{val.length > 17 ? (
<Tooltip title={val}>
<div className='course-name'>{val}</div>
</Tooltip>
) : (
<div className='course-name'>{val}</div>
)}
<div>
<span className='course-time'>
{formatDate("YYYY-MM-DD H:i", parseInt(record.source.startTime))}~{formatDate("H:i", parseInt(record.source.endTime))}
</span>
<span
className='course-status'
style={{
color: ENUM.courseStateShow[record.source.courseState].color,
border: `1px solid ${ENUM.courseStateShow[record.source.courseState].color}`
}}>
{ENUM.courseStateShow[record.source.courseState].title}
</span>
{record.hideToUser && (
<Tooltip title={<div>课程未成功开课,已在学员知识列表中隐藏</div>}>
<i
className='icon iconfont'
style={{
marginLeft: "5px",
cursor: "pointer",
color: "#FF4F4F",
fontSize: "14px"
}}>
&#xe61d;
</i>
</Tooltip>
)}
</div>
<div className='teacher-assistant'>
{record.source.teacherName.length > 4 ? (
<Tooltip title={record.source.teacherName}>
<span className='teacher'>讲师:{record.source.teacherName}</span>
</Tooltip>
) : (
<span className='teacher'>讲师:{record.source.teacherName}</span>
)}
{record.source.admins.length > 0 && (
<>
<span className='split'> | </span>
{this.handleAdminName(record.source.admins).length > 4 ? (
<Tooltip title={this.handleAdminName(record.source.admins)}>
<span className='assistant'>
助教:
{record.source.admins.map((item, index) => {
return (
<span>
{item.adminName} {index < record.source.admins.length - 1 && <span></span>}{" "}
</span>
)
})}
</span>
</Tooltip>
) : (
<span className='assistant'>
助教:
{record.source.admins.map((item, index) => {
return (
<span key={index}>
{item.adminName} {index < record.source.admins.length - 1 && <span></span>}{" "}
</span>
)
})}
</span>
)}
</>
)}
</div>
</div>
</div>
)}
{type === "VOICE" && (
<div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img
className='course-cover'
src={
coverUrl
? coverUrl
: courseDivision !== "EXTERNAL"
? `${mediaCourseUrl}?x-oss-process=video/snapshot,t_0,m_fast`
: "https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png"
}
alt=''
/>
{val.length > 25 ? (
<Tooltip title={val}>
<div className='course-name clamp'>{val}</div>
</Tooltip>
) : (
<div className='course-name clamp'>{val}</div>
)}
</div>
)}
{type === "PICTURE" && (
<div className="record__item">
<img
className="course-cover"
src={
coverUrl ||
"https://image.xiaomaiketang.com/xm/wFnpZtp2yB.png"
}
/>
{val.length > 25 ? (
<Tooltip title={val}>
<div className='course-name clamp'>{val}</div>
</Tooltip>
) : (
<div className='course-name clamp'>{val}</div>
)}
</div>
)}
{type === "FOLDER" && (
<div
className='record__item'
onClick={() => {
this.handleScanFile(record.source)
}}>
<div className={`folder-type ${record.source && record.source.folderFormat}`} />
<img src={FileTypeIcon[record.source.folderFormat] } style={{width:24,height:24}} alt='' className='item-img' />
{val.length > 25 ? (
<Tooltip title={val}>
<div className='course-name clamp'>{val}</div>
......@@ -344,22 +204,11 @@ class KnowledgeBaseList extends React.Component {
<div className='course-name clamp'>{val}</div>
)}
</div>
)}
</div>
)
}
},
{
title: "课程类型",
key: "type",
dataIndex: "type",
align: "center",
// width: 100,
render: (val, record) => {
return <div className=''>{val ? ENUM.CourseTypeEnum[val] : "-"}</div>
}
},
{
title: "创建人",
key: "createName",
dataIndex: "createName",
......@@ -393,7 +242,7 @@ class KnowledgeBaseList extends React.Component {
},
{
title: "",
width: 48
},
{
title: "操作",
......@@ -452,7 +301,6 @@ class KnowledgeBaseList extends React.Component {
const { match } = this.props;
localStorage.setItem("WatchData_CourseName", item.name);
window.RCHistory.push({
// pathname: `${match.url}/course-data?type=${item.courseType}&id=${item.liveCourseId}`,
pathname: `${match.url}/course-data?type=${item.type}&id=${item.id}`
})
}
......@@ -501,7 +349,7 @@ class KnowledgeBaseList extends React.Component {
dataSource={dataSource}
columns={this.parseColumns()}
pagination={false}
scroll={{ x: 900 }}
bordered
className="knowledge-list-table"
renderEmpty={{
......
......@@ -109,7 +109,10 @@
.knowledge-base-list {
.record__item {
display: flex;
// align-items: center;
align-items: center;
.item-img{
margin-right: 4px;
}
.course-cover {
min-width: 107px;
max-width: 90px;
......@@ -153,13 +156,7 @@
text-overflow: ellipsis;
white-space: nowrap;
height: 20px;
&.clamp {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
white-space: normal;
height: 40px;
}
}
.course-time {
font-size: 12px;
......
......@@ -9,10 +9,8 @@
import React, { useState, useEffect } from "react";
import { Button, Menu, Dropdown,message, Modal } from "antd";
import { Button, Menu, Dropdown, message, Modal } from "antd";
import SelectPrepareFileModal from "@/modules/prepare-lesson/modal/SelectPrepareFileModal";
import { DownOutlined } from "@ant-design/icons";
import AddCourse from "../modal/AddCourse";
import User from "@/common/js/user";
import Service from "@/common/js/service";
import KnowledgeAPI from "@/data-source/knowledge/request-api";
......@@ -31,16 +29,7 @@ export default function KnowledgeBaseOpt({
const [openMoveModal, setOpenMoveModal] = useState(false);
const [data, setData] = useState([]);
const menu = (
<Menu>
<Menu.Item key="1" style={{ textAlign: "center" }}>
<span onClick={handAddCourse}>添加课程</span>
</Menu.Item>
<Menu.Item key="2" style={{ textAlign: "center" }}>
<span onClick={handleAddFile}>添加资料</span>
</Menu.Item>
</Menu>
);
useEffect(() => {
queryCategoryTree();
......@@ -76,19 +65,6 @@ export default function KnowledgeBaseOpt({
return newTreeData;
};
function handAddCourse() {
let modal = (
<AddCourse
onClose={() => {
setModal(null);
}}
onChange={onChange}
categoryId={categoryId}
updateCategoryTree={updateCategoryTree}
></AddCourse>
);
setModal(modal);
}
function handUpload(refIds) {
const params = {
......@@ -118,7 +94,7 @@ export default function KnowledgeBaseOpt({
isOpen={true}
accept=".ppt,.pptx,.doc,.docx,.pdf,.jpg,.jpeg,.png,.xlsx,.xls"
tooltip="支持文件类型:ppt、word、excel、pdf、jpg、jpeg、png"
selectTypeList={["JPG", "JPEG", "PNG",'DOC','PDF','EXCEL','application/msword','application/vnd.ms-powerpoint']} // DOC 包含 .pptx,.docx,.xls.XLSX,WORD:DOC
selectTypeList={["JPG", "JPEG", "PNG", 'DOC', 'PDF', 'EXCEL', 'application/msword', 'application/vnd.ms-powerpoint']} // DOC 包含 .pptx,.docx,.xls.XLSX,WORD:DOC
onClose={() => {
setModal(null);
}}
......@@ -205,12 +181,9 @@ export default function KnowledgeBaseOpt({
return (
<div className="knowledge-course-opt">
{_.isEmpty(selectedRowKeys) ?
(categoryId !== '0' && <Dropdown overlay={menu}>
<Button type="primary" className="mr8">
添加知识
<DownOutlined />
categoryId !== '0' && <Button type="primary" onClick={handleAddFile} className="mr8">
添加资料
</Button>
</Dropdown>)
: <div className="select-container">
<span className="con">
<div>
......
......@@ -107,11 +107,11 @@ class AddCourse extends React.Component {
selectVideo: {
external: [],
internal: [],
}, //弹窗内已选择的视频课程
}, //弹窗内已选择的线上课程
currentVideoCourseListData: {
external: [],
internal: [],
}, //页面中已关联的视频课程
}, //页面中已关联的线上课程
pictureDataSource: [],
pictureSize: 10,
......@@ -201,7 +201,7 @@ class AddCourse extends React.Component {
});
};
// 获取视频课列表
// 获取线上课列表
handleFetchVideoList = () => {
const { videoQuery, videoSize, videoCourseDivision, videoDataSource, videoTotalCount } = this.state;
......@@ -866,7 +866,7 @@ class AddCourse extends React.Component {
)}
</div>
</TabPane>
<TabPane tab='视频课' key='VIDEO'>
<TabPane tab='线上课' key='VIDEO'>
<Radio.Group value={videoCourseDivision} onChange={this.videoCourseDivisionChange} style={{ marginBottom: 8 }}>
<Radio.Button value='internal'>内部课程</Radio.Button>
<Radio.Button value='external'>外部课程</Radio.Button>
......
......@@ -25,7 +25,7 @@ class VideoList extends React.Component {
constructor(props) {
super(props);
this.state = {
id: "", // 视频课ID
id: "", // 线上课ID
studentIds: [],
selectedRowKeys: [],
query: {
......@@ -33,8 +33,8 @@ class VideoList extends React.Component {
current: 1,
storeId: User.getStoreId(),
},
dataSource: [], // 视频课列表
totalCount: 0, // 视频课数据总条数
dataSource: [], // 线上课列表
totalCount: 0, // 线上课数据总条数
};
}
......@@ -50,11 +50,11 @@ class VideoList extends React.Component {
}
componentWillMount() {
// 获取视频课列表
// 获取线上课列表
this.handleFetchScheduleList();
}
// 获取视频课列表
// 获取线上课列表
handleFetchScheduleList = (_query = {}) => {
const query = {
...this.state.query,
......
/*
* @Author: yuananting
* @Date: 2021-07-05 10:48:08
* @LastEditors: yuananting
* @LastEditTime: 2021-07-15 13:58:17
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
/*
* @Author: zhangleyuan
* @Date: 2021-02-20 16:45:51
* @LastEditors: Please set LastEditors
......@@ -12,6 +21,7 @@ import { withRouter } from 'react-router-dom';
import SelectOperatorModal from '../modal/SelectOperatorModal';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import Upload from '@/core/upload';
import GraphicsEditor from '@/modules/course-manage/components/GraphicsEditor';
import ImgClipModal from '@/components/ImgClipModal'
import './BasicInfo.less';
......@@ -124,10 +134,14 @@ class BasicInfo extends React.Component {
this.props.onChange(field, _percentCompleteLive);
};
changeIntro = (value) => {
this.props.onChange('introduce', value);
};
render() {
const { operatorModalVisible, showSelectFileModal, visible, hasImgReady, cutImageBlob,imageFile} = this.state;
const { data } = this.props;
const { planName, coverUrl, instro, enableState, operateType, selectOperatorList, percentCompleteLive, percentCompleteVideo, percentCompletePicture } =
const { planName, coverUrl, introduce, enableState, operateType, selectOperatorList, percentCompleteLive, percentCompleteVideo, percentCompletePicture } =
data;
// 当前是否使用的是默认图片
const isDefaultCover = coverUrl === defaultCover;
......@@ -148,10 +162,6 @@ class BasicInfo extends React.Component {
<div className='cover'>
<span className='label'>封面图:</span>
<div className='cover__wrap'>
<div className='img-content'>
{isDefaultCover && <span className='tag'>默认图</span>}
<img src={coverUrl} width='690' alt='' />
</div>
<div className='opt-btns'>
<Button
onClick={() => {
......@@ -166,36 +176,44 @@ class BasicInfo extends React.Component {
</span>
<div className='tips'>建议尺寸1280*720px或16:9。封面图最大5M,支持jpg、jpeg和png。</div>
</div>
<div className='img-content'>
{isDefaultCover && <span className='tag'>默认图</span>}
<img src={coverUrl} width='690' alt='' />
</div>
</div>
</div>
<div className='introduction'>
<span className='label'>简介:</span>
<TextArea
{/* <TextArea
placeholder='请输入培训计划简介'
maxLength={200}
style={{ width: '552px', height: '110px' }}
className='instro-textarea'
value={instro}
onChange={(e) => this.props.onChange('instro', e.target.value)}
/> */}
<GraphicsEditor
id='intro'
isIntro={true}
maxLimit={1000}
detail={{
content: introduce,
}}
onChange={(val) => {
this.changeIntro(val);
}}
/>
</div>
<div className='wether-use'>
<span className='label'>是否启用:</span>
<div className='content'>
<div>
<Switch
checked={enableState === 'YES' ? true : false}
onChange={() => {
this.enableStateChange();
}}
/>
</div>
<div>
<div className='instro-text'>
<div>开启:此培训计划可以分享给学员进行学习</div>
<div>关闭:此培训计划暂不可分享给学员进行学习,后续可开启</div>
</div>
</div>
<div className='instro-text'>{enableState === 'YES' ? '已开启,培训计划可正常分享给学员学习' : '已关闭,培训计划暂不进行分享和学习'}</div>
</div>
</div>
<div className='view-range'>
......@@ -213,7 +231,7 @@ class BasicInfo extends React.Component {
onChange={(e) => {
this.props.onChange('operateType', e.target.value);
}}>
<Row style={{ marginBottom: '5px' }}>
<Row style={{ marginBottom: '16px' }}>
<Col span={24}>
<Radio value='All_Operate'>
所有运营师
......@@ -239,7 +257,7 @@ class BasicInfo extends React.Component {
选择运营师
</Button>
<span>
已选择<span>{selectOperatorList.length}</span>名运营师
已选择 <span style={{ color: '#2966FF' }}>{selectOperatorList.length}</span> 名运营师
</span>
</div>
)}
......@@ -269,7 +287,7 @@ class BasicInfo extends React.Component {
<div className='live-standard-info'>
<span className='icon iconfont'>&#xe864;</span>
<span className='instro'>
视频课单个课程,学员学习进度达到
线上课单个课节,学员学习进度达到
<Input
width='40'
value={percentCompleteVideo}
......@@ -279,7 +297,7 @@ class BasicInfo extends React.Component {
onBlur={(e) => this.percentCompleteBlur(e, 'percentCompleteVideo')}
className='input-box'
/>
%,即视为"已完成"学习
%,即课节视为"已完成"学习
</span>
</div>
<div className='live-standard-info'>
......
.plan-basic-info{
.plan-basic-info {
.label {
width: 110px;
text-align: right;
display:inline-block;
font-size:14px;
color:#666;
display: inline-block;
font-size: 14px;
color: #666;
.require {
color: #EC4B35;
color: #ec4b35;
}
.iconfont{
font-size:14px;
color:#BFBFBF;
.iconfont {
font-size: 14px;
color: #bfbfbf;
}
}
.cover {
display: flex;
margin-top: 16px;
margin-top: 24px;
&__wrap {
position: relative;
display: flex;
.tag {
border-radius: 2px;
background: #D6D6D6;
background: #d6d6d6;
font-size: 12px;
height: 18px;
width: 52px;
text-align: center;
color: #FFF;
color: #fff;
position: absolute;
top: 8px;
left: 8px;
}
}
.img-content {
margin-right: 20px;
position: relative;
margin-top: 8px;
width: 299px;
height: 169px;
img {
......@@ -42,90 +42,93 @@
object-fit: contain;
}
}
.opt-btns{
.opt-btns {
.default-btn {
margin:0 8px;
color: #2966FF;
margin-left: 14px;
color: #2966ff;
cursor: pointer;
&.disabled {
color: #CCC;
color: #ccc;
cursor: not-allowed;
}
}
.tips{
margin-top:8px;
font-size:14px;
color:#999;
.tips {
margin-top: 8px;
font-size: 14px;
color: #999;
}
}
}
.introduction{
margin-top:16px;
.instro-textarea{
.introduction {
display: flex;
margin-top: 24px;
.instro-textarea {
vertical-align: top;
}
}
.wether-use{
display:flex;
margin-top:16px;
.instro-text{
color:#999;
margin-left:12px;
.wether-use {
display: flex;
margin-top: 34px;
.instro-text {
color: #999;
margin-left: 12px;
}
.content{
display:flex;
.content {
display: flex;
}
}
.view-range{
display:flex;
margin-top:16px;
.label{
margin-top:2px;
.view-range {
display: flex;
margin-top: 24px;
.label {
margin-top: 2px;
}
.instro-text{
color:#999;
margin-left:12px;
.instro-text {
color: #999;
margin-left: 12px;
}
.choose-business{
margin-top:16px;
.choose-business {
margin-top: 12px;
.ant-btn {
margin-right: 12px;
}
.playback__text{
margin-left:12px;
color:#999999;
}
.playback__text {
margin-left: 12px;
color: #999999;
}
.done-standard{
}
.done-standard {
display: flex;
margin-top:22px;
.standard-label{
margin-top:3px;
margin-top: 22px;
.standard-label {
margin-top: 3px;
}
.live-standard-info{
margin-bottom:10px;
.live-standard-info {
margin-bottom: 10px;
}
input{
display:inline-block;
width:90px;
height:32px;
input {
display: inline-block;
width: 90px;
height: 32px;
}
.icon{
color:#A0A0A0;
font-size:14px;
margin-right:4px;
.icon {
color: #a0a0a0;
font-size: 14px;
margin-right: 4px;
}
.instro{
color:#333333;
font-size:14px;
.instro {
color: #333333;
font-size: 14px;
}
.input-box{
.input-box {
width: 60px;
height: 32px;
border-radius: 4px;
border: 1px solid #E8E8E8;
color:#333333;
font-size:14px;
margin:0 2px;
border: 1px solid #e8e8e8;
color: #333333;
font-size: 14px;
margin: 0 2px;
}
}
}
......@@ -18,7 +18,7 @@ function ExpiredCourseList(props) {
<span>直播课</span>
}
{ item.courseType === "VOICE " &&
<span>视频</span>
<span>线上</span>
}
</div>
<div className="course-instro">
......
.user-Learn-modal{
.customer-info{
margin-bottom:16px;
.customer-name{
font-size:14px;
color:#333;
margin-right:32px;
.user-Learn-modal {
.customer-info {
margin-bottom: 16px;
.customer-name {
font-size: 14px;
color: #333;
margin-right: 32px;
}
.customer-phone{
font-size:14px;
color:#333;
.customer-phone {
font-size: 14px;
color: #333;
}
}
.plan-instro{
.plan-instro {
display: flex;
align-items: center;
margin-bottom:16px;
.img-con{
margin-right:8px;
img{
margin-bottom: 16px;
.img-con {
margin-right: 8px;
img {
width: 97px;
height: 54px;
display: inline-block;
border-radius:4px;
}
}
.plan-name{
color:#333;
font-size:16px;
}
.plan-learn-percentage{
color:#333;
font-size:14px;
}
}
.task-table{
.ant-table-thead{
tr {
th{
padding:9px 16px;
}
}
}
tr{
td{
padding:14px 16px;
}
}
.taskName{
color:#333;
font-size:14px;
}
.task-learn-percentage{
color:#333;
font-size:14px;
}
.course-info{
display:flex;
margin-left:57px;
align-items: center;
.course-type{
font-size:11px;
color:#666666;
padding:0px 6px;
border: 1px solid #999999;
margin-right:4px;
border-radius: 2px;
line-height: 16px;
}
.name-and-state{
flex:1;
.course-name{
color:#666666;
font-size:14px;
margin-right:8px;
}
.tip{
font-size:14px;
color:#FF4F4F;
margin-right:2px;
}
.course-state{
color:#999;
font-size:14px;
}
}
}
.ant-table-expanded-row{
td{
padding:0 16px;
}
border-radius: 4px;
}
.ant-table-content{
border:1px solid #e8e8e8;
tr{
td{
border:none;
}
.child-table{
.ant-table-content{
border:none;
thead{
display:none;
}
tbody tr {
td{
border-bottom:none;
padding:14px 16px;
.plan-name {
color: #333;
font-size: 16px;
}
.plan-learn-percentage {
color: #333;
font-size: 14px;
}
}
.table-box {
.child-table {
margin-left: 40px;
tr > td {
&:first-child {
padding-right: 0 !important;
}
}
.odd-row{
background:transparent;
td{
background: #FFF;
}
& + .ant-table-expanded-row{
background:transparent;
td{
background: #FFF;
}
}
&:hover{
& + .ant-table-expanded-row{
background:transparent;
td{
background: #F3f6fa !important;
}
&:nth-child(2) {
padding-left: 0 !important;
}
}
.course-info {
display: flex;
.course-type {
border-radius: 2px;
border: 1px solid #999999;
font-size: 11px;
color: #666666;
padding: 1px 8px;
margin-right: 6px;
}
.even-row{
background:transparent;
td{
background: #FAFAFA;
}
& + .ant-table-expanded-row{
background:transparent;
td{
background: #FAFAFA;
}
}
&:hover{
& + .ant-table-expanded-row{
background:transparent;
td{
background: #F3f6fa !important;
.chapter-record {
display: flex;
align-items: center;
.chapter-img {
width: 18px;
height: 18px;
margin: 0 10px;
}
}
.record-name {
display: flex;
align-items: center;
.img-con {
margin-right: 8px;
img {
width: 97px;
height: 54px;
border-radius: 4px;
}
}
}
......
/*
* @Author: yuananting
* @Date: 2021-07-05 10:50:10
* @LastEditors: yuananting
* @LastEditTime: 2021-07-13 19:55:29
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import _ from 'underscore';
import { Table, Radio, Tabs, Modal, Input, message, Button, Tooltip } from 'antd';
import { PageControl, XMTable } from '@/components';
import college from '@/common/lottie/college';
import { PageControl } from '@/components';
import CourseService from '@/domains/course-domain/CourseService';
import User from '@/common/js/user';
......@@ -79,11 +87,11 @@ class SelectOperatorModal extends React.Component {
selectVideo: {
external: [],
internal: [],
}, //弹窗内已选择的视频课程
}, //弹窗内已选择的线上课程
currentVideoCourseListData: {
external: [],
internal: [],
}, //页面中已关联的视频课程
}, //页面中已关联的线上课程
pictureDataSource: [],
pictureSize: 10,
......@@ -91,10 +99,10 @@ class SelectOperatorModal extends React.Component {
current: 1,
},
pictureTotalCount: 0,
selectPicture: [], //弹窗内已选择的视频课程
currentPictureCourseListData: [], //页面中已关联的视频课程
selectPicture: [], //弹窗内已选择的线上课程
currentPictureCourseListData: [], //页面中已关联的线上课程
activeKey: 'video',
activeKey: 'live',
currentTaskCourseData: this.props.data[this.props.selectedTaskIndex].courseList || [],
};
}
......@@ -136,7 +144,7 @@ class SelectOperatorModal extends React.Component {
});
};
// 获取视频课列表
// 获取线上课列表
handleFetchVideoDataList = () => {
const { videoQuery, videoSize, videoDataSource, videoTotalCount, videoCourseDivision } = this.state;
......@@ -370,7 +378,6 @@ class SelectOperatorModal extends React.Component {
// 请求表头
parseVideoColumns = () => {
const { videoCourseDivision } = this.state;
const columns = [
{
title: (
......@@ -387,33 +394,23 @@ class SelectOperatorModal extends React.Component {
dataIndex: 'course',
width: '60%',
render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record;
const { coverUrl } = record;
return (
<div className='course-info'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img
className='course-cover'
src={
coverUrl ||
(videoCourseDivision === 'internal'
? `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`
: 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png')
}
alt=''
/>
<img className='course-cover' src={coverUrl || 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png'} alt='' />
<div className='course-name'>{record.courseName}</div>
</div>
);
},
},
{
title: '课程时长',
key: 'courseTime',
dataIndex: 'courseTime',
title: '课节数',
key: 'courseChapterNum',
dataIndex: 'courseChapterNum',
width: '20%',
align: 'right',
render: (val, record) => {
return <span className='course-status'>{dealTimeDuration(record.videoDuration)}</span>;
return <span>{val || 1}</span>;
},
},
{
......@@ -555,6 +552,7 @@ class SelectOperatorModal extends React.Component {
_item.courseId = item.id;
_item.courseType = 'VOICE';
_item.courseName = item.courseName;
_item.courseChapterNum = item.courseChapterNum
return _item;
});
......@@ -616,7 +614,7 @@ class SelectOperatorModal extends React.Component {
className='link-create-course'
href={window.location.origin + window.location.pathname + '#/create-video-course?type=add'}
onClick={this.props.onClose}>
没有找到需要的视频课?<span>去创建</span>
没有找到需要的线上课?<span>去创建</span>
</a>
);
break;
......@@ -734,11 +732,7 @@ class SelectOperatorModal extends React.Component {
</div>
</div>
<div>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
<Table
rowKey={(record) => record.liveCourseId}
dataSource={liveDataSource}
columns={this.parseLiveColumns()}
......@@ -792,7 +786,7 @@ class SelectOperatorModal extends React.Component {
</div>
</TabPane>
<TabPane tab='视频课' key='video'>
<TabPane tab='线上课' key='video'>
<Radio.Group value={videoCourseDivision} onChange={this.videoCourseDivisionChange} style={{ marginBottom: 16 }}>
<Radio.Button value='internal'>内部课程</Radio.Button>
<Radio.Button value='external'>外部课程</Radio.Button>
......@@ -830,11 +824,7 @@ class SelectOperatorModal extends React.Component {
</div>
</div>
<div>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
<Table
rowKey={(record) => record.id}
dataSource={videoDataSource[videoCourseDivision]}
columns={this.parseVideoColumns()}
......@@ -939,11 +929,7 @@ class SelectOperatorModal extends React.Component {
</div>
</div>
<div>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
<Table
rowKey={(record) => record.id}
dataSource={pictureDataSource}
columns={this.parsePictureColumns()}
......
......@@ -221,6 +221,7 @@ class SelectPrepareFileModal extends React.Component {
maxSelectLength
} = this.props;
console.log('selectedFileList',selectedFileList)
if (multiple) {
const currSelectedFileList = [...(prevSelectedFileList || []), ...selectedFileList];
// 最多选择20个文件,判断是否超出了20个
......
......@@ -37,7 +37,7 @@ class ScanFileModal extends React.Component {
{fileType === "MP4" && (
<div>
<Player
src={item.ossAddress || item.ossUrl}
src={item.mediaUrl || item.ossAddress || item.ossUrl}
fluid={false}
height={306}
width={"100%"}
......
......@@ -10,4 +10,7 @@
height: 60px;
}
}
.video-react .video-react-play-progress:after {
width: max-content;
}
}
......@@ -81,7 +81,7 @@ const mainRoutes = [
{
path: '/video-course',
component: VideoCoursePage,
name: '视频课',
name: '线上课',
},
{
path: '/graphics-course',
......@@ -101,7 +101,7 @@ const mainRoutes = [
{
path: '/create-video-course',
component: AddVideoCoursePage,
name: '创建视频课',
name: '创建线上课',
},
{
path: '/knowledge-base',
......@@ -163,7 +163,7 @@ const mainRoutes = [
{
path: '/create-plan',
component: AddPlanPage,
name: '创建视频课',
name: '创建线上课',
},
{
path: '/store-info',
......
/*
* @Author: yuananting
* @Date: 2021-02-21 15:53:31
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-08 16:46:15
* @LastEditors: wufan
* @LastEditTime: 2021-07-05 10:29:23
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -28,8 +28,8 @@ export const menuList: any = [
link: '/live-course',
},
{
groupName: '视频课',
groupCode: 'CourseVideoClass',
groupName: "线上课",
groupCode: "CourseVideoClass",
link: '/video-course',
},
{
......
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