Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
X
xiaomai-cloud-class-web
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
xiaomai-cloud-class
xiaomai-cloud-class-web
Commits
aaad704c
Commit
aaad704c
authored
Mar 11, 2021
by
chenshu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat:初始化页面
parent
1a26589e
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
2312 additions
and
1 deletions
+2312
-1
src/modules/course-manage/graphics-course/AddGraphicsCourse.jsx
+552
-0
src/modules/course-manage/graphics-course/AddGraphicsCourse.less
+147
-0
src/modules/course-manage/graphics-course/components/AddVideoIntro.jsx
+285
-0
src/modules/course-manage/graphics-course/components/AddVideoIntro.less
+274
-0
src/modules/course-manage/graphics-course/components/GraphicsCourseFilter.jsx
+235
-0
src/modules/course-manage/graphics-course/components/GraphicsCourseFilter.less
+56
-0
src/modules/course-manage/graphics-course/components/GraphicsCourseList.jsx
+363
-0
src/modules/course-manage/graphics-course/components/GraphicsCourseList.less
+89
-0
src/modules/course-manage/graphics-course/components/GraphicsCourseOpt.less
+7
-0
src/modules/course-manage/graphics-course/components/VieoCourseOpt.jsx
+28
-0
src/modules/course-manage/graphics-course/index.jsx
+77
-0
src/modules/course-manage/graphics-course/modal/WatchDataModal.jsx
+170
-0
src/modules/course-manage/graphics-course/modal/WatchDataModal.less
+7
-0
src/modules/home/Home.jsx
+4
-0
src/routes/config/mainRoutes.tsx
+12
-0
src/routes/config/menuList.tsx
+6
-1
No files found.
src/modules/course-manage/graphics-course/AddGraphicsCourse.jsx
0 → 100644
View file @
aaad704c
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:07:47
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-04 10:26:07
* @Description: 图文课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import
React
from
'react'
;
import
{
Button
,
Input
,
Radio
,
message
,
Modal
,
Cascader
}
from
'antd'
;
import
{
DISK_MAP
,
FileTypeIcon
,
FileVerifyMap
}
from
'@/common/constants/academic/lessonEnum'
;
import
{
ImgCutModalNew
}
from
'@/components'
;
import
ShowTips
from
"@/components/ShowTips"
;
import
Breadcrumbs
from
"@/components/Breadcrumbs"
;
import
AddVideoIntro
from
'./components/AddVideoIntro'
;
import
SelectStudent
from
'../modal/select-student'
;
import
SelectPrepareFileModal
from
'../../prepare-lesson/modal/SelectPrepareFileModal'
;
import
PreviewCourseModal
from
'../modal/PreviewCourseModal'
;
import
StoreService
from
"@/domains/store-domain/storeService"
;
import
CourseService
from
"@/domains/course-domain/CourseService"
;
import
User
from
'@/common/js/user'
;
import
_
from
"underscore"
;
import
'./AddGraphicsCourse.less'
;
const
EDIT_BOX_KEY
=
Math
.
random
();
const
fieldNames
=
{
label
:
'categoryName'
,
value
:
'id'
,
children
:
'sonCategoryList'
};
//添加课程时课程默认的一些值
const
defaultShelfState
=
'YES'
;
const
defaultScheduleMedia
=
[{
contentType
:
'INTRO'
,
mediaType
:
'TEXT'
,
mediaContent
:
''
,
key
:
EDIT_BOX_KEY
}]
const
whetherVisitorsJoin
=
'NO'
class
AddGraphicsCourse
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
const
id
=
getParameterByName
(
"id"
);
const
pageType
=
getParameterByName
(
"type"
);
this
.
state
=
{
id
,
// 图文课ID,编辑的时候从URL上带过来
pageType
,
// 页面类型: add->新建 edit->编辑
imageFile
:
null
,
// 需要被截取的图片
courseName
:
null
,
// 图文课名称
scheduleVideoId
:
null
,
// 图文课链接
coverId
:
null
,
// 图文封面的recourceId
coverUrl
:
'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'
,
// 图文课封面
studentList
:
[],
// 上课学员列表
shelfState
:
'YES'
,
//是否开启店铺展示
scheduleMedia
:
[{
// 图文课媒体资源
contentType
:
"INTRO"
,
mediaType
:
'TEXT'
,
mediaContent
:
''
,
key
:
EDIT_BOX_KEY
}],
diskList
:
[],
// 机构可见磁盘目录
selectedFileList
:
[],
// 已经从资料云盘中勾选的文件
showCutModal
:
false
,
// 是否显示截图弹窗
showSelectFileModal
:
false
,
studentModal
:
false
,
categoryName
:
null
,
//分类名称
courseCatalogList
:[],
//分类列表
categoryId
:
null
,
//分类的Id值
whetherVisitorsJoin
:
'NO'
// 是否允许游客加入
}
}
componentWillMount
()
{
// this.handleFetchDiskList();
const
{
id
,
pageType
}
=
this
.
state
;
this
.
getCourseCatalogList
();
if
(
pageType
===
'edit'
)
{
this
.
handleFetchScheudleDetail
(
id
);
// this.handleFetchStudentList(id);
}
}
//获取分类列表
getCourseCatalogList
=
()
=>
{
StoreService
.
getCourseCatalogList
({
current
:
1
,
size
:
1000
}).
then
((
res
)
=>
{
this
.
setState
({
courseCatalogList
:
res
.
result
.
records
})
});
}
catalogChange
=
(
value
)
=>
{
const
changeValueLength
=
value
.
length
;
switch
(
changeValueLength
){
case
1
:
this
.
setState
({
categoryId
:
value
[
0
]});
break
;
case
2
:
this
.
setState
({
categoryId
:
value
[
1
]});
break
;
default
:
this
.
setState
({
categoryId
:
null
});
break
;
}
}
// 获取图文课详情
handleFetchScheudleDetail
=
(
courseId
)
=>
{
CourseService
.
videoScheduleDetail
({
courseId
}).
then
((
res
)
=>
{
const
{
result
=
{}
}
=
res
||
{};
const
{
// coverId,
// coverUrl,
// videoType,
// videoDuration,
// videoName,
// scheduleMedia,
courseName
,
// scheduleVideoId,
// scheduleVideoUrl,
shelfState
,
whetherVisitorsJoin
,
courseMediaVOS
,
categoryOneName
,
categoryTwoName
,
categoryId
}
=
result
;
let
coverId
;
let
coverUrl
;
let
videoType
;
let
videoDuration
;
let
videoName
;
let
scheduleMedia
=
[];
let
scheduleVideoId
;
let
scheduleVideoUrl
;
courseMediaVOS
.
map
((
item
)
=>
{
switch
(
item
.
contentType
){
case
"COVER"
:
coverId
=
item
.
mediaContent
;
coverUrl
=
item
.
mediaUrl
;
break
;
case
"SCHEDULE"
:
videoDuration
=
item
.
videoDuration
;
videoName
=
item
.
mediaName
;
scheduleVideoId
=
item
.
mediaContent
;
scheduleVideoUrl
=
item
.
mediaUrl
;
videoType
=
item
.
mediaType
;
break
;
case
"INTRO"
:
scheduleMedia
=
[...
scheduleMedia
,
item
]
break
;
default
:
break
;
}
return
item
;
})
let
categoryName
;
if
(
categoryTwoName
){
categoryName
=
`
${
categoryOneName
}
-
${
categoryTwoName
}
`
;
}
else
{
categoryName
=
`
${
categoryOneName
}
`
;
}
this
.
setState
({
coverId
,
coverUrl
,
videoType
,
videoName
,
videoDuration
,
scheduleMedia
,
courseName
,
scheduleVideoId
,
scheduleVideoUrl
,
shelfState
,
whetherVisitorsJoin
,
categoryName
,
categoryId
});
})
}
handleGoBack
=
()
=>
{
const
{
coverId
,
videoName
,
videoDuration
,
courseName
,
scheduleMedia
,
scheduleVideoId
,
categoryId
,
shelfState
,
whetherVisitorsJoin
}
=
this
.
state
;
if
(
videoName
||
videoDuration
||
scheduleVideoId
||
!
_
.
isEqual
(
scheduleMedia
,
defaultScheduleMedia
)
||
categoryId
||
courseName
||
coverId
||
shelfState
!==
defaultShelfState
||
whetherVisitorsJoin
!==
whetherVisitorsJoin
){
Modal
.
confirm
({
title
:
'确认要返回吗?'
,
content
:
'返回后,本次编辑的内容将不被保存。'
,
okText
:
'确认返回'
,
cancelText
:
'留在本页'
,
icon
:
<
span
className=
"icon iconfont default-confirm-icon"
>

</
span
>,
onOk
:
()
=>
{
RCHistory
.
goBack
();
}
});
}
else
{
RCHistory
.
goBack
();
}
}
// 修改表单
handleChangeForm
=
(
field
,
value
,
coverUrl
)
=>
{
console
.
log
(
'field'
,
value
);
this
.
setState
({
[
field
]:
value
,
coverUrl
:
coverUrl
?
coverUrl
:
this
.
state
.
coverUrl
});
}
// 显示选择学员弹窗
handleShowSelectStuModal
=
()
=>
{
this
.
setState
({
studentModal
:
true
});
const
{
studentList
,
selectedStuList
}
=
this
.
state
;
// const _studentList = _.map(studentList, (item) => {
// return item.studentId
// })
const
studentModal
=
(
<
SelectStudent
showTabs=
{
true
}
type=
"videoCourse"
onSelect=
{
this
.
handleSelectStudent
}
after=
{
true
}
//表明是不是上课后的状态
studentList=
{
studentList
}
close=
{
()
=>
{
this
.
setState
({
studentModal
:
null
,
});
}
}
/>
)
this
.
setState
({
studentModal
});
}
handleSelectStudent
=
(
studentIds
)
=>
{
let
studentList
=
[];
_
.
each
(
studentIds
,
(
item
)
=>
{
studentList
.
push
({
studentId
:
item
});
});
// this.setState({ studentModal: null });
this
.
setState
({
studentList
});
this
.
setState
({
studentModal
:
false
});
}
// 显示预览弹窗
handleShowPreviewModal
=
()
=>
{
const
{
coverUrl
,
scheduleVideoUrl
,
courseName
,
scheduleMedia
,
videoDuration
}
=
this
.
state
;
const
courseBasinInfo
=
{
coverUrl
,
scheduleVideoUrl
,
courseName
,
videoDuration
}
const
courseIntroInfo
=
{
liveCourseMediaRequests
:
scheduleMedia
}
const
previewCourseModal
=
(
<
PreviewCourseModal
type=
"videoCourse"
courseBasicInfo=
{
courseBasinInfo
}
courseIntroInfo=
{
courseIntroInfo
}
close=
{
()
=>
{
this
.
setState
({
previewCourseModal
:
null
})
}
}
/>
);
this
.
setState
({
previewCourseModal
});
}
// 选择图文
handleSelectVideo
=
(
file
)
=>
{
this
.
setState
({
showSelectFileModal
:
false
})
const
{
ossUrl
,
resourceId
,
folderName
,
folderFormat
,
folderSize
}
=
file
;
const
videoDom
=
document
.
createElement
(
'video'
);
videoDom
.
src
=
ossUrl
;
videoDom
.
onloadedmetadata
=
()
=>
{
this
.
setState
({
size
:
folderSize
,
videoName
:
folderName
,
videoType
:
folderFormat
,
scheduleVideoUrl
:
ossUrl
,
scheduleVideoId
:
resourceId
,
videoDuration
:
videoDom
.
duration
});
}
}
// 上传封面图
handleShowImgCutModal
=
(
event
)
=>
{
const
imageFile
=
event
.
target
.
files
[
0
];
if
(
!
imageFile
)
return
;
this
.
setState
({
imageFile
,
showCutModal
:
true
,
});
}
// 保存
handleSubmit
=
()
=>
{
const
{
instId
,
adminId
}
=
window
.
currentUserInstInfo
;
const
{
id
,
size
,
coverId
,
coverUrl
,
pageType
,
joinType
,
videoName
,
videoDuration
,
studentList
,
courseName
,
scheduleMedia
,
scheduleVideoId
,
scheduleVideoUrl
,
categoryId
,
shelfState
,
whetherVisitorsJoin
}
=
this
.
state
;
const
commonParams
=
{
videoName
,
videoDuration
,
scheduleVideoId
,
scheduleMedia
:
scheduleMedia
.
filter
(
item
=>
!!
item
.
mediaContent
),
categoryId
,
courseName
,
coverId
,
operatorId
:
User
.
getStoreUserId
(),
storeId
:
User
.
getStoreId
(),
shelfState
,
whetherVisitorsJoin
};
// 校验必填字段:课程名称, 课程图文
this
.
handleValidate
(
courseName
,
scheduleVideoId
,
categoryId
,
scheduleMedia
).
then
((
res
)
=>
{
if
(
!
res
)
return
;
if
(
pageType
===
'add'
)
{
CourseService
.
createVideoSchedule
(
commonParams
).
then
((
res
)
=>
{
if
(
!
res
)
return
;
message
.
success
(
"新建成功"
);
window
.
RCHistory
.
goBack
();
})
}
else
{
const
editParams
=
{
courseId
:
id
,
...
commonParams
,
}
CourseService
.
editVideoSchedule
(
editParams
).
then
((
res
)
=>
{
if
(
!
res
)
return
;
message
.
success
(
"保存成功"
);
window
.
RCHistory
.
goBack
();
});
}
});
}
handleValidate
=
(
courseName
,
scheduleVideoId
,
categoryId
,
scheduleMedia
)
=>
{
return
new
Promise
((
resolve
)
=>
{
if
(
!
courseName
)
{
message
.
warning
(
'请输入课程名称'
);
resolve
(
false
);
return
false
}
if
(
!
scheduleVideoId
)
{
message
.
warning
(
'请上传图文'
);
resolve
(
false
);
return
false
}
if
(
!
categoryId
){
message
.
warning
(
'请选择课程分类'
);
resolve
(
false
);
return
false
}
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
)
{
message
.
warning
(
`第
${
i
+
1
}
个文字简介的字数超过了1000个字`
);
resolve
(
false
);
return
false
}
}
resolve
(
true
);
});
}
render
()
{
const
{
pageType
,
courseName
,
scheduleVideoId
,
coverId
,
coverUrl
,
scheduleVideoUrl
,
studentList
,
scheduleMedia
,
showCutModal
,
showSelectFileModal
,
diskList
,
imageFile
,
joinType
,
videoName
,
videoType
,
shelfState
,
categoryName
,
courseCatalogList
,
whetherVisitorsJoin
}
=
this
.
state
;
// 已选择的上课学员数量
const
hasSelectedStu
=
studentList
.
length
;
const
courseWareIcon
=
FileVerifyMap
[
videoType
]
?
FileTypeIcon
[
FileVerifyMap
[
videoType
].
type
]
:
FileTypeIcon
[
videoType
];
return
(
<
div
className=
"page add-video-course-page"
>
<
Breadcrumbs
navList=
{
pageType
===
"add"
?
"新建图文课"
:
"编辑图文课"
}
goBack=
{
this
.
handleGoBack
}
/>
<
div
className=
"box"
>
<
div
className=
"show-tips"
>
<
ShowTips
message=
"请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企培保有依据国家规定及平台规则进行处理的权利"
/>
</
div
>
<
div
className=
"form"
>
<
div
className=
"course-name required"
>
<
span
className=
"label"
>
课程名称:
</
span
>
<
Input
value=
{
courseName
}
placeholder=
"请输入图文课的名称(40字以内)"
maxLength=
{
40
}
style=
{
{
width
:
240
}
}
onChange=
{
(
e
)
=>
{
this
.
handleChangeForm
(
'courseName'
,
e
.
target
.
value
)}
}
/>
</
div
>
<
div
className=
"cover-url flex mt16"
>
<
div
className=
"label"
>
封面图:
</
div
>
<
div
className=
"cover-url__wrap"
>
<
div
className=
"img-content"
>
<
img
src=
{
coverUrl
}
/>
</
div
>
<
div
className=
"opt-btns"
>
<
input
type=
"file"
accept=
"image/png, image/jpeg, image/jpg"
ref=
"picInputFile"
style=
{
{
display
:
'none'
}
}
onChange=
{
(
event
)
=>
{
this
.
handleShowImgCutModal
(
event
)
}
}
/>
<
Button
onClick=
{
()
=>
{
this
.
setState
({
currentInputFile
:
this
.
refs
.
picInputFile
});
this
.
refs
.
picInputFile
.
click
()
}
}
>
{
`${(pageType === 'add' && (!scheduleVideoId && !coverUrl)) ? '上传' : '修改'}封面`
}
</
Button
>
<
div
className=
"tips"
>
建议尺寸1280*720px或16:9。封面图最大5M,支持jpg、jpeg和png。
</
div
>
</
div
>
</
div
>
</
div
>
<
div
className=
"course-catalog required"
>
<
span
className=
"label"
>
课程分类:
</
span
>
{
(
pageType
===
'add'
)
&&
<
Cascader
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'
}
}
>

</
span
>
}
/>
}
{
(
pageType
===
'edit'
&&
categoryName
)
&&
<
Cascader
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'
}
}
>

</
span
>
}
/>
}
</
div
>
<
div
className=
"intro-info mt16"
>
<
AddVideoIntro
data=
{
{
liveCourseMediaRequests
:
scheduleMedia
,
shelfState
,
whetherVisitorsJoin
,
label
:
'图文课简介'
}
}
onChange=
{
this
.
handleChangeForm
}
/>
</
div
>
</
div
>
</
div
>
<
div
className=
"footer"
>
<
Button
onClick=
{
this
.
handleGoBack
}
>
取消
</
Button
>
<
Button
onClick=
{
this
.
handleShowPreviewModal
}
>
预览
</
Button
>
<
Button
type=
"primary"
onClick=
{
_
.
debounce
(()
=>
this
.
handleSubmit
(),
3000
,
true
)
}
>
保存
</
Button
>
</
div
>
{
/* 选择备课文件弹窗 */
}
{
showSelectFileModal
&&
<
SelectPrepareFileModal
operateType=
"select"
selectTypeList=
{
[
'MP4'
]
}
accept=
"video/mp4"
confirm=
{
{
title
:
'文件过大,无法上传'
,
content
:
'为保障学员的观看体验,上传的图文大小不能超过2G'
,
}
}
tooltip=
{
'格式支持mp4,大小不超过2G'
}
isOpen=
{
showSelectFileModal
}
diskList=
{
diskList
}
addVideo=
{
true
}
onClose=
{
()
=>
{
this
.
setState
({
showSelectFileModal
:
false
})
}
}
onSelect=
{
this
.
handleSelectVideo
}
/>
}
<
ImgCutModalNew
title=
"裁剪"
width=
{
550
}
cutWidth=
{
500
}
cutHeight=
{
282
}
cutContentWidth=
{
500
}
cutContentHeight=
{
300
}
visible=
{
showCutModal
}
imageFile=
{
imageFile
}
bizCode=
'LIVE_COURSE_MEDIA'
onOk=
{
(
urlStr
,
resourceId
)
=>
{
this
.
setState
({
showCutModal
:
false
});
this
.
handleChangeForm
(
'coverId'
,
resourceId
,
urlStr
)
this
.
state
.
currentInputFile
.
value
=
''
;
}
}
onClose=
{
()
=>
this
.
setState
({
showCutModal
:
false
})
}
reUpload=
{
()
=>
{
this
.
state
.
currentInputFile
.
click
()
}
}
/>
{
this
.
state
.
previewCourseModal
}
</
div
>
)
}
}
export
default
AddGraphicsCourse
;
src/modules/course-manage/graphics-course/AddGraphicsCourse.less
0 → 100644
View file @
aaad704c
.add-video-course-page {
position:relative !important;
.box{
margin-bottom:66px !important;
}
.ant-radio-group {
display: flex;
flex-direction: column;
.radio-item {
margin-bottom: 12px;
.text {
color: #333;
}
.sub-text {
color: #999;
}
}
.ant-radio {
vertical-align: top;
padding-top: 2px;
}
}
.form {
margin-top: 16px;
padding: 0 16px;
.label{
display:inline-block;
text-align:right;
width:85px;
}
.required {
position: relative;
&::before {
position: absolute;
content: '*';
color: red;
left: 5px;
top: 6px;
}
&.label::before {
top: 0;
}
}
.course-catalog{
margin-bottom:16px;
margin-top:16px;
}
.course-ware {
display: flex;
align-items: center;
margin-bottom: 4px;
&__img {
width: 24px;
margin-right: 4px;
}
&__name {
color: #333;
}
}
.flex {
display: flex;
}
.cover-url__wrap {
.img-content {
width: 298px;
height: 172px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.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;
margin-left: 24px;
margin-top: 8px;
.has-selected {
margin-left: 12px;
color: #333;
}
}
.sub-content {
margin-left: 85px;
margin-top: 4px;
.tips {
margin-left: 4px;
color: #999;
}
}
}
.footer {
position: fixed;
bottom: 0;
height: 58px;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 252px;
background: #fff;
border-top: 1px solid #E8E8E8;
z-index: 999;
.ant-btn {
margin-left: 10px;
}
}
}
\ No newline at end of file
src/modules/course-manage/graphics-course/components/AddVideoIntro.jsx
0 → 100644
View file @
aaad704c
/*
* @Author: 吴文洁
* @Date: 2020-07-16 11:05:17
* @Last Modified by: mikey.zhaopeng
* @Last Modified time: 2020-11-24 14:29:52
* @Description: 添加直播-简介
*/
import
React
from
'react'
;
import
{
Input
,
message
,
Upload
,
Radio
,
Row
,
Col
,
Button
,
Popover
,
Switch
}
from
'antd'
;
import
Service
from
'@/common/js/service'
;
import
EditorBox
from
'../../components/EditorBox'
;
import
User
from
'@/common/js/user'
;
import
UploadOss
from
'@/core/upload'
;
import
'./AddVideoIntro.less'
;
import
SelectPrepareFileModal
from
'@/modules/prepare-lesson/modal/SelectPrepareFileModal'
;
import
{
DISK_MAP
}
from
'@/common/constants/academic/lessonEnum'
;
import
{
ImgCutModalNew
}
from
'@/components'
;
const
{
TextArea
}
=
Input
;
const
defaultCover
=
'https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1599635741526.png'
;
class
AddVideoIntro
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
warmUrl
:
defaultCover
,
showSelectFileModal
:
false
,
diskList
:
[],
selectType
:
null
}
}
// 上传封面图
handleShowImgCutModal
=
(
event
)
=>
{
const
imageFile
=
event
.
target
.
files
[
0
];
if
(
!
imageFile
)
return
;
this
.
setState
({
imageFile
,
showCutModal
:
true
,
});
}
// 选择暖场资源
handleSelectVideo
=
(
file
)
=>
{
const
{
selectType
}
=
this
.
state
;
this
.
setState
({
showSelectFileModal
:
false
})
const
{
ossUrl
,
resourceId
,
folderName
,
folderFormat
,
folderSize
}
=
file
;
if
(
selectType
===
'WARMUP'
){
const
liveCourseWarmMedia
=
{
contentType
:
'WARMUP'
,
mediaType
:
folderFormat
===
'MP4'
?
'VIDEO'
:
'PICTURE'
,
mediaContent
:
resourceId
,
mediaUrl
:
ossUrl
,
mediaName
:
folderName
,
size
:
folderSize
}
this
.
props
.
onChange
(
'liveCourseWarmMedia'
,
liveCourseWarmMedia
);
}
else
{
// 最多添加九图片
const
{
liveCourseMediaRequests
}
=
this
.
props
.
data
;
const
list
=
_
.
filter
(
liveCourseMediaRequests
,
(
item
)
=>
{
return
item
.
mediaType
==
"PICTURE"
;
});
if
(
list
.
length
>
8
)
{
message
.
warning
(
"最多添加9张图片"
);
return
;
}
liveCourseMediaRequests
.
push
({
contentType
:
'INTRO'
,
size
:
folderSize
,
mediaName
:
folderName
,
mediaContent
:
resourceId
,
mediaType
:
'PICTURE'
,
mediaUrl
:
ossUrl
,
});
this
.
props
.
onChange
(
'liveCourseMediaRequests'
,
liveCourseMediaRequests
);
}
}
// 删除简介
handleDeleteIntro
=
(
index
)
=>
{
const
{
liveCourseMediaRequests
}
=
this
.
props
.
data
;
liveCourseMediaRequests
.
splice
(
index
,
1
);
this
.
props
.
onChange
(
'liveCourseMediaRequests'
,
liveCourseMediaRequests
);
}
// 上移简介
handleMoveUpIntro
=
(
index
)
=>
{
const
{
liveCourseMediaRequests
}
=
this
.
props
.
data
;
const
prevItem
=
liveCourseMediaRequests
[
index
];
const
nextItem
=
liveCourseMediaRequests
[
index
+
1
];
liveCourseMediaRequests
.
splice
(
index
,
2
,
nextItem
,
prevItem
);
this
.
props
.
onChange
(
'liveCourseMediaRequests'
,
liveCourseMediaRequests
);
}
// 下移简介
handleMoveDownIntro
=
(
index
)
=>
{
const
{
liveCourseMediaRequests
}
=
this
.
props
.
data
;
const
prevItem
=
liveCourseMediaRequests
[
index
-
1
];
const
nextItem
=
liveCourseMediaRequests
[
index
];
liveCourseMediaRequests
.
splice
(
index
-
1
,
2
,
nextItem
,
prevItem
);
this
.
props
.
onChange
(
'liveCourseMediaRequests'
,
liveCourseMediaRequests
);
}
renderLittleIcon
=
(
index
)
=>
{
const
{
liveCourseMediaRequests
}
=
this
.
props
.
data
;
return
(
<
div
className=
"little-icon"
>
<
span
className=
"icon iconfont close"
onClick=
{
()
=>
{
this
.
handleDeleteIntro
(
index
);
}
}
></
span
>
{
index
>
0
&&
<
span
className=
"icon iconfont"
onClick=
{
()
=>
{
this
.
handleMoveDownIntro
(
index
);
}
}
>

</
span
>
}
{
index
!==
liveCourseMediaRequests
.
length
-
1
&&
<
span
className=
"icon iconfont"
onClick=
{
()
=>
{
this
.
handleMoveUpIntro
(
index
);
}
}
>

</
span
>
}
</
div
>
)
}
handleChangeIntro
=
(
index
,
value
,
length
)
=>
{
const
{
liveCourseMediaRequests
}
=
this
.
props
.
data
;
liveCourseMediaRequests
[
index
].
mediaContent
=
value
;
liveCourseMediaRequests
[
index
].
mediaContentLength
=
length
this
.
props
.
onChange
(
'liveCourseMediaRequests'
,
liveCourseMediaRequests
);
}
handleAddIntroText
=
()
=>
{
const
{
liveCourseMediaRequests
}
=
this
.
props
.
data
;
liveCourseMediaRequests
.
push
({
contentType
:
"INTRO"
,
mediaType
:
'TEXT'
,
mediaContent
:
''
,
key
:
Math
.
random
()
});
this
.
props
.
onChange
(
'liveCourseMediaRequests'
,
liveCourseMediaRequests
);
}
handleUpload
=
(
Blob
)
=>
{
this
.
setState
({
showSelectFileModal
:
true
,
selectType
:
'INTRO'
})
}
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'
)
}
}
componentWillMount
()
{
}
render
()
{
const
{
data
:
{
whetherVisitorsJoin
,
liveCourseMediaRequests
=
[],
shelfState
}
}
=
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"
>
<
span
className=
"label"
>
视频课简介:
</
span
>
<
div
className=
"content"
>
<
div
className=
"intro-list"
>
{
liveCourseMediaRequests
.
map
((
item
,
index
)
=>
{
if
(
item
.
mediaType
===
'TEXT'
)
{
return
(
<
div
className=
"intro-list__item"
key=
{
item
.
key
}
>
<
EditorBox
detail=
{
{
content
:
item
.
mediaContent
}
}
onChange=
{
(
val
,
length
)
=>
{
this
.
handleChangeIntro
(
index
,
val
,
length
)
}
}
/>
{
this
.
renderLittleIcon
(
index
)
}
</
div
>
)
}
if
(
item
.
mediaType
===
'PICTURE'
)
{
return
(
<
div
className=
"intro-list__item picture"
key=
{
index
}
>
<
div
className=
"img__wrap"
>
<
img
src=
{
item
.
mediaUrl
}
/>
</
div
>
{
this
.
renderLittleIcon
(
index
)
}
</
div
>
)
}
})
}
</
div
>
<
div
className=
"operate"
>
<
div
className=
"operate__item"
onClick=
{
this
.
handleAddIntroText
}
>
<
span
className=
"icon iconfont"
>

</
span
>
<
span
className=
"text"
>
文字
</
span
>
</
div
>
<
div
className=
"operate__item"
onClick=
{
this
.
handleUpload
}
>
<
span
className=
"icon iconfont"
>

</
span
>
<
span
className=
"text"
>
图片
</
span
>
</
div
>
</
div
>
<
div
className=
"tips"
>
• 图片支持jpeg、jpg、png、gif格式
</
div
>
</
div
>
</
div
>
{
/* 选择暖场图文件弹窗 */
}
{
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'
}
isOpen=
{
showSelectFileModal
}
onClose=
{
()
=>
{
this
.
setState
({
showSelectFileModal
:
false
})
}
}
onSelect=
{
this
.
handleSelectVideo
}
/>
}
</
div
>
)
}
}
export
default
AddVideoIntro
;
src/modules/course-manage/graphics-course/components/AddVideoIntro.less
0 → 100644
View file @
aaad704c
.add-video__intro-info {
.playback {
margin-bottom: 10px;
.require {
color: #EC4B35;
}
&__text {
color: #999;
}
}
.label{
display:inline-block;
text-align:right;
width:85px;
}
.playback,
.introduce {
display: flex;
flex-direction: row;
}
.allow-tourist-join{
display:flex;
.content{
display:flex;
}
.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 {
display: flex;
margin-bottom: 20px;
}
textarea.ant-input {
min-height: 80px;
}
.intro-list__item {
display: flex;
margin-bottom: 16px;
position: relative;
&.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 {
display: flex;
align-items: center;
justify-content: center;
width: 550px;
height: 80px;
line-height: 80px;
padding: 16px;
margin-top: 16px;
border: 1px dashed #EBEBEB;
border-radius: 4px;
.ant-upload {
vertical-align: middle;
}
&__item {
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;
}
}
}
.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-delete-wrap {
opacity: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
img {
position: absolute;
left: 50%;
top: 50%;
width: 40px;
height: 40px;
transform: translate(-50%, -50%);
}
&:hover {
opacity: 1;
cursor: pointer;
}
}
}
.opt-btns {
.default-btn {
margin-left: 16px;
color: #FF7519;
cursor: pointer;
&.disabled {
color: #CCC;
cursor: not-allowed;
}
}
}
}
.example-wrap {
font-family: PingFangSC-Regular, PingFang SC;
text-align: center;
.title {
margin-bottom: 6px;
font-size: 14px;
color: #333333;
}
.text {
margin-bottom: 16px;
font-size: 12px;
color: #999999;
}
img {
width: 180px;
}
}
.check-record-rule {
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;
}
}
.text {
color: #999;
}
}
.auto-send-class-report {
.label {
width: 57px;
height: 12px;
font-size: 14px;
font-weight: 400;
color: #666666;
line-height: 12px;
}
.open-text, .close-text {
width: 572px;
font-size: 14px;
font-weight: 400;
color: #999999;
line-height: 20px;
margin-left: 100px;
margin-top: 5px;
}
.open-text {
margin-top: 8px;
}
.close-text {
margin-bottom: 16px;
}
}
\ No newline at end of file
src/modules/course-manage/graphics-course/components/GraphicsCourseFilter.jsx
0 → 100644
View file @
aaad704c
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:11:57
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-02-23 16:35:37
* @Description: 视频课-搜索模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import
React
from
'react'
;
import
{
Row
,
Input
,
Select
,
Tooltip
}
from
'antd'
;
import
RangePicker
from
"@/modules/common/DateRangePicker"
;
import
'./GraphicsCourseFilter.less'
;
import
moment
from
'moment'
;
import
StoreService
from
"@/domains/store-domain/storeService"
;
const
{
Search
}
=
Input
;
const
{
Option
}
=
Select
;
const
DEFAULT_QUERY
=
{
courseName
:
null
,
// 课程名称
operatorId
:
null
,
// 创建人
beginTime
:
null
,
// 开始日期
endTime
:
null
,
// 结束日期
shelfState
:
null
,
}
const
defaultTeacherQuery
=
{
size
:
10
,
current
:
1
,
nickName
:
null
}
class
GraphicsCourseFilter
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
query
:
{
...
DEFAULT_QUERY
},
// 使用扩展运算符,避免浅拷贝
teacherQuery
:
defaultTeacherQuery
,
teacherList
:[],
expandFilter
:
false
}
}
componentDidMount
()
{
this
.
getTeacherList
();
}
getTeacherList
(
current
=
1
,
selectList
){
const
{
teacherQuery
,
teacherList
}
=
this
.
state
;
const
_query
=
{
...
teacherQuery
,
current
,
size
:
10
};
StoreService
.
getStoreUserBasicPage
(
_query
).
then
((
res
)
=>
{
const
{
result
=
{}
}
=
res
;
const
{
records
=
[],
total
=
0
,
hasNext
}
=
result
;
const
list
=
current
>
1
?
teacherList
.
concat
(
records
)
:
records
;
this
.
setState
({
hasNext
,
teacherList
:
list
,
})
});
}
// 滑动加载更多讲师列表
handleScrollTeacherList
=
(
e
)
=>
{
const
{
hasNext
}
=
this
.
state
;
const
container
=
e
.
target
;
const
scrollToBottom
=
container
&&
container
.
scrollHeight
<=
container
.
clientHeight
+
container
.
scrollTop
;
if
(
scrollToBottom
&&
hasNext
)
{
const
{
teacherQuery
}
=
this
.
state
;
let
_teacherQuery
=
teacherQuery
;
_teacherQuery
.
current
=
_teacherQuery
.
current
+
1
this
.
setState
({
teacherQuery
:{...
_teacherQuery
}
},()
=>
{
this
.
getTeacherList
(
_teacherQuery
.
current
)})
}
}
// 改变搜索条件
handleChangeQuery
=
(
field
,
value
)
=>
{
this
.
setState
({
query
:
{
...
this
.
state
.
query
,
[
field
]:
value
,
current
:
1
,
}
},
()
=>
{
if
(
field
===
'courseName'
)
return
;
this
.
props
.
onChange
(
this
.
state
.
query
)
});
}
handleChangeDates
=
(
dates
)
=>
{
const
query
=
_
.
clone
(
this
.
state
.
query
);
if
(
_
.
isEmpty
(
dates
))
{
delete
query
.
beginTime
;
delete
query
.
endTime
;
}
else
{
query
.
beginTime
=
dates
[
0
].
valueOf
();
query
.
endTime
=
dates
[
1
].
valueOf
();
}
this
.
setState
({
query
:{
...
query
,
current
:
1
,
}
},
()
=>
{
this
.
props
.
onChange
(
this
.
state
.
query
);
})
}
// 重置搜索条件
handleReset
=
()
=>
{
this
.
setState
({
query
:
DEFAULT_QUERY
,
},
()
=>
{
this
.
props
.
onChange
(
this
.
state
.
query
);
})
}
render
()
{
const
{
query
:
{
courseName
,
operator
,
beginTime
,
endTime
,
operatorId
,
shelfState
},
expandFilter
,
teacherList
,
teacherQuery
}
=
this
.
state
;
return
(
<
div
className=
"video-course-filter"
>
<
Row
type=
"flex"
justify=
"space-between"
align=
"top"
>
<
div
className=
"search-condition"
>
<
div
className=
"search-condition__item"
>
<
span
className=
"search-name"
>
图文课名称:
</
span
>
<
Search
value=
{
courseName
}
placeholder=
"搜索视频课名称"
onChange=
{
(
e
)
=>
{
this
.
handleChangeQuery
(
'courseName'
,
e
.
target
.
value
)}
}
onSearch=
{
()
=>
{
this
.
props
.
onChange
(
this
.
state
.
query
)
}
}
style=
{
{
width
:
"calc(100% - 84px)"
}
}
enterButton=
{
<
span
className=
"icon iconfont"
>

</
span
>
}
/>
</
div
>
<
div
className=
"search-condition__item"
>
<
span
>
创建人:
</
span
>
<
Select
placeholder=
"请选择创建人"
style=
{
{
width
:
"calc(100% - 70px)"
}
}
showSearch
allowClear
filterOption=
{
(
input
,
option
)
=>
option
}
onPopupScroll=
{
this
.
handleScrollTeacherList
}
suffixIcon=
{
<
span
className=
"icon iconfont"
style=
{
{
fontSize
:
'12px'
,
color
:
'#BFBFBF'
}
}
>

</
span
>
}
value=
{
operatorId
}
onChange=
{
(
value
)
=>
{
this
.
handleChangeQuery
(
'operatorId'
,
value
)
}
}
onSearch=
{
(
value
)
=>
{
teacherQuery
.
nickName
=
value
this
.
setState
({
teacherQuery
},
()
=>
{
this
.
getTeacherList
()
})
}
}
onClear
={(
value
)=
>
{
this
.
setState
({
teacherQuery
:{
size
:
10
,
current
:
1
,
nickName
:
null
}
},
()
=>
{
this
.
getTeacherList
()
})
}
}
>
{
_
.
map
(
teacherList
,
(
item
,
index
)
=>
{
return
(
<
Select
.
Option
value=
{
item
.
id
}
key=
{
item
.
id
}
>
{
item
.
nickName
}
</
Select
.
Option
>
);
})
}
</
Select
>
</
div
>
<
div
className=
"search-condition__item"
>
<
span
className=
"search-date"
>
创建日期:
</
span
>
<
RangePicker
id=
"course_date_picker"
allowClear=
{
false
}
value=
{
beginTime
?
[
moment
(
beginTime
),
moment
(
endTime
)]
:
null
}
format=
{
"YYYY-MM-DD"
}
onChange=
{
(
dates
)
=>
{
this
.
handleChangeDates
(
dates
)
}
}
style=
{
{
width
:
"calc(100% - 70px)"
}
}
/>
</
div
>
{
expandFilter
&&
<
div
className=
"search-condition__item"
>
<
span
className=
"shelf-status"
>
店铺展示:
</
span
>
<
Select
style=
{
{
width
:
"calc(100% - 84px)"
}
}
placeholder=
"请选择"
allowClear=
{
true
}
value=
{
shelfState
}
onChange=
{
(
value
)
=>
{
this
.
handleChangeQuery
(
'shelfState'
,
value
)
}
}
suffixIcon=
{
<
span
className=
"icon iconfont"
style=
{
{
fontSize
:
'12px'
,
color
:
'#BFBFBF'
}
}
>

</
span
>
}
>
<
Option
value=
"YES"
>
开启
</
Option
>
<
Option
value=
"NO"
>
关闭
</
Option
>
</
Select
>
</
div
>
}
</
div
>
<
div
className=
"reset-fold-area"
>
<
Tooltip
title=
"清空筛选"
><
span
className=
"resetBtn iconfont icon"
onClick=
{
this
.
handleReset
}
>

</
span
></
Tooltip
>
<
span
style=
{
{
cursor
:
'pointer'
}
}
className=
"fold-btn"
onClick=
{
()
=>
{
this
.
setState
({
expandFilter
:
!
expandFilter
});
}
}
>
{
this
.
state
.
expandFilter
?
<
span
><
span
>
收起
</
span
><
span
className=
"iconfont icon fold-icon"
>

</
span
>
</
span
>
:
<
span
>
展开
<
span
className=
"iconfont icon fold-icon"
>

</
span
></
span
>
}
</
span
>
</
div
>
</
Row
>
</
div
>
)
}
}
export
default
GraphicsCourseFilter
;
src/modules/course-manage/graphics-course/components/GraphicsCourseFilter.less
0 → 100644
View file @
aaad704c
.video-course-filter {
position: relative;
.video-list-table{
// tr:nth-child(even){
// background: transparent !important;
// }
// tr:nth-child(odd){
// td{
// background: #FAFAFA !important;
// }
// }
}
.search-condition {
width: calc(100% - 80px);
display: flex;
align-items: center;
flex-wrap: wrap;
&__item {
width: 30%;
margin-right: 3%;
margin-bottom: 12px;
.search-name{
vertical-align: middle;
}
.shelf-status{
width:84px;
display:inline-block;
text-align:right;
}
}
}
.reset-fold-area {
position: absolute;
right: 12px;
}
.resetBtn {
color: #999999;
font-size: 18px;
margin-right: 8px;
}
.fold-btn {
font-size: 14px;
color: #666666;
line-height: 20px;
.fold-icon {
font-size: 12px;
margin-left:4px;
}
}
}
.data-icon {
cursor: pointer;
}
src/modules/course-manage/graphics-course/components/GraphicsCourseList.jsx
0 → 100644
View file @
aaad704c
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:12:45
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-02-01 16:34:11
* @Description: 视频课-列表模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import
React
from
'react'
;
import
{
Table
,
Modal
,
message
,
Tooltip
,
Switch
,
Dropdown
}
from
'antd'
;
import
{
PageControl
}
from
"@/components"
;
import
{
LIVE_SHARE_MAP
}
from
'@/common/constants/academic/cloudClass'
;
import
{
appId
,
shareUrl
,
LIVE_SHARE
}
from
'@/domains/course-domain/constants'
;
import
ShareLiveModal
from
'@/modules/course-manage/modal/ShareLiveModal'
;
import
WatchDataModal
from
'../modal/WatchDataModal'
import
CourseService
from
"@/domains/course-domain/CourseService"
;
import
User
from
'@/common/js/user'
import
'./GraphicsCourseList.less'
;
const
ENV
=
process
.
env
.
DEPLOY_ENV
||
'dev'
;
class
GraphicsCourseList
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
id
:
''
,
// 视频课ID
studentIds
:[]
}
}
componentDidMount
()
{
const
videoCourseItem
=
localStorage
.
getItem
(
'videoCourseItem'
);
if
(
videoCourseItem
)
{
const
_videoCourseItem
=
JSON
.
parse
(
videoCourseItem
);
this
.
handleShowShareModal
(
_videoCourseItem
,
true
);
}
}
// 观看数据弹窗
handleShowWatchDataModal
=
(
record
)
=>
{
console
.
log
(
'111'
);
console
.
log
(
"record"
,
record
);
const
watchDataModal
=
(
<
WatchDataModal
type=
'videoCourseList'
data=
{
record
}
close=
{
()
=>
{
this
.
setState
({
watchDataModal
:
null
});
}
}
/>
);
this
.
setState
({
watchDataModal
});
}
// 请求表头
parseColumns
=
()
=>
{
const
columns
=
[
{
title
:
'图文课'
,
key
:
'scheduleName'
,
dataIndex
:
'scheduleName'
,
width
:
321
,
fixed
:
'left'
,
render
:
(
val
,
record
)
=>
{
const
{
coverUrl
,
scheduleVideoUrl
}
=
record
;
return
(
<
div
className=
"record__item"
>
{
/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */
}
<
img
className=
"course-cover"
src=
{
coverUrl
||
`${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`
}
/>
{
record
.
courseName
.
length
>
25
?
<
Tooltip
title=
{
record
.
courseName
}
>
<
div
className=
"course-name"
>
{
record
.
courseName
}
</
div
>
</
Tooltip
>
:
<
div
className=
"course-name"
>
{
record
.
courseName
}
</
div
>
}
</
div
>
)
}
},
{
title
:
'课程分类'
,
key
:
'categoryName'
,
dataIndex
:
'categoryName'
,
width
:
'20%'
,
render
:
(
val
,
record
)
=>
{
return
(
<
div
className=
"record__item"
>
{
record
.
categoryOneName
}{
record
.
categoryTwoName
?
`-${record.categoryTwoName}`
:
''
}
</
div
>
)
}
},
{
title
:
'创建人'
,
key
:
'createName'
,
dataIndex
:
'createName'
,
width
:
'10%'
,
render
:
(
val
)
=>
{
return
(
<
div
>
{
val
&&
<
Tooltip
title=
{
val
}
>
<
div
>
{
val
.
length
>
4
?
`${val.slice(0,4)}
...
`
:
val
}
</
div
>
</
Tooltip
>
}
</
div
>
)
}
},
{
title
:
<
span
>
<
span
>
店铺展示
</
span
>
<
Tooltip
title=
{
<
div
>
开启后,用户可在店铺内查看到此课程。若课程“未成功开课”,则系统会自动“关闭”店铺展示。
<
br
/>
关闭后,店铺内不再展示此课程,但用户仍可通过分享的海报/链接查看此课程。
</
div
>
}
><
i
className=
"icon iconfont"
style=
{
{
marginLeft
:
'5px'
,
cursor
:
'pointer'
,
color
:
'#bfbfbf'
,
fontSize
:
'14px'
}
}
>

</
i
></
Tooltip
>
</
span
>,
width
:
'12%'
,
dataIndex
:
"courseware"
,
render
:
(
val
,
item
,
index
)
=>
{
return
(
<
Switch
defaultChecked=
{
item
.
shelfState
===
"YES"
?
true
:
false
}
onChange=
{
()
=>
this
.
changeShelfState
(
item
)
}
/>
)
},
},
{
title
:
"观看用户数"
,
width
:
110
,
key
:
"watchUserCount"
,
dataIndex
:
"watchUserCount"
,
render
:
(
val
,
item
)
=>
{
return
(
<
div
className=
"watchUserCount"
>
{
val
}
</
div
>
)
},
},
{
title
:
'创建时间'
,
width
:
181
,
key
:
'created'
,
dataIndex
:
'created'
,
sorter
:
true
,
render
:
(
val
)
=>
{
return
formatDate
(
'YYYY-MM-DD H:i'
,
val
)
}
},
{
title
:
'最近修改时间'
,
width
:
181
,
key
:
'updated'
,
dataIndex
:
'updated'
,
sorter
:
true
,
render
:
(
val
)
=>
{
return
formatDate
(
'YYYY-MM-DD H:i'
,
val
)
}
},
{
title
:
'操作'
,
key
:
'operate'
,
dataIndex
:
'operate'
,
width
:
210
,
fixed
:
'right'
,
render
:
(
val
,
record
)
=>
{
return
(
<
div
className=
"operate"
>
<
div
className=
"operate__item"
onClick=
{
()
=>
this
.
handleShowWatchDataModal
(
record
)
}
>
观看数据
</
div
>
<
span
className=
"operate__item split"
>
|
</
span
>
<
div
className=
"operate__item"
onClick=
{
()
=>
this
.
handleShowShareModal
(
record
)
}
>
分享
</
div
>
<
span
className=
"operate__item split"
>
|
</
span
>
<
Dropdown
overlay=
{
this
.
renderMoreOperate
(
record
)
}
>
<
span
className=
"more-operate"
>
<
span
className=
"operate-text"
>
更多
</
span
>
<
span
className=
"iconfont icon"
style=
{
{
color
:
"#5289FA"
}
}
>

</
span
>
</
span
>
</
Dropdown
>
</
div
>
)
}
}
];
return
columns
;
}
renderMoreOperate
=
(
item
)
=>
{
return
(
<
div
className=
"live-course-more-menu"
>
<
div
className=
"operate__item"
key=
"plan"
onClick=
{
()
=>
{
RCHistory
.
push
(
`/create-video-course?type=edit&id=${item.id}`
);
}
}
>
关联培训计划
</
div
>
<
div
className=
"operate__item"
key=
"edit"
onClick=
{
()
=>
{
RCHistory
.
push
(
`/create-video-course?type=edit&id=${item.id}`
);
}
}
>
编辑
</
div
>
<
div
className=
"operate__item"
key=
"delete"
onClick=
{
()
=>
this
.
handleDeleteGraphicsCourse
(
item
.
id
)
}
>
删除
</
div
>
</
div
>
)
}
//改变上架状态
changeShelfState
=
(
item
)
=>
{
let
_shelfState
=
item
.
shelfState
if
(
_shelfState
===
'NO'
){
_shelfState
=
"YES"
;
item
.
shelfState
=
"YES"
}
else
{
_shelfState
=
"NO"
item
.
shelfState
=
"NO"
}
const
params
=
{
courseId
:
item
.
id
,
shelfState
:
_shelfState
}
CourseService
.
changeVideoShelfState
(
params
).
then
((
res
)
=>
{
if
(
res
.
success
){
if
(
_shelfState
===
"YES"
){
message
.
success
(
"已开启展示"
);
}
else
{
message
.
success
(
"已取消展示"
);
}
}
})
}
// 删除视频课
handleDeleteGraphicsCourse
=
(
scheduleId
)
=>
{
Modal
.
confirm
({
title
:
'你确定要删除此视频课吗?'
,
content
:
'删除后,学员将不能进行观看。'
,
icon
:
<
span
className=
"icon iconfont default-confirm-icon"
>

</
span
>,
okText
:
'确定'
,
okType
:
'danger'
,
cancelText
:
'取消'
,
onOk
:
()
=>
{
const
param
=
{
courseId
:
scheduleId
,
storeId
:
User
.
getStoreId
()
}
CourseService
.
delVideoSchedule
(
param
).
then
(()
=>
{
message
.
success
(
'删除成功'
);
this
.
props
.
onChange
();
})
}
});
}
// 显示分享弹窗
handleShowShareModal
=
(
record
,
needStr
=
false
)
=>
{
const
{
id
,
scheduleVideoUrl
}
=
record
;
const
_appId
=
appId
;
const
htmlUrl
=
`
${
LIVE_SHARE
}
video_detail/
${
id
}
?id=
${
User
.
getStoreId
()}
`
;
const
longUrl
=
htmlUrl
;
const
{
coverUrl
,
courseName
}
=
record
;
const
shareData
=
{
longUrl
,
coverUrl
,
scheduleVideoUrl
,
courseName
,
};
const
shareLiveModal
=
(
<
ShareLiveModal
needStr=
{
needStr
}
data=
{
shareData
}
type=
"videoClass"
close=
{
()
=>
{
this
.
setState
({
shareLiveModal
:
null
});
localStorage
.
setItem
(
'videoCourseItem'
,
''
);
}
}
/>
);
this
.
setState
({
shareLiveModal
});
}
handleChangeTable
=
(
pagination
,
filters
,
sorter
)
=>
{
const
{
columnKey
,
order
}
=
sorter
;
const
{
query
}
=
this
.
props
;
let
{
order
:
_order
}
=
query
;
// 按创建时间升序排序
if
(
columnKey
===
'created'
&&
order
===
'ascend'
)
{
_order
=
'CREATED_ASC'
;
}
// 按创建时间降序排序
if
(
columnKey
===
'created'
&&
order
===
'descend'
)
{
_order
=
'CREATED_DESC'
;
}
// 按更新时间升序排序
if
(
columnKey
===
'updated'
&&
order
===
'ascend'
)
{
_order
=
'UPDATED_ASC'
;
}
// 按更新时间降序排序
if
(
columnKey
===
'updated'
&&
order
===
'descend'
)
{
_order
=
'UPDATED_DESC'
;
}
const
_query
=
{
...
query
,
orderEnum
:
_order
};
this
.
props
.
onChange
(
_query
);
}
render
()
{
const
{
dataSource
=
[],
totalCount
,
query
}
=
this
.
props
;
const
{
current
,
size
}
=
query
;
return
(
<
div
className=
"video-course-list"
>
<
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
current=
{
current
-
1
}
pageSize=
{
size
}
total=
{
totalCount
}
toPage=
{
(
page
)
=>
{
const
_query
=
{...
query
,
current
:
page
+
1
};
this
.
props
.
onChange
(
_query
)
}
}
/>
</
div
>
{
this
.
state
.
shareLiveModal
}
{
this
.
state
.
watchDataModal
}
</
div
>
)
}
}
export
default
GraphicsCourseList
;
src/modules/course-manage/graphics-course/components/GraphicsCourseList.less
0 → 100644
View file @
aaad704c
.video-course-list {
margin-top: 12px;
.video-list-table{
tbody {
tr{
&:nth-child(even){
background: transparent !important;
td{
background:#FFF !important;
}
}
&:nth-child(odd){
background: #FAFAFA !important;
td{
background: #FAFAFA !important;
}
}
&:hover{
td{
background:#F3f6fa !important;
}
}
}
}
}
.watchUserCount{
text-align:right;
padding:16px;
}
.operate-text {
color: #5289FA;
cursor: pointer;
}
.operate {
display: flex;
&__item {
color: #5289FA;
cursor: pointer;
&.split {
margin: 0 8px;
color: #BFBFBF;
}
}
}
.more-operate{
line-height:20px;
}
.record__item {
display: flex;
.course-cover {
min-width: 97px;
max-width: 97px;
height: 50px;
border-radius: 2px;
margin-right: 8px;
background-color: #666;
}
.course-name {
color: #666;
width:188px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
height:48px;
}
}
}
.video-course-more-menu {
background: white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
border-radius: 4px;
div {
line-height: 30px;
padding: 0 15px;
cursor: pointer;
&:hover {
background: #f3f6fa;
}
}
}
\ No newline at end of file
src/modules/course-manage/graphics-course/components/GraphicsCourseOpt.less
0 → 100644
View file @
aaad704c
.video-course-opt {
margin-top:4px;
.link {
color: #FF8534;
}
}
\ No newline at end of file
src/modules/course-manage/graphics-course/components/VieoCourseOpt.jsx
0 → 100644
View file @
aaad704c
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:12:15
* @LastEditors: zhangleyuan
* @LastEditTime: 2020-12-26 16:07:27
* @Description: 视频课-操作模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import
React
from
'react'
;
import
{
Button
}
from
'antd'
;
import
'./GraphicsCourseOpt.less'
;
export
default
function
GraphicsCourseOpt
()
{
return
(
<
div
className=
"video-course-opt"
>
<
Button
type=
"primary"
onClick=
{
()
=>
{
RCHistory
.
push
(
'/create-graphics-course?type=add'
);
}
}
className=
"mr12"
>
新建图文课
</
Button
>
</
div
>
);
}
src/modules/course-manage/graphics-course/index.jsx
0 → 100644
View file @
aaad704c
import
React
from
'react'
;
import
GraphicsCourseFilter
from
'./components/GraphicsCourseFilter'
;
import
GraphicsCourseOpt
from
'./components/VieoCourseOpt'
;
import
GraphicsCourseList
from
'./components/GraphicsCourseList'
;
import
CourseService
from
"@/domains/course-domain/CourseService"
;
import
User
from
'@/common/js/user'
class
GraphicsCourse
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
query
:
{
size
:
10
,
current
:
1
,
storeId
:
User
.
getStoreId
()
},
dataSource
:
[],
// 视频课列表
totalCount
:
0
,
// 视频课数据总条数
}
}
componentWillMount
()
{
// 获取视频课列表
this
.
handleFetchScheduleList
();
}
// 获取视频课列表
handleFetchScheduleList
=
(
_query
=
{})
=>
{
const
query
=
{
...
this
.
state
.
query
,
...
_query
};
// 更新请求参数
this
.
setState
({
query
});
CourseService
.
videoSchedulePage
(
query
).
then
((
res
)
=>
{
const
{
result
=
{}
}
=
res
||
{};
const
{
records
=
[],
total
=
0
}
=
result
;
this
.
setState
({
dataSource
:
records
,
totalCount
:
Number
(
total
)
});
});
}
render
()
{
const
{
dataSource
,
totalCount
,
query
}
=
this
.
state
;
return
(
<
div
className=
"page video-course-page"
>
<
div
className=
"content-header"
>
图文课
</
div
>
<
div
className=
"box"
>
{
/* 搜索模块 */
}
<
GraphicsCourseFilter
onChange=
{
this
.
handleFetchScheduleList
}
/>
{
/* 操作模块 */
}
<
GraphicsCourseOpt
/>
{
/* 视频课列表模块 */
}
<
GraphicsCourseList
query=
{
query
}
dataSource=
{
dataSource
}
totalCount=
{
totalCount
}
onChange=
{
this
.
handleFetchScheduleList
}
/>
</
div
>
</
div
>
)
}
}
export
default
GraphicsCourse
;
src/modules/course-manage/graphics-course/modal/WatchDataModal.jsx
0 → 100644
View file @
aaad704c
/*
* @Author: 吴文洁
* @Date: 2020-05-19 11:01:31
* @Last Modified by: 吴文洁
* @Last Modified time: 2020-05-25 16:50:47
* @Description 余额异常弹窗
*/
import
React
from
'react'
;
import
{
Table
,
Modal
,
Input
}
from
'antd'
;
import
{
PageControl
}
from
"@/components"
;
import
CourseService
from
"@/domains/course-domain/CourseService"
;
import
User
from
'@/common/js/user'
import
'./WatchDataModal.less'
;
import
dealTimeDuration
from
"../../utils/dealTimeDuration"
;
const
{
Search
}
=
Input
;
class
WatchDataModal
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
visible
:
true
,
dataSource
:[],
size
:
10
,
query
:
{
current
:
1
,
},
totalCount
:
0
};
}
componentDidMount
()
{
this
.
handleFetchDataList
();
}
onClose
=
()
=>
{
this
.
props
.
close
();
}
// 获取观看视频数据列表
handleFetchDataList
=
()
=>
{
const
{
query
,
size
,
totalCount
}
=
this
.
state
const
{
id
}
=
this
.
props
.
data
;
const
params
=
{
...
query
,
size
,
courseId
:
id
,
storeId
:
User
.
getStoreId
()
}
CourseService
.
videoWatchInfo
(
params
).
then
((
res
)
=>
{
const
{
result
=
{}
}
=
res
;
const
{
records
=
[],
total
=
0
}
=
result
;
this
.
setState
({
dataSource
:
records
,
totalCount
:
Number
(
total
)
});
});
}
handleChangNickname
=
(
value
)
=>
{
const
isPhone
=
(
value
||
''
).
match
(
/^
\d
+$/
);
const
{
query
}
=
this
.
state
;
if
(
isPhone
){
query
.
phone
=
value
;
query
.
nickName
=
null
;
}
else
{
query
.
nickName
=
value
;
query
.
phone
=
null
;
}
query
.
current
=
1
;
this
.
setState
({
query
})
}
onShowSizeChange
=
(
current
,
size
)
=>
{
if
(
current
==
size
)
{
return
}
this
.
setState
({
size
},()
=>
{
this
.
handleFetchDataList
()})
}
// 请求表头
parseColumns
=
()
=>
{
const
columns
=
[
{
title
:
'观看用户'
,
key
:
'name'
,
dataIndex
:
'name'
},
{
title
:
'手机号'
,
key
:
'phone'
,
dataIndex
:
'phone'
},
{
title
:
'观看者类型'
,
key
:
'userRole'
,
dataIndex
:
'userRole'
},
{
title
:
'首次观看时间'
,
key
:
'firstWatch'
,
dataIndex
:
'firstWatch'
,
render
:
(
val
)
=>
{
return
formatDate
(
'YYYY-MM-DD H:i'
,
val
)
}
},
{
title
:
'观看时长'
,
key
:
'watchDuration'
,
dataIndex
:
'watchDuration'
,
render
:
(
val
)
=>
{
return
<
span
>
{
val
?
dealTimeDuration
(
val
)
:
"00:00:00"
}
</
span
>
}
}
];
return
columns
;
}
render
()
{
const
{
visible
,
size
,
dataSource
,
totalCount
,
query
}
=
this
.
state
;
return
(
<
Modal
title=
"视频课观看数据"
visible=
{
visible
}
footer=
{
null
}
onCancel=
{
this
.
onClose
}
maskClosable=
{
false
}
className=
"watch-data-modal"
closable=
{
true
}
width=
{
800
}
closeIcon=
{
<
span
className=
"icon iconfont modal-close-icon"
>

</
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"
>

</
span
>
}
/>
</
div
>
<
div
>
<
Table
rowKey=
{
record
=>
record
.
id
}
dataSource=
{
dataSource
}
columns=
{
this
.
parseColumns
()
}
pagination=
{
false
}
bordered
/>
{
dataSource
.
length
>
0
&&
<
div
className=
"box-footer"
>
<
PageControl
current=
{
query
.
current
-
1
}
pageSize=
{
size
}
total=
{
totalCount
}
size=
"small"
toPage=
{
(
page
)
=>
{
const
_query
=
{...
query
,
current
:
page
+
1
};
this
.
setState
({
query
:
_query
},()
=>
{
this
.
handleFetchDataList
()})
}
}
onShowSizeChange=
{
this
.
onShowSizeChange
}
/>
</
div
>
}
</
div
>
</
Modal
>
)
}
}
export
default
WatchDataModal
;
\ No newline at end of file
src/modules/course-manage/graphics-course/modal/WatchDataModal.less
0 → 100644
View file @
aaad704c
.watch-data-modal{
.search-container{
text-align:right;
margin-bottom:17px;
}
}
\ No newline at end of file
src/modules/home/Home.jsx
View file @
aaad704c
...
...
@@ -303,6 +303,10 @@ class Home extends React.Component {
className=
{
`tab${scheduleType === 'VOICE' ? ' selected' : ''}`
}
onClick=
{
()
=>
this
.
setState
({
scheduleType
:
'VOICE'
},
()
=>
this
.
getHotCourse
())
}
>
视频课
</
span
>
<
span
className=
{
`tab${scheduleType === 'GRAPHICS' ? ' selected' : ''}`
}
onClick=
{
()
=>
this
.
setState
({
scheduleType
:
'GRAPHICS'
},
()
=>
this
.
getHotCourse
())
}
>
图文课
</
span
>
</
div
>
<
div
className=
"study-select"
>
<
span
className=
"select-word"
>
(
{
moment
().
subtract
(
timeRange
-
1
,
'day'
).
format
(
'MM.DD'
)
}
~
{
moment
().
format
(
'MM.DD'
)
}
)
</
span
>
...
...
src/routes/config/mainRoutes.tsx
View file @
aaad704c
...
...
@@ -14,7 +14,9 @@ import CourseCatalogPage from '@/modules/store-manage/CourseCatalogPage';
import
LiveCoursePage
from
'@/modules/course-manage/LiveCoursePage'
;
import
AddLivePage
from
'@/modules/course-manage/AddLive'
import
VideoCoursePage
from
'@/modules/course-manage/video-course'
import
GraphicsCoursePage
from
'@/modules/course-manage/graphics-course'
import
AddVideoCoursePage
from
'@/modules/course-manage/video-course/AddVideoCourse'
import
AddGraphicsCoursePage
from
'@/modules/course-manage/graphics-course/AddGraphicsCourse'
import
DataList
from
'@/modules/course-manage/DataList/DataList'
;
import
ClassBook
from
'@/modules/resource-disk'
;
import
ResourceDisk
from
'@/modules/resource-disk'
;
...
...
@@ -62,6 +64,11 @@ const mainRoutes = [
name
:
'视频课'
},
{
path
:
'/graphics-course'
,
component
:
GraphicsCoursePage
,
name
:
'图文课'
},
{
path
:
'/create-live-course'
,
component
:
AddLivePage
,
name
:
'创建直播课'
...
...
@@ -72,6 +79,11 @@ const mainRoutes = [
name
:
'创建视频课'
},
{
path
:
'/create-graphics-course'
,
component
:
AddGraphicsCoursePage
,
name
:
'创建图文课'
},
{
path
:
'/resource-disk'
,
component
:
ResourceDisk
,
name
:
'资料云盘'
...
...
src/routes/config/menuList.tsx
View file @
aaad704c
...
...
@@ -19,7 +19,12 @@ export const menuList: any = [
groupName
:
"视频课"
,
groupCode
:
"CourseVideoClass"
,
link
:
'/video-course'
}
},
{
groupName
:
"图文课"
,
groupCode
:
"CourseVideoClass"
,
link
:
'/graphics-course'
},
]
},
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment