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
1f72a597
Commit
1f72a597
authored
May 06, 2021
by
chenshu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat:初始化
parent
97cf7a0e
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
2491 additions
and
0 deletions
+2491
-0
src/modules/course-manage/offline-course/AddGraphicsCourse.jsx
+689
-0
src/modules/course-manage/offline-course/AddGraphicsCourse.less
+147
-0
src/modules/course-manage/offline-course/components/AddGraphicsIntro.jsx
+199
-0
src/modules/course-manage/offline-course/components/AddGraphicsIntro.less
+277
-0
src/modules/course-manage/offline-course/components/OfflineCourseFilter.jsx
+229
-0
src/modules/course-manage/offline-course/components/OfflineCourseFilter.less
+58
-0
src/modules/course-manage/offline-course/components/OfflineCourseList.jsx
+487
-0
src/modules/course-manage/offline-course/components/OfflineCourseList.less
+89
-0
src/modules/course-manage/offline-course/components/OfflineCourseOpt.jsx
+28
-0
src/modules/course-manage/offline-course/components/OfflineCourseOpt.less
+7
-0
src/modules/course-manage/offline-course/index.jsx
+85
-0
src/modules/course-manage/offline-course/modal/WatchDataModal.jsx
+178
-0
src/modules/course-manage/offline-course/modal/WatchDataModal.less
+7
-0
src/routes/config/mainRoutes.tsx
+6
-0
src/routes/config/menuList.tsx
+5
-0
No files found.
src/modules/course-manage/offline-course/AddGraphicsCourse.jsx
0 → 100644
View file @
1f72a597
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:07:47
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-29 14:08:47
* @Description: 图文课新增/编辑页
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import
React
from
'react'
;
import
{
Button
,
Input
,
Radio
,
message
,
Modal
,
Cascader
}
from
'antd'
;
import
$
from
'jquery'
;
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
AddGraphicsIntro
from
'./components/AddGraphicsIntro'
;
import
SelectStudent
from
'../modal/select-student'
;
import
SelectPrepareFileModal
from
'../../prepare-lesson/modal/SelectPrepareFileModal'
;
import
PreviewGraphicsModal
from
'../modal/PreviewGraphicsModal'
;
import
StoreService
from
"@/domains/store-domain/storeService"
;
import
Service
from
'@/common/js/service'
;
import
{
randomString
}
from
'@/domains/basic-domain/utils'
;
import
User
from
'@/common/js/user'
;
import
_
from
"underscore"
;
import
Upload
from
'@/core/upload'
;
import
'./AddGraphicsCourse.less'
;
const
EDIT_BOX_KEY
=
Math
.
random
();
const
fieldNames
=
{
label
:
'categoryName'
,
value
:
'id'
,
children
:
'sonCategoryList'
};
//添加课程时课程默认的一些值
const
defaultShelfState
=
'YES'
;
const
whetherVisitorsJoin
=
'NO'
const
defaultCoverUrl
=
'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'
;
let
cutFlag
=
false
;
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
,
// 图文课名称
courseMedia
:
''
,
introduce
:
''
,
courseMediaId
:
null
,
// 图文课链接
coverId
:
null
,
// 图文封面的recourceId
coverUrl
:
defaultCoverUrl
,
// 图文课封面
studentList
:
[],
// 上课学员列表
shelfState
:
'YES'
,
//是否开启学院展示
diskList
:
[],
// 机构可见磁盘目录
selectedFileList
:
[],
// 已经从资料云盘中勾选的文件
showCutModal
:
false
,
// 是否显示截图弹窗
showSelectVideoModal
:
false
,
studentModal
:
false
,
categoryName
:
null
,
//分类名称
courseCatalogList
:[],
//分类列表
categoryId
:
null
,
//分类的Id值
whetherVisitorsJoin
:
'NO'
,
// 是否允许游客加入
isContent
:
true
,
}
}
componentWillMount
()
{
const
{
id
,
pageType
}
=
this
.
state
;
this
.
getCourseCatalogList
();
if
(
pageType
===
'edit'
)
{
this
.
handleFetchScheudleDetail
(
id
);
}
}
initBus
=
()
=>
{
Bus
.
bind
(
'graphicsEditorImage'
,
this
.
uploadImage
)
Bus
.
bind
(
'graphicsEditorVideo'
,
this
.
uploadVideo
)
}
removeBus
=
()
=>
{
Bus
.
unbind
(
'graphicsEditorImage'
,
this
.
uploadImage
)
Bus
.
unbind
(
'graphicsEditorVideo'
,
this
.
uploadVideo
)
}
uploadImage
=
()
=>
{
this
.
setState
({
showSelectImageModal
:
true
})
}
uploadVideo
=
()
=>
{
this
.
setState
({
showSelectVideoModal
:
true
})
}
//获取分类列表
getCourseCatalogList
=
()
=>
{
StoreService
.
getCourseCatalogList
({
current
:
1
,
size
:
1000
}).
then
((
res
)
=>
{
this
.
setState
({
courseCatalogList
:
res
.
result
.
records
})
});
}
catalogChange
=
(
value
,
options
)
=>
{
const
changeValueLength
=
value
.
length
;
switch
(
changeValueLength
){
case
1
:
this
.
setState
({
categoryId
:
value
[
0
],
categoryName
:
options
[
0
].
categoryName
});
break
;
case
2
:
this
.
setState
({
categoryId
:
value
[
1
],
categoryName
:
`
${
options
[
0
].
categoryName
}
-
${
options
[
1
].
categoryName
}
`
});
break
;
default
:
this
.
setState
({
categoryId
:
null
,
categoryName
:
''
});
break
;
}
}
// 获取图文课详情
handleFetchScheudleDetail
=
(
courseId
)
=>
{
Service
.
Hades
(
'public/hades/mediaCourseDetail'
,{
courseId
}).
then
((
res
)
=>
{
const
{
result
=
{}
}
=
res
||
{};
const
{
courseName
,
shelfState
,
whetherVisitorsJoin
,
courseMediaVOS
,
categoryOneName
,
categoryTwoName
,
categoryId
}
=
result
;
let
coverId
;
let
coverUrl
=
this
.
state
.
coverUrl
;
let
hasIntro
=
false
;
courseMediaVOS
.
map
((
item
)
=>
{
switch
(
item
.
contentType
){
case
"COVER"
:
coverId
=
item
.
mediaContent
;
coverUrl
=
item
.
mediaUrl
;
break
;
case
"SCHEDULE"
:
this
.
getTextDetail
(
'courseMedia'
,
item
.
mediaUrl
);
break
;
case
"INTRO"
:
hasIntro
=
true
;
this
.
getTextDetail
(
'introduce'
,
item
.
mediaUrl
);
break
;
default
:
break
;
}
return
item
;
})
let
categoryName
;
if
(
categoryTwoName
){
categoryName
=
`
${
categoryOneName
}
-
${
categoryTwoName
}
`
;
}
else
{
categoryName
=
`
${
categoryOneName
}
`
;
}
this
.
setState
({
loadintroduce
:
!
hasIntro
,
coverId
,
coverUrl
,
courseName
,
shelfState
,
whetherVisitorsJoin
,
categoryName
,
categoryId
});
})
}
getTextDetail
=
(
key
,
url
)
=>
{
$
.
ajax
({
data
:
{},
type
:
'GET'
,
url
,
contentType
:
'application/x-www-form-urlencoded; charset=UTF-8'
,
success
:
(
res
)
=>
{
this
.
setState
({
[
key
]:
res
,
[
`load
${
key
}
`
]:
true
});
}
})
}
handleGoBack
=
()
=>
{
const
{
coverId
,
videoName
,
videoDuration
,
courseName
,
courseMediaId
,
categoryId
,
shelfState
,
whetherVisitorsJoin
}
=
this
.
state
;
if
(
videoName
||
videoDuration
||
courseMediaId
||
categoryId
||
courseName
||
coverId
||
shelfState
!==
defaultShelfState
||
whetherVisitorsJoin
!==
whetherVisitorsJoin
){
Modal
.
confirm
({
title
:
'确认要返回吗?'
,
content
:
'返回后,本次编辑的内容将不被保存。'
,
okText
:
'确认返回'
,
cancelText
:
'留在本页'
,
icon
:
<
span
className=
"icon iconfont default-confirm-icon"
>

</
span
>,
onOk
:
()
=>
{
window
.
RCHistory
.
push
({
pathname
:
`/graphics-course`
,
});
}
});
}
else
{
window
.
RCHistory
.
push
({
pathname
:
`/graphics-course`
,
});
}
}
// 修改表单
handleChangeForm
=
(
field
,
value
,
coverUrl
)
=>
{
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
,
courseName
,
courseMedia
,
introduce
,
categoryName
,
}
=
this
.
state
;
const
courseBasinInfo
=
{
coverUrl
,
courseName
,
categoryName
}
const
courseIntroInfo
=
{
courseMedia
,
introduce
,
}
const
previewGraphicsModal
=
(
<
PreviewGraphicsModal
courseBasicInfo=
{
courseBasinInfo
}
courseIntroInfo=
{
courseIntroInfo
}
close=
{
()
=>
{
this
.
setState
({
previewGraphicsModal
:
null
})
}
}
/>
);
this
.
setState
({
previewGraphicsModal
});
}
handleSelectCover
=
(
file
)
=>
{
this
.
uploadCoverImage
(
file
);
}
//上传图片
uploadCoverImage
=
(
imageFile
)
=>
{
debugger
const
{
folderName
}
=
imageFile
;
const
fileName
=
window
.
random_string
(
16
)
+
folderName
.
slice
(
folderName
.
lastIndexOf
(
"."
));
const
self
=
this
;
this
.
setState
(
{
visible
:
true
,
},
()
=>
{
setTimeout
(()
=>
{
const
okBtnDom
=
document
.
querySelector
(
"#headPicModal"
);
const
options
=
{
size
:
[
500
,
282
],
ok
:
okBtnDom
,
maxZoom
:
3
,
style
:
{
jpgFillColor
:
"transparent"
,
},
done
:
function
(
dataUrl
)
{
clearTimeout
(
self
.
timer
);
self
.
timer
=
setTimeout
(()
=>
{
if
((
self
.
state
.
rotate
!=
this
.
rotate
())
||
(
self
.
state
.
scale
!=
this
.
scale
()))
{
const
_dataUrl
=
this
.
clip
()
const
cutImageBlob
=
self
.
convertBase64UrlToBlob
(
_dataUrl
);
self
.
setState
({
cutImageBlob
,
dataUrl
:
_dataUrl
,
rotate
:
this
.
rotate
(),
scale
:
this
.
scale
()
})
}
},
500
)
const
cutImageBlob
=
self
.
convertBase64UrlToBlob
(
dataUrl
);
self
.
setState
({
cutImageBlob
,
dataUrl
})
setTimeout
(()
=>
{
cutFlag
=
false
;
},
2000
);
},
fail
:
(
failInfo
)
=>
{
message
.
error
(
"图片上传失败了,请重新上传"
);
},
loadComplete
:
function
(
img
)
{
setTimeout
(()
=>
{
const
_dataUrl
=
this
.
clip
()
self
.
setState
({
dataUrl
:
_dataUrl
,
hasImgReady
:
true
})
},
100
)
},
};
const
imgUrl
=
`
${
imageFile
.
ossUrl
}
?
${
new
Date
().
getTime
()}
`
if
(
!
this
.
state
.
photoclip
)
{
const
_photoclip
=
new
PhotoClip
(
"#headPicModal"
,
options
);
_photoclip
.
load
(
imgUrl
);
this
.
setState
({
photoclip
:
_photoclip
,
});
}
else
{
this
.
state
.
photoclip
.
clear
();
this
.
state
.
photoclip
.
load
(
imgUrl
);
}
},
200
);
}
);
};
//获取resourceId
getSignature
=
(
blob
,
fileName
)
=>
{
Upload
.
uploadBlobToOSS
(
blob
,
'cover'
+
(
new
Date
()).
valueOf
(),
null
,
'signInfo'
).
then
((
signInfo
)
=>
{
this
.
setState
({
coverClicpPath
:
signInfo
.
fileUrl
,
coverId
:
signInfo
.
resourceId
,
visible
:
false
},()
=>
this
.
updateCover
())
});
};
updateCover
=
()
=>
{
const
{
coverClicpPath
,
coverId
}
=
this
.
state
this
.
setState
({
showSelectCoverModal
:
false
,
coverUrl
:
coverClicpPath
,
coverId
:
coverId
})
}
// base64转换成blob
convertBase64UrlToBlob
=
(
urlData
)
=>
{
const
bytes
=
window
.
atob
(
urlData
.
split
(
","
)[
1
]);
const
ab
=
new
ArrayBuffer
(
bytes
.
length
);
const
ia
=
new
Uint8Array
(
ab
);
for
(
let
i
=
0
;
i
<
bytes
.
length
;
i
++
)
{
ia
[
i
]
=
bytes
.
charCodeAt
(
i
);
}
return
new
Blob
([
ab
],
{
type
:
"image/png"
});
};
// 保存
handleSubmit
=
()
=>
{
const
{
id
,
coverId
,
pageType
,
courseName
,
courseMedia
,
introduce
,
categoryId
,
shelfState
,
whetherVisitorsJoin
,
}
=
this
.
state
;
const
commonParams
=
{
categoryId
,
courseName
,
coverId
,
operatorId
:
User
.
getStoreUserId
(),
storeId
:
User
.
getStoreId
(),
shelfState
,
whetherVisitorsJoin
,
courseType
:
'PICTURE'
,
};
// 校验必填字段:课程名称, 课程图文
this
.
handleValidate
(
courseName
,
courseMedia
,
categoryId
).
then
((
res
)
=>
{
if
(
!
res
)
return
;
Upload
.
uploadTextToOSS
(
courseMedia
,
`
${
randomString
()}
.txt`
,
(
courseMediaId
)
=>
{
Upload
.
uploadTextToOSS
(
introduce
,
`
${
randomString
()}
.txt`
,
(
introduceId
)
=>
{
this
.
submitRemote
({
id
,
pageType
,
commonParams
,
courseMediaId
,
introduceId
,
});
},
()
=>
message
.
warning
(
'上传课程简介失败'
));
},
()
=>
message
.
warning
(
'上传课程内容失败'
));
});
}
submitRemote
=
(
data
)
=>
{
const
{
id
,
pageType
,
commonParams
,
courseMediaId
,
introduceId
}
=
data
;
commonParams
.
courseMediaId
=
courseMediaId
;
commonParams
.
introduceId
=
introduceId
;
if
(
pageType
===
'add'
)
{
Service
.
Hades
(
'public/hades/createMediaCourse'
,
commonParams
).
then
((
res
)
=>
{
if
(
!
res
)
return
;
message
.
success
(
"新建成功"
);
window
.
RCHistory
.
push
({
pathname
:
`/graphics-course`
,
});
})
}
else
{
const
editParams
=
{
courseId
:
id
,
...
commonParams
,
}
Service
.
Hades
(
'public/hades/editMediaCourse'
,
editParams
).
then
((
res
)
=>
{
if
(
!
res
)
return
;
message
.
success
(
"保存成功"
);
window
.
RCHistory
.
push
({
pathname
:
`/graphics-course`
,
});
});
}
}
handleValidate
=
(
courseName
,
courseMedia
,
categoryId
)
=>
{
return
new
Promise
((
resolve
)
=>
{
if
(
!
courseName
)
{
message
.
warning
(
'请输入课程名称'
);
resolve
(
false
);
return
false
}
if
(
!
courseMedia
)
{
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
{
id
,
pageType
,
courseName
,
coverUrl
,
studentList
,
courseMedia
,
introduce
,
showCutModal
,
diskList
,
imageFile
,
videoType
,
shelfState
,
categoryName
,
courseCatalogList
,
whetherVisitorsJoin
,
loadcourseMedia
,
loadintroduce
,
showSelectCoverModal
,
visible
,
hasImgReady
,
cutImageBlob
,
}
=
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"
>
<
Button
onClick=
{
()
=>
{
this
.
setState
({
showSelectCoverModal
:
true
});
}
}
>
{
`${(pageType === 'add' && !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"
>
<
AddGraphicsIntro
data=
{
{
id
,
courseMedia
,
introduce
,
shelfState
,
whetherVisitorsJoin
,
loadcourseMedia
,
loadintroduce
,
}
}
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
>
{
showSelectCoverModal
&&
<
SelectPrepareFileModal
key=
"basic"
operateType=
"select"
multiple=
{
false
}
accept=
"image/jpeg,image/png,image/jpg"
selectTypeList=
{
[
'JPG'
,
'JPEG'
,
'PNG'
]
}
tooltip=
'支持文件类型:jpg、jpeg、png'
isOpen=
{
showSelectCoverModal
}
onClose=
{
()
=>
{
this
.
setState
({
showSelectCoverModal
:
false
})
}
}
onSelect=
{
this
.
handleSelectCover
}
/>
}
<
Modal
title=
"设置图片"
width=
{
1080
}
visible=
{
visible
}
maskClosable=
{
false
}
closeIcon=
{
<
span
className=
"icon iconfont modal-close-icon"
>

</
span
>
}
onCancel=
{
()
=>
{
this
.
setState
({
visible
:
false
});
}
}
zIndex=
{
10001
}
footer=
{
[
<
Button
key=
"back"
onClick=
{
()
=>
{
this
.
setState
({
visible
:
false
});
}
}
>
重新上传
</
Button
>,
<
Button
key=
"submit"
type=
"primary"
disabled=
{
!
hasImgReady
}
onClick=
{
()
=>
{
if
(
!
cutFlag
)
{
cutFlag
=
true
;
this
.
refs
.
hiddenBtn
.
click
();
}
this
.
getSignature
(
cutImageBlob
);
}
}
>
确定
</
Button
>,
]
}
>
<
div
className=
"clip-box"
>
<
div
id=
"headPicModal"
ref=
"headPicModal"
style=
{
{
width
:
"500px"
,
height
:
"430px"
,
marginBottom
:
0
,
}
}
></
div
>
<
div
id=
"clipBtn"
style=
{
{
display
:
"none"
}
}
ref=
"hiddenBtn"
></
div
>
<
div
className=
"preview-img"
>
<
div
className=
"title"
>
效果预览
</
div
>
<
div
id=
"preview-url-box"
style=
{
{
width
:
500
,
height
:
282
}
}
>
<
img
src=
{
this
.
state
.
dataUrl
}
style=
{
{
width
:
'100%'
}
}
alt=
""
/>
</
div
>
<
div
className=
"tip-box"
>
<
div
className=
"tip"
>
温馨提示
</
div
>
<
div
className=
"tip"
>
①预览效果图时可能存在延迟,单击左侧图片刷新即可
</
div
>
<
div
className=
"tip"
>
②设置图片时双击可旋转图片,滚动可放大或缩小图片
</
div
>
</
div
>
</
div
>
</
div
>
</
Modal
>
{
this
.
state
.
previewGraphicsModal
}
</
div
>
)
}
}
export
default
AddGraphicsCourse
;
src/modules/course-manage/offline-course/AddGraphicsCourse.less
0 → 100644
View file @
1f72a597
.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/offline-course/components/AddGraphicsIntro.jsx
0 → 100644
View file @
1f72a597
/*
* @Author: 吴文洁
* @Date: 2020-07-16 11:05:17
* @Last Modified by: chenshu
* @Last Modified time: 2021-04-06 16:17:57
* @Description: 添加直播-简介
*/
import
React
from
'react'
;
import
{
Input
,
message
,
Upload
,
Radio
,
Row
,
Col
,
Button
,
Popover
,
Switch
}
from
'antd'
;
import
Service
from
'@/common/js/service'
;
import
GraphicsEditor
from
'../../components/GraphicsEditor'
;
import
User
from
'@/common/js/user'
;
import
UploadOss
from
'@/core/upload'
;
import
'./AddGraphicsIntro.less'
;
import
SelectPrepareFileModal
from
'@/modules/prepare-lesson/modal/SelectPrepareFileModal'
;
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
,
}
}
// 上传封面图
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
);
}
}
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'
)
}
}
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
{
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
)
&&
<
GraphicsEditor
id=
"content"
detail=
{
{
content
:
courseMedia
}
}
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
)
&&
<
GraphicsEditor
id=
"intro"
isIntro=
{
true
}
detail=
{
{
content
:
introduce
}
}
onChange=
{
(
val
)
=>
{
this
.
changeIntro
(
val
)
}
}
/>
}
</
div
>
</
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
AddGraphicsIntro
;
src/modules/course-manage/offline-course/components/AddGraphicsIntro.less
0 → 100644
View file @
1f72a597
.add-video__intro-info {
.w-e-full-screen-editor {
background: #fff !important;
}
.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/offline-course/components/OfflineCourseFilter.jsx
0 → 100644
View file @
1f72a597
import
React
from
'react'
;
import
{
Row
,
Input
,
Select
,
Tooltip
}
from
'antd'
;
import
RangePicker
from
"@/modules/common/DateRangePicker"
;
import
'./OfflineCourseFilter.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
OfflineCourseFilter
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
OfflineCourseFilter
;
src/modules/course-manage/offline-course/components/OfflineCourseFilter.less
0 → 100644
View file @
1f72a597
.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;
align-items: center;
display: flex;
.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/offline-course/components/OfflineCourseList.jsx
0 → 100644
View file @
1f72a597
/*
* @Author: 吴文洁
* @Date: 2020-08-05 10:12:45
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-27 16:24:47
* @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
RelatedPlanModal
from
'../../modal/RelatedPlanModal'
;
import
User
from
'@/common/js/user'
import
'./OfflineCourseList.less'
;
const
ENV
=
process
.
env
.
DEPLOY_ENV
||
'dev'
;
const
defaultCoverUrl
=
'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'
;
class
OfflineCourseList
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
id
:
''
,
// 视频课ID
studentIds
:[],
RelatedPlanModalVisible
:
false
,
selectPlanList
:{}
}
}
componentDidMount
()
{
const
videoCourseItem
=
localStorage
.
getItem
(
'videoCourseItem'
);
if
(
videoCourseItem
)
{
const
_videoCourseItem
=
JSON
.
parse
(
videoCourseItem
);
this
.
handleShowShareModal
(
_videoCourseItem
,
true
);
}
}
// 观看数据弹窗
handleShowWatchDataModal
=
(
record
)
=>
{
const
watchDataModal
=
(
<
WatchDataModal
type=
'videoCourseList'
data=
{
record
}
close=
{
()
=>
{
this
.
setState
({
watchDataModal
:
null
});
}
}
/>
);
this
.
setState
({
watchDataModal
});
}
handlePlanName
=
(
planArray
)
=>
{
let
planStr
=
""
;
planArray
.
map
((
item
,
index
)
=>
{
if
(
index
<
planArray
.
length
-
1
){
planStr
=
planStr
+
item
.
planName
+
'、'
;
}
else
{
planStr
=
planStr
+
item
.
planName
}
})
return
planStr
}
// 请求表头
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
||
defaultCoverUrl
}
/>
{
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
:
120
,
render
:
(
val
,
record
)
=>
{
return
(
<
div
className=
"record__item"
>
{
record
.
categoryOneName
}{
record
.
categoryTwoName
?
`-${record.categoryTwoName}`
:
''
}
</
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
:
<
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
:
120
,
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
:
'关联项'
,
width
:
200
,
key
:
"planList"
,
dataIndex
:
"planList"
,
render
:
(
val
,
record
)
=>
{
return
(
<
div
className=
"related-task"
>
{
record
.
relatedPlanList
?
<
Tooltip
title=
{
this
.
handlePlanName
(
record
.
relatedPlanList
)
}
placement=
"top"
arrowPointAtCenter
>
{
record
.
relatedPlanList
.
map
((
item
,
index
)
=>
{
return
<
span
>
{
item
.
planName
}
{
(
index
<
record
.
relatedPlanList
.
length
-
1
)
&&
(<
span
>
、
</
span
>)
}
</
span
>
})
}
</
Tooltip
>
:
<
span
></
span
>
}
</
div
>
)
}
},
{
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
;
}
handleRelatedModalShow
=
(
item
)
=>
{
const
selectPlanList
=
{};
if
(
item
.
relatedPlanList
){
item
.
relatedPlanList
.
map
((
item
,
index
)
=>
{
selectPlanList
[
item
.
planId
]
=
{}
selectPlanList
[
item
.
planId
].
planId
=
item
.
planId
;
selectPlanList
[
item
.
planId
].
taskBaseVOList
=
[{
taskId
:
item
.
taskId
}];
return
item
})
}
this
.
setState
({
RelatedPlanModalVisible
:
true
,
selectCourseId
:
item
.
id
,
selectPlanList
:
selectPlanList
,
})
}
closeRelatedPlanModalVisible
=
()
=>
{
this
.
setState
({
RelatedPlanModalVisible
:
false
})
}
onChangeSelectPlanList
=
(
selectPlanList
)
=>
{
this
.
setState
({
selectPlanList
:
selectPlanList
})
}
onConfirmSelectPlanList
=
()
=>
{
this
.
setState
({
RelatedPlanModalVisible
:
false
},
()
=>
{
this
.
props
.
onChange
();
});
}
renderMoreOperate
=
(
item
)
=>
{
return
(
<
div
className=
"live-course-more-menu"
>
{
(
User
.
getUserRole
()
===
"CloudManager"
||
User
.
getUserRole
()
===
"StoreManager"
)
&&
<
div
className=
"operate__item"
key=
"plan"
onClick=
{
()
=>
{
this
.
handleRelatedModalShow
(
item
);
}
}
>
关联培训计划
</
div
>
}
<
div
className=
"operate__item"
key=
"edit"
onClick=
{
()
=>
{
RCHistory
.
push
(
`/create-graphics-course?type=edit&id=${item.id}`
);
}
}
>
编辑
</
div
>
<
div
className=
"operate__item"
key=
"delete"
onClick=
{
()
=>
this
.
handleDeleteOfflineCourse
(
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
(
"已取消展示"
);
}
}
})
}
// 删除视频课
handleDeleteOfflineCourse
=
(
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
}
graphics_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"
title=
"图文课"
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
);
}
handleRelatedModalShow
=
(
item
)
=>
{
const
selectPlanList
=
{};
if
(
item
.
relatedPlanList
){
item
.
relatedPlanList
.
map
((
item
,
index
)
=>
{
selectPlanList
[
item
.
planId
]
=
{}
selectPlanList
[
item
.
planId
].
planId
=
item
.
planId
;
selectPlanList
[
item
.
planId
].
taskBaseVOList
=
[{
taskId
:
item
.
taskId
}];
return
item
})
}
this
.
setState
({
RelatedPlanModalVisible
:
true
,
selectCourseId
:
item
.
id
,
selectPlanList
:
selectPlanList
})
}
closeRelatedPlanModalVisible
=
()
=>
{
this
.
setState
({
RelatedPlanModalVisible
:
false
})
}
onChangeSelectPlanList
=
(
selectPlanList
)
=>
{
this
.
setState
({
selectPlanList
:
selectPlanList
})
}
onConfirmSelectPlanList
=
()
=>
{
this
.
setState
({
RelatedPlanModalVisible
:
false
},()
=>
{
this
.
props
.
onChange
();})
}
render
()
{
const
{
RelatedPlanModalVisible
,
selectCourseId
,
selectPlanList
}
=
this
.
state
;
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
>
{
RelatedPlanModalVisible
&&
<
RelatedPlanModal
onClose=
{
this
.
closeRelatedPlanModalVisible
}
visible=
{
RelatedPlanModalVisible
}
selectCourseId=
{
selectCourseId
}
selectPlanList=
{
selectPlanList
}
onChange=
{
this
.
onChangeSelectPlanList
}
onConfirm=
{
this
.
onConfirmSelectPlanList
}
/>
}
{
RelatedPlanModalVisible
&&
<
RelatedPlanModal
onClose=
{
this
.
closeRelatedPlanModalVisible
}
visible=
{
RelatedPlanModalVisible
}
selectCourseId=
{
selectCourseId
}
selectPlanList=
{
selectPlanList
}
onChange=
{
this
.
onChangeSelectPlanList
}
onConfirm=
{
this
.
onConfirmSelectPlanList
}
/>
}
{
this
.
state
.
shareLiveModal
}
{
this
.
state
.
watchDataModal
}
</
div
>
)
}
}
export
default
OfflineCourseList
;
src/modules/course-manage/offline-course/components/OfflineCourseList.less
0 → 100644
View file @
1f72a597
.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/offline-course/components/OfflineCourseOpt.jsx
0 → 100644
View file @
1f72a597
/*
* @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
'./OfflineCourseOpt.less'
;
export
default
function
OfflineCourseOpt
()
{
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/offline-course/components/OfflineCourseOpt.less
0 → 100644
View file @
1f72a597
.video-course-opt {
margin-top:4px;
.link {
color: #FF8534;
}
}
\ No newline at end of file
src/modules/course-manage/offline-course/index.jsx
0 → 100644
View file @
1f72a597
import
React
from
'react'
;
import
OfflineCourseFilter
from
'./components/OfflineCourseFilter'
;
import
OfflineCourseOpt
from
'./components/OfflineCourseOpt'
;
import
OfflineCourseList
from
'./components/OfflineCourseList'
;
import
Service
from
'@/common/js/service'
;
import
User
from
'@/common/js/user'
class
OfflineCoursePage
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
query
:
{
size
:
10
,
current
:
1
,
courseType
:
'PICTURE'
,
storeId
:
User
.
getStoreId
()
},
dataSource
:
[],
// 视频课列表
totalCount
:
0
,
// 视频课数据总条数
}
}
componentWillMount
()
{
// 获取视频课列表
this
.
handleFetchScheduleList
();
}
// 获取视频课列表
handleFetchScheduleList
=
(
_query
=
{})
=>
{
const
query
=
{
...
this
.
state
.
query
,
...
_query
};
// 更新请求参数
this
.
setState
({
query
});
Service
.
Hades
(
'public/hades/mediaCoursePage'
,
query
).
then
((
res
)
=>
{
const
{
result
=
{}
}
=
res
||
{};
const
{
records
=
[],
total
=
0
}
=
result
;
this
.
setState
({
dataSource
:
records
,
totalCount
:
Number
(
total
)
});
})
// 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"
>
{
/* 搜索模块 */
}
<
OfflineCourseFilter
onChange=
{
this
.
handleFetchScheduleList
}
/>
{
/* 操作模块 */
}
<
OfflineCourseOpt
/>
{
/* 视频课列表模块 */
}
<
OfflineCourseList
query=
{
query
}
dataSource=
{
dataSource
}
totalCount=
{
totalCount
}
onChange=
{
this
.
handleFetchScheduleList
}
/>
</
div
>
</
div
>
)
}
}
export
default
OfflineCoursePage
;
src/modules/course-manage/offline-course/modal/WatchDataModal.jsx
0 → 100644
View file @
1f72a597
/*
* @Author: 吴文洁
* @Date: 2020-05-19 11:01:31
* @Last Modified by: chenshu
* @Last Modified time: 2021-03-24 15:13:38
* @Description 余额异常弹窗
*/
import
React
from
'react'
;
import
{
Table
,
Modal
,
Input
}
from
'antd'
;
import
{
PageControl
}
from
"@/components"
;
import
Service
from
"@/common/js/service"
;
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
()
}
Service
.
Hades
(
'public/hades/mediaCourseWatchInfo'
,
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
>
}
},
{
title
:
'学习进度'
,
key
:
'progress'
,
dataIndex
:
'progress'
,
render
:
(
val
)
=>
{
return
<
span
>
{
val
===
100
?
'已完成'
:
`${val || 0}%`
}
</
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/offline-course/modal/WatchDataModal.less
0 → 100644
View file @
1f72a597
.watch-data-modal{
.search-container{
text-align:right;
margin-bottom:17px;
}
}
\ No newline at end of file
src/routes/config/mainRoutes.tsx
View file @
1f72a597
...
@@ -17,6 +17,7 @@ import LiveCoursePage from '@/modules/course-manage/LiveCoursePage';
...
@@ -17,6 +17,7 @@ import LiveCoursePage from '@/modules/course-manage/LiveCoursePage';
import
AddLivePage
from
'@/modules/course-manage/AddLive'
import
AddLivePage
from
'@/modules/course-manage/AddLive'
import
VideoCoursePage
from
'@/modules/course-manage/video-course'
import
VideoCoursePage
from
'@/modules/course-manage/video-course'
import
GraphicsCoursePage
from
'@/modules/course-manage/graphics-course'
import
GraphicsCoursePage
from
'@/modules/course-manage/graphics-course'
import
OfflineCoursePage
from
'@/modules/course-manage/offline-course'
import
AddVideoCoursePage
from
'@/modules/course-manage/video-course/AddVideoCourse'
import
AddVideoCoursePage
from
'@/modules/course-manage/video-course/AddVideoCourse'
import
AddGraphicsCoursePage
from
'@/modules/course-manage/graphics-course/AddGraphicsCourse'
import
AddGraphicsCoursePage
from
'@/modules/course-manage/graphics-course/AddGraphicsCourse'
// import DataList from '@/modules/course-manage/DataList/DataList';
// import DataList from '@/modules/course-manage/DataList/DataList';
...
@@ -86,6 +87,11 @@ const mainRoutes = [
...
@@ -86,6 +87,11 @@ const mainRoutes = [
name
:
"图文课"
,
name
:
"图文课"
,
},
},
{
{
path
:
"/offline-course"
,
component
:
OfflineCoursePage
,
name
:
"线下课"
,
},
{
path
:
"/create-live-course"
,
path
:
"/create-live-course"
,
component
:
AddLivePage
,
component
:
AddLivePage
,
name
:
"创建直播课"
,
name
:
"创建直播课"
,
...
...
src/routes/config/menuList.tsx
View file @
1f72a597
...
@@ -33,6 +33,11 @@ export const menuList: any = [
...
@@ -33,6 +33,11 @@ export const menuList: any = [
groupCode
:
"GraphicLesson"
,
groupCode
:
"GraphicLesson"
,
link
:
'/graphics-course'
link
:
'/graphics-course'
},
},
{
groupName
:
"线下课"
,
groupCode
:
"GraphicLesson"
,
link
:
'/offline-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