Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-resource
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-resource
Commits
a5393832
提交
a5393832
authored
6月 29, 2022
作者:
lihuihui
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
课程开发
上级
4b439fa9
显示空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
1036 行增加
和
1 行删除
+1036
-1
Index.vue
src/components/tinymce/Index.vue
+8
-1
api.ts
src/modules/course/create/api.ts
+76
-0
AddCourseCover.vue
src/modules/course/create/components/AddCourseCover.vue
+224
-0
AddExam.vue
src/modules/course/create/components/AddExam.vue
+145
-0
AddLecturer.vue
src/modules/course/create/components/AddLecturer.vue
+120
-0
AddLive.vue
src/modules/course/create/components/AddLive.vue
+119
-0
StepOne.vue
src/modules/course/create/components/StepOne.vue
+183
-0
UploadImg.vue
src/modules/course/create/components/UploadImg.vue
+125
-0
index.ts
src/modules/course/create/index.ts
+12
-0
Create.vue
src/modules/course/create/views/Create.vue
+19
-0
vite.config.ts
vite.config.ts
+5
-0
没有找到文件。
src/components/tinymce/Index.vue
浏览文件 @
a5393832
...
@@ -3,6 +3,13 @@ import Editor from '@tinymce/tinymce-vue'
...
@@ -3,6 +3,13 @@ import Editor from '@tinymce/tinymce-vue'
import
md5
from
'blueimp-md5'
import
md5
from
'blueimp-md5'
import
{
getSignature
,
uploadFile
}
from
'@/api/base'
import
{
getSignature
,
uploadFile
}
from
'@/api/base'
const
props
=
defineProps
({
height
:
{
type
:
Number
,
default
:
600
}
})
const
ImageUploadHandler
=
(
blobInfo
:
any
)
=>
const
ImageUploadHandler
=
(
blobInfo
:
any
)
=>
new
Promise
((
resolve
,
reject
)
=>
{
new
Promise
((
resolve
,
reject
)
=>
{
const
file
=
blobInfo
.
blob
()
const
file
=
blobInfo
.
blob
()
...
@@ -35,7 +42,7 @@ const ImageUploadHandler = (blobInfo: any) =>
...
@@ -35,7 +42,7 @@ const ImageUploadHandler = (blobInfo: any) =>
const
init
=
{
const
init
=
{
language
:
'zh-Hans'
,
language
:
'zh-Hans'
,
height
:
600
,
height
:
props
.
height
,
menubar
:
false
,
menubar
:
false
,
statusbar
:
false
,
statusbar
:
false
,
plugins
:
'table charmap fullscreen lists link code preview quickbars'
,
plugins
:
'table charmap fullscreen lists link code preview quickbars'
,
...
...
src/modules/course/create/api.ts
0 → 100644
浏览文件 @
a5393832
import
httpRequest
from
'@/utils/axios'
// 讲师搜索
export
function
searchLecturer
(
params
:
{
name
:
string
;
'per-page'
?:
string
;
})
{
return
httpRequest
.
get
(
'/api/resource/v1/course/course/search-lecturer'
,
{
params
})
}
// 直播搜索
export
function
searchLive
(
params
:
{
name
:
string
;
'per-page'
?:
string
;
})
{
return
httpRequest
.
get
(
'/api/resource/v1/course/course/search-live'
,
{
params
})
}
// 试卷搜索
export
function
searchExam
(
data
:
{
nonce
:
string
;
timestamp
:
string
;
signature
:
string
,
paper_title
:
string
;
limit
:
string
;
is_all
:
number
;
project_prefix
:
string
;
})
{
return
httpRequest
.
post
(
'/api/qbs/api/v1/question-papers/search'
,
data
)
}
// 新建课件
export
function
createCourse
(
data
:
any
)
{
return
httpRequest
.
post
(
'/api/resource/v1/course/course/create'
,
data
)
}
// 获取封面列表
export
function
getCoverList
()
{
return
httpRequest
.
get
(
'/api/resource/v1/util/get-cover-list'
)
}
// 获取视频详情
export
function
getVideoDetails
(
params
:
{
id
:
string
})
{
return
httpRequest
.
get
(
'/api/resource/v1/resource/video/view'
,
{
params
})
}
// 更新视频
export
function
updateVideo
(
data
:
{
id
:
string
;
name
:
string
;
classification
:
string
;
knowledge_points
?:
string
;
cover
?:
string
})
{
return
httpRequest
.
post
(
'/api/resource/v1/resource/video/update'
,
data
)
}
// 部门共享
export
function
setDepartment
(
data
:
{
id
:
string
})
{
return
httpRequest
.
post
(
'/api/resource/v1/resource/video/set-department'
,
data
)
}
// 平台共享
export
function
setPlatform
(
data
:
{
id
:
string
})
{
return
httpRequest
.
post
(
'/api/resource/v1/resource/video/set-platform'
,
data
)
}
// 上下线
export
function
setStatus
(
data
:
{
id
:
string
})
{
return
httpRequest
.
post
(
'/api/resource/v1/resource/video/set-status'
,
data
)
}
// 更改负责人
export
function
setBelong
(
data
:
{
id
:
string
;
belong_operator
:
string
})
{
return
httpRequest
.
post
(
'/api/resource/v1/resource/video/set-belong'
,
data
)
}
// 获取视频详情
export
function
getVideoPpt
(
params
:
{
id
:
string
})
{
return
httpRequest
.
get
(
'/api/resource/v1/resource/video/get-ppt'
,
{
params
})
}
// 创建课件
export
function
createPpt
(
data
:
{
id
:
string
;
ppts
:
string
})
{
return
httpRequest
.
post
(
'/api/resource/v1/resource/video/create-ppt'
,
data
)
}
// 编辑课件
export
function
updatePpt
(
data
:
{
id
:
string
;
ppt_id
:
string
,
name
:
string
,
point
:
string
,
url
:
string
})
{
return
httpRequest
.
post
(
'/api/resource/v1/resource/video/update-ppt'
,
data
)
}
// 编辑课件
export
function
deletePpt
(
data
:
{
id
:
string
;
ppt_id
:
string
})
{
return
httpRequest
.
post
(
'/api/resource/v1/resource/video/delete-ppt'
,
data
)
}
src/modules/course/create/components/AddCourseCover.vue
0 → 100644
浏览文件 @
a5393832
<
script
setup
lang=
"ts"
>
import
{
getCoverList
}
from
'../api'
import
{
PictureFilled
,
ArrowLeftBold
,
ArrowRightBold
}
from
'@element-plus/icons-vue'
import
UploadImg
from
'./UploadImg.vue'
const
emit
=
defineEmits
<
{
(
e
:
'update:dialogVisible'
,
dialogVisible
:
false
):
void
(
e
:
'change'
,
url
:
string
):
void
}
>
()
defineProps
({
dialogVisible
:
{
type
:
Boolean
,
required
:
true
}
})
// 关闭弹框
const
handleCancel
=
()
=>
{
emit
(
'update:dialogVisible'
,
false
)
}
// 选择封面
const
changeCover
=
()
=>
{
emit
(
'update:dialogVisible'
,
false
)
emit
(
'change'
,
courseCover
.
value
)
}
// 封面
const
courseCover
=
ref
(
''
)
// 课程封面图轮播
let
swiperCovers
:
[{
id
:
string
;
url
:
string
}[]]
=
$ref
([[]])
// 获取swiper 自定义左右切换按钮
let
swiper
=
ref
()
const
swiperChange
=
(
type
?:
string
)
=>
{
type
===
'prev'
?
swiper
.
value
.
prev
()
:
swiper
.
value
.
next
()
}
// 获取封面
getCoverList
().
then
(
res
=>
{
const
filtersData
=
res
.
data
.
list
.
filter
((
i
:
any
)
=>
i
.
type
===
'1'
)
let
index
=
0
while
(
index
<
filtersData
.
length
)
{
swiperCovers
.
push
(
filtersData
.
slice
(
index
,
(
index
+=
8
)))
}
swiperCovers
.
forEach
((
item
:
any
,
index
:
number
)
=>
{
if
(
item
.
length
===
0
)
{
swiperCovers
.
splice
(
index
,
1
)
}
})
console
.
log
(
swiperCovers
,
'123'
)
})
let
isSwiperBtn
=
$ref
(
0
)
const
watchSwiper
=
(
index
:
number
)
=>
{
if
(
!
index
)
{
isSwiperBtn
=
0
}
else
{
swiperCovers
.
length
===
index
+
1
?
(
isSwiperBtn
=
1
)
:
(
isSwiperBtn
=
2
)
}
}
const
swiperItemHandle
=
(
url
:
string
)
=>
{
courseCover
.
value
=
url
}
</
script
>
<
template
>
<div
class=
"add-cover"
>
<el-dialog
:model-value=
"dialogVisible"
:before-close=
"handleCancel"
title=
"选择封面"
width=
"770px"
>
<div
class=
"video-cover"
>
<div
class=
"img-box"
>
<el-icon
:size=
"50"
color=
"#ccc"
v-if=
"courseCover == ''"
>
<PictureFilled></PictureFilled>
</el-icon>
<div
v-else
class=
"cover-img"
:style=
"`background-image:url($
{courseCover})`">
</div>
</div>
<div
class=
"video-cover_right"
>
<UploadImg
accept=
".jpg,.jpeg,.gif,.png"
v-model=
"courseCover"
></UploadImg>
<div
class=
"list"
>
<div
class=
"item"
>
<img
src=
"https://webapp-pub.oss-cn-beijing.aliyuncs.com/center_resource/upload-video-icon.png"
class=
"icon"
/>
<div
class=
"text"
>
该图片作为视频的封面图,用于视频封面显示
</div>
</div>
<div
class=
"item"
>
<img
src=
"https://webapp-pub.oss-cn-beijing.aliyuncs.com/center_resource/upload-video-icon.png"
class=
"icon"
/>
<div
class=
"text"
>
支持jpg/jpeg/gif/png格式
</div>
</div>
<div
class=
"item"
>
<img
src=
"https://webapp-pub.oss-cn-beijing.aliyuncs.com/center_resource/upload-video-icon.png"
class=
"icon"
/>
<div
class=
"text"
>
支持小于4M,最好宽240*高175*及以上尺寸
</div>
</div>
<div
class=
"item"
>
<img
src=
"https://webapp-pub.oss-cn-beijing.aliyuncs.com/center_resource/upload-video-icon.png"
class=
"icon"
/>
<div
class=
"text"
>
可以自己上传图片,也可以从下面这些图片中选择使用
</div>
</div>
</div>
</div>
</div>
<div
class=
"swiper-box"
>
<div
class=
"block text-center"
v-if=
"swiperCovers.length"
>
<el-carousel
@
change=
"watchSwiper"
height=
"202px"
:autoplay=
"false"
arrow=
"never"
trigger=
"click"
ref=
"swiper"
>
<el-carousel-item
v-for=
"(item, index) in swiperCovers"
:key=
"index"
class=
"cover-list"
>
<div
@
click=
"swiperItemHandle(cItem.url)"
:key=
"cItem.id"
v-for=
"cItem in item"
:style=
"`background-image:url($
{cItem.url})`"
class="cover-list_item"
>
</div>
</el-carousel-item>
</el-carousel>
</div>
<el-icon
v-if=
"isSwiperBtn != 0"
class=
"arrow left"
@
click=
"swiperChange('prev')"
><ArrowLeftBold
/></el-icon>
<el-icon
v-if=
"isSwiperBtn != 1"
class=
"arrow right"
@
click=
"swiperChange('next')"
><ArrowRightBold
/></el-icon>
</div>
<template
#
footer
>
<span
class=
"dialog-footer"
>
<el-button
@
click=
"handleCancel"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"changeCover"
>
确认
</el-button>
</span>
</
template
>
</el-dialog>
</div>
</template>
<
style
lang=
"scss"
>
.video-cover
{
display
:
flex
;
.img-box
{
width
:
208px
;
height
:
130px
;
border-radius
:
4px
;
background
:
#f7f7f7
;
font-size
:
20px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
// img {
// width: 100%;
// // height: 100%;
// display: block;
// }
.cover-img
{
width
:
208px
;
height
:
130px
;
background-size
:
cover
;
}
}
.video-cover_right
{
margin-left
:
20px
;
.list
{
.item
{
display
:
flex
;
align-items
:
center
;
margin-top
:
12px
;
img
{
width
:
9px
;
display
:
block
;
margin-right
:
5px
;
}
.text
{
font-size
:
12px
;
color
:
#999999
;
line-height
:
100%
;
}
}
}
}
}
.swiper-box
{
min-width
:
660px
;
margin-top
:
20px
;
padding
:
0
40px
;
position
:
relative
;
.arrow
{
position
:
absolute
;
top
:
50%
;
transform
:
translateY
(
-50%
);
font-size
:
30px
;
color
:
#d5d5d5
;
cursor
:
pointer
;
&
.left
{
left
:
0
;
}
&
.right
{
right
:
10px
;
}
}
.cover-list
{
display
:
flex
;
flex-wrap
:
wrap
;
.cover-list_item
{
display
:
block
;
width
:
155px
;
height
:
96px
;
margin
:
0
10px
10px
0
;
border-radius
:
4px
;
cursor
:
pointer
;
background-size
:
cover
;
}
}
}
:deep
(
.el-carousel__indicators--horizontal
)
{
display
:
none
;
}
</
style
>
src/modules/course/create/components/AddExam.vue
0 → 100644
浏览文件 @
a5393832
<
script
setup
lang=
"ts"
>
import
{
searchExam
}
from
'../api'
const
emit
=
defineEmits
([
'change'
])
let
lecturerList
:
any
=
ref
([])
const
listOptions
=
computed
(()
=>
{
return
{
columns
:
[
// { label: '', slots: 'table-avatar', align: 'center' },
{
label
:
'试卷名称'
,
prop
:
'paper_title'
,
align
:
'center'
},
{
label
:
'组卷模式'
,
prop
:
'paper_type'
,
align
:
'center'
,
computed
({
row
}:
any
)
{
const
json
=
{
1
:
'选题组卷'
,
2
:
'自动组卷'
}
return
json
[
parseInt
(
row
.
paper_type
)]
}
},
{
label
:
'试卷用途'
,
prop
:
'paper_uses'
,
align
:
'center'
,
computed
({
row
}:
any
)
{
const
json
=
{
1
:
'考试'
,
2
:
'课后作业'
,
3
:
'课程测试'
}
return
json
[
parseInt
(
row
.
paper_uses
)]
}
},
{
label
:
'总分'
,
prop
:
'paper_total_score'
,
align
:
'center'
},
{
label
:
'及格分数'
,
prop
:
'pass_score'
,
align
:
'center'
},
{
label
:
'操作'
,
slots
:
'table-operate'
,
align
:
'center'
}
],
data
:
lecturerList
.
value
}
})
// 试卷弹窗
const
dialogVisible
=
ref
(
false
)
interface
ListItem
{
paper_title
:
string
id
:
string
}
const
options
=
ref
<
ListItem
[]
>
([])
const
loading
=
ref
(
false
)
// 所有被搜索出来的值
const
allLecturers
:
any
=
ref
([])
// 考试选中的值
const
lecturerValue
=
ref
([])
// 远程搜索
const
remoteMethod
=
(
query
:
string
)
=>
{
if
(
query
)
{
loading
.
value
=
true
searchExam
({
paper_title
:
query
,
limit
:
'100'
,
is_all
:
1
,
project_prefix
:
'x1'
,
signature
:
'UG7wBenexQhiuD2wpCwuxkU0jqcj006d'
,
timestamp
:
'32322323'
,
nonce
:
'3232'
}).
then
((
res
:
any
)
=>
{
loading
.
value
=
false
options
.
value
=
res
.
data
.
data
.
filter
((
item
:
any
)
=>
{
return
!
lecturerValue
.
value
.
find
((
id
:
string
)
=>
id
===
item
.
id
)
})
options
.
value
.
forEach
((
item
:
any
)
=>
{
const
findItem
=
allLecturers
.
value
.
find
((
cItem
:
any
)
=>
cItem
.
id
===
item
.
id
)
if
(
!
findItem
)
{
allLecturers
.
value
.
push
(
item
)
}
})
})
}
else
{
options
.
value
=
[]
}
}
const
changeValue
=
()
=>
{
lecturerList
.
value
=
lecturerValue
.
value
.
reduce
((
a
:
any
,
b
:
any
)
=>
{
a
.
push
(
allLecturers
.
value
.
find
((
item
:
any
)
=>
item
.
id
===
b
))
return
a
},
[])
}
const
changeLecturer
=
()
=>
{
changeValue
()
emit
(
'change'
,
lecturerValue
.
value
)
dialogVisible
.
value
=
false
}
const
dialogShow
=
()
=>
{
options
.
value
=
[]
dialogVisible
.
value
=
true
}
// 删除考试
const
removeLectuter
=
(
id
:
string
)
=>
{
const
index
=
lecturerValue
.
value
.
findIndex
((
ids
:
string
)
=>
ids
===
id
)
lecturerValue
.
value
.
splice
(
index
,
1
)
changeValue
()
emit
(
'change'
,
lecturerValue
.
value
)
}
</
script
>
<
template
>
<div>
<el-button
type=
"primary"
@
click=
"dialogShow"
>
添加课程考试
</el-button>
<AppList
v-bind=
"listOptions"
ref=
"appList"
>
<template
#
table-avatar=
"
{ row }">
<img
:src=
"row.avatar"
style=
"width: 50px; height: 50px; display: block; margin: 0 auto"
/>
</
template
>
<
template
#
table-summarize=
"{ row }"
>
<div
v-html=
"row.summarize"
></div>
</
template
>
<
template
#
table-operate=
"{ row }"
>
<el-button
plain
@
click=
"removeLectuter(row.id)"
>
删除
</el-button>
</
template
>
</AppList>
<el-dialog
v-model=
"dialogVisible"
width=
"400px"
title=
"添加考试"
>
<div
style=
"display: flex; justify-content: center"
>
<el-select
v-model=
"lecturerValue"
multiple
filterable
remote
reserve-keyword
placeholder=
"请输入试卷名称"
:remote-method=
"remoteMethod"
:loading=
"loading"
>
<el-option
v-for=
"item in options"
:key=
"item.id"
:label=
"item.paper_title"
:value=
"item.id"
/>
</el-select>
</div>
<
template
#
footer
>
<span
class=
"dialog-footer"
>
<el-button
@
click=
"dialogVisible = false"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"changeLecturer"
>
确认
</el-button>
</span>
</
template
>
</el-dialog>
</div>
</template>
<
style
lang=
"scss"
></
style
>
src/modules/course/create/components/AddLecturer.vue
0 → 100644
浏览文件 @
a5393832
<
script
setup
lang=
"ts"
>
import
{
searchLecturer
}
from
'../api'
const
emit
=
defineEmits
([
'change'
])
let
lecturerList
:
any
=
ref
([])
const
listOptions
=
computed
(()
=>
{
return
{
columns
:
[
{
label
:
'头像'
,
slots
:
'table-avatar'
,
align
:
'center'
},
{
label
:
'姓名'
,
prop
:
'name'
,
align
:
'center'
},
{
label
:
'职位'
,
prop
:
'title'
,
align
:
'center'
},
{
label
:
'机构'
,
prop
:
'office'
,
align
:
'center'
},
{
label
:
'简介'
,
slots
:
'table-summarize'
,
align
:
'center'
},
{
label
:
'操作'
,
slots
:
'table-operate'
,
align
:
'center'
}
],
data
:
lecturerList
.
value
}
})
// 讲师弹窗
const
dialogVisible
=
ref
(
false
)
interface
ListItem
{
name
:
string
id
:
string
}
const
options
=
ref
<
ListItem
[]
>
([])
const
loading
=
ref
(
false
)
// 所有被搜索出来的值
const
allLecturers
:
any
=
ref
([])
// 讲师选中的值
const
lecturerValue
=
ref
([])
// 远程搜索
const
remoteMethod
=
(
query
:
string
)
=>
{
if
(
query
)
{
loading
.
value
=
true
searchLecturer
({
name
:
query
,
'per-page'
:
'100'
}).
then
((
res
:
any
)
=>
{
loading
.
value
=
false
options
.
value
=
res
.
data
.
list
.
filter
((
item
:
any
)
=>
{
return
!
lecturerValue
.
value
.
find
((
id
:
string
)
=>
id
===
item
.
id
)
})
options
.
value
.
forEach
((
item
:
any
)
=>
{
const
findItem
=
allLecturers
.
value
.
find
((
cItem
:
any
)
=>
cItem
.
id
===
item
.
id
)
if
(
!
findItem
)
{
allLecturers
.
value
.
push
(
item
)
}
})
})
}
else
{
options
.
value
=
[]
}
}
const
changeValue
=
()
=>
{
lecturerList
.
value
=
lecturerValue
.
value
.
reduce
((
a
:
any
,
b
:
any
)
=>
{
a
.
push
(
allLecturers
.
value
.
find
((
item
:
any
)
=>
item
.
id
===
b
))
return
a
},
[])
}
const
changeLecturer
=
()
=>
{
changeValue
()
emit
(
'change'
,
lecturerValue
.
value
)
dialogVisible
.
value
=
false
}
const
dialogShow
=
()
=>
{
options
.
value
=
[]
dialogVisible
.
value
=
true
}
// 删除讲师
const
removeLectuter
=
(
id
:
string
)
=>
{
const
index
=
lecturerValue
.
value
.
findIndex
((
ids
:
string
)
=>
ids
===
id
)
lecturerValue
.
value
.
splice
(
index
,
1
)
changeValue
()
emit
(
'change'
,
lecturerValue
.
value
)
}
</
script
>
<
template
>
<div>
<el-button
type=
"primary"
@
click=
"dialogShow"
>
添加讲师
</el-button>
<AppList
v-bind=
"listOptions"
ref=
"appList"
>
<template
#
table-avatar=
"
{ row }">
<img
:src=
"row.avatar"
style=
"width: 50px; height: 50px; display: block; margin: 0 auto"
/>
</
template
>
<
template
#
table-summarize=
"{ row }"
>
<div
v-html=
"row.summarize"
></div>
</
template
>
<
template
#
table-operate=
"{ row }"
>
<el-button
plain
@
click=
"removeLectuter(row.id)"
>
删除
</el-button>
</
template
>
</AppList>
<el-dialog
v-model=
"dialogVisible"
width=
"400px"
title=
"添加讲师"
>
<div
style=
"display: flex; justify-content: center"
>
<el-select
v-model=
"lecturerValue"
multiple
filterable
remote
reserve-keyword
placeholder=
"请输入讲师姓名"
:remote-method=
"remoteMethod"
:loading=
"loading"
>
<el-option
v-for=
"item in options"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
</div>
<
template
#
footer
>
<span
class=
"dialog-footer"
>
<el-button
@
click=
"dialogVisible = false"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"changeLecturer"
>
确认
</el-button>
</span>
</
template
>
</el-dialog>
</div>
</template>
<
style
lang=
"scss"
></
style
>
src/modules/course/create/components/AddLive.vue
0 → 100644
浏览文件 @
a5393832
<
script
setup
lang=
"ts"
>
import
{
searchLive
}
from
'../api'
const
emit
=
defineEmits
([
'change'
])
let
lecturerList
:
any
=
ref
([])
const
listOptions
=
computed
(()
=>
{
return
{
columns
:
[
{
label
:
'会议code'
,
prop
:
'meeting_code'
,
align
:
'center'
},
{
label
:
'主题'
,
prop
:
'subject'
,
align
:
'center'
},
{
label
:
'开始时间'
,
prop
:
'start_time'
,
align
:
'center'
},
{
label
:
'结束时间'
,
prop
:
'end_time'
,
align
:
'center'
},
{
label
:
'操作'
,
slots
:
'table-operate'
,
align
:
'center'
}
],
data
:
lecturerList
.
value
}
})
// 直播弹窗
const
dialogVisible
=
ref
(
false
)
interface
ListItem
{
subject
:
string
id
:
string
}
const
options
=
ref
<
ListItem
[]
>
([])
const
loading
=
ref
(
false
)
// 所有被搜索出来的值
const
allLecturers
:
any
=
ref
([])
// 直播选中的值
const
lecturerValue
=
ref
([])
// 远程搜索
const
remoteMethod
=
(
query
:
string
)
=>
{
if
(
query
)
{
loading
.
value
=
true
searchLive
({
name
:
query
,
'per-page'
:
'100'
}).
then
((
res
:
any
)
=>
{
loading
.
value
=
false
options
.
value
=
res
.
data
.
list
.
filter
((
item
:
any
)
=>
{
return
!
lecturerValue
.
value
.
find
((
id
:
string
)
=>
id
===
item
.
id
)
})
options
.
value
.
forEach
((
item
:
any
)
=>
{
const
findItem
=
allLecturers
.
value
.
find
((
cItem
:
any
)
=>
cItem
.
id
===
item
.
id
)
if
(
!
findItem
)
{
allLecturers
.
value
.
push
(
item
)
}
})
})
}
else
{
options
.
value
=
[]
}
}
const
changeValue
=
()
=>
{
lecturerList
.
value
=
lecturerValue
.
value
.
reduce
((
a
:
any
,
b
:
any
)
=>
{
a
.
push
(
allLecturers
.
value
.
find
((
item
:
any
)
=>
item
.
id
===
b
))
return
a
},
[])
}
const
changeLecturer
=
()
=>
{
changeValue
()
emit
(
'change'
,
lecturerValue
.
value
)
dialogVisible
.
value
=
false
}
const
dialogShow
=
()
=>
{
options
.
value
=
[]
dialogVisible
.
value
=
true
}
// 删除直播
const
removeLectuter
=
(
id
:
string
)
=>
{
const
index
=
lecturerValue
.
value
.
findIndex
((
ids
:
string
)
=>
ids
===
id
)
lecturerValue
.
value
.
splice
(
index
,
1
)
changeValue
()
emit
(
'change'
,
lecturerValue
.
value
)
}
</
script
>
<
template
>
<div>
<el-button
type=
"primary"
@
click=
"dialogShow"
>
添加周期性直播
</el-button>
<AppList
v-bind=
"listOptions"
ref=
"appList"
>
<template
#
table-avatar=
"
{ row }">
<img
:src=
"row.avatar"
style=
"width: 50px; height: 50px; display: block; margin: 0 auto"
/>
</
template
>
<
template
#
table-summarize=
"{ row }"
>
<div
v-html=
"row.summarize"
></div>
</
template
>
<
template
#
table-operate=
"{ row }"
>
<el-button
plain
@
click=
"removeLectuter(row.id)"
>
删除
</el-button>
</
template
>
</AppList>
<el-dialog
v-model=
"dialogVisible"
width=
"400px"
title=
"添加直播"
>
<div
style=
"display: flex; justify-content: center"
>
<el-select
v-model=
"lecturerValue"
multiple
filterable
remote
reserve-keyword
placeholder=
"会议主题或者会议code"
:remote-method=
"remoteMethod"
:loading=
"loading"
>
<el-option
v-for=
"item in options"
:key=
"item.id"
:label=
"item.subject"
:value=
"item.id"
/>
</el-select>
</div>
<
template
#
footer
>
<span
class=
"dialog-footer"
>
<el-button
@
click=
"dialogVisible = false"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"changeLecturer"
>
确认
</el-button>
</span>
</
template
>
</el-dialog>
</div>
</template>
<
style
lang=
"scss"
></
style
>
src/modules/course/create/components/StepOne.vue
0 → 100644
浏览文件 @
a5393832
<
script
setup
lang=
"ts"
>
import
{
createCourse
}
from
'../api'
import
{
useGetCategoryList
}
from
'@/composables/useGetCategoryList'
import
{
useMapStore
}
from
'@/stores/map'
import
VEditor
from
'@/components/tinymce/Index.vue'
// 课程封面
import
AddCourseCover
from
'./AddCourseCover.vue'
// 添加讲师
import
AddLecturer
from
'./AddLecturer.vue'
// 添加试卷
import
AddExam
from
'./AddExam.vue'
// 添加直播
import
AddLive
from
'./AddLive.vue'
const
store
=
useMapStore
()
interface
ICourseList
{
label
:
string
,
value
:
string
}
// 课程类型
const
courseType
=
computed
<
ICourseList
[]
>
(()
=>
{
return
store
.
getMapValuesByKey
(
'system_online_type'
)
})
// 课程类型
const
electiveType
=
computed
<
ICourseList
[]
>
(()
=>
{
return
store
.
getMapValuesByKey
(
'system_elective_type'
)
})
// 下拉选择tree 视频分类
let
{
list
:
selectTree
}
=
useGetCategoryList
()
const
defaultProps
=
{
children
:
'children'
,
label
:
'category_name'
,
value
:
'id'
}
// form
const
form
=
reactive
<
Record
<
string
,
any
>>
({
source
:
'2'
,
cover
:
'https://webapp-pub.oss-cn-beijing.aliyuncs.com/center_resource/course-cover.png'
})
// 表单验证
const
rules
=
{
name
:
[{
required
:
true
,
message
:
'请输入标题'
}],
online_type
:
[{
required
:
true
,
message
:
'请选择类型'
}],
elective_type
:
[{
required
:
true
,
message
:
'请选择类型'
}],
classification
:
[{
required
:
true
,
message
:
'请选择分类'
}],
credit
:
[{
required
:
true
,
message
:
'请输入学分'
}],
lecturer_id
:
[{
required
:
true
,
message
:
''
}],
exam_id
:
[{
required
:
true
,
message
:
''
}],
live_id
:
[{
required
:
true
,
message
:
''
}]
}
// 课程封面dialog
const
dialogVisible
=
ref
(
false
)
// 选择封面
const
coverChange
=
(
url
:
string
)
=>
{
form
.
cover
=
url
}
// 选择讲师
const
changeLecturer
=
(
data
:
any
)
=>
{
form
.
lecturer_id
=
data
.
toString
()
}
// 选择考试
const
changeExam
=
(
data
:
any
)
=>
{
form
.
exam_id
=
data
.
toString
()
}
// 选择直播
const
changeLive
=
(
data
:
any
)
=>
{
form
.
live_id
=
data
.
toString
()
}
// 第一步保存后的id
const
stepOneId
=
ref
()
// 新建课件
const
createCourseForm
=
()
=>
{
createCourse
(
form
).
then
((
res
:
any
)
=>
{
if
(
res
.
code
===
0
)
{
// 操作第二部
stepOneId
.
value
=
res
.
data
.
id
}
})
}
</
script
>
<
template
>
<!-- 基本信息 -->
<div
class=
"update-course-info"
>
<div
style=
"display: flex"
>
<div
class=
"cover"
:style=
"`background-image: url($
{form.cover})`">
<div
class=
"upload-btn"
@
click=
"dialogVisible = true"
>
添加封面
</div>
</div>
<el-form
ref=
"ruleFormRef"
:model=
"form"
:rules=
"rules"
style=
"width: 50%"
>
<el-form-item
label=
"课程名称:"
prop=
"name"
>
<el-input
v-model=
"form.name"
maxlength=
"40"
/>
</el-form-item>
<el-form-item
label=
"课程类型:"
prop=
"online_type"
>
<el-radio-group
v-model=
"form.online_type"
>
<el-radio
:key=
"item.value"
:label=
"item.value"
v-for=
"item in courseType"
>
{{
item
.
label
}}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"选课类型:"
prop=
"elective_type"
>
<el-radio-group
v-model=
"form.elective_type"
>
<el-radio
:key=
"item.value"
:label=
"item.value"
v-for=
"item in electiveType"
>
{{
item
.
label
}}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"课程分类:"
prop=
"classification"
>
<el-tree-select
:render-after-expand=
"false"
:props=
"defaultProps"
style=
"width: 100%"
v-model=
"form.classification"
:data=
"selectTree"
/>
</el-form-item>
<el-form-item
label=
"课程学分:"
prop=
"credit"
>
<el-input
v-model=
"form.credit"
maxlength=
"40"
/>
</el-form-item>
</el-form>
</div>
<el-form
ref=
"ruleFormRef"
label-position=
"top"
:model=
"form"
:rules=
"rules"
style=
"width: 100%; margin-top: 30px"
>
<el-form-item
label=
"课程讲师:"
prop=
"lecturer_id"
>
<!-- 添加讲师 -->
<AddLecturer
@
change=
"changeLecturer"
style=
"width: 100%"
></AddLecturer>
</el-form-item>
<el-form-item
label=
"课程简介与描述:"
>
<v-editor
v-model=
"form.represent"
class=
"editor"
:height=
"200"
></v-editor>
</el-form-item>
<el-form-item
label=
"课程小论文:"
>
<v-editor
v-model=
"form.essay"
class=
"editor"
:height=
"200"
></v-editor>
</el-form-item>
<el-form-item
label=
"前期准备与预备知识:"
>
<v-editor
v-model=
"form.previous_preparation"
class=
"editor"
:height=
"200"
></v-editor>
</el-form-item>
<el-form-item
label=
"授课目标:"
>
<v-editor
v-model=
"form.target"
class=
"editor"
:height=
"200"
></v-editor>
</el-form-item>
<el-form-item
label=
"课程考试:"
prop=
"exam_id"
>
<!-- 添加考试 -->
<AddExam
@
change=
"changeExam"
style=
"width: 100%"
></AddExam>
</el-form-item>
<el-form-item
label=
"课程考试:"
prop=
"live_id"
>
<!-- 添加直播 -->
<AddLive
@
change=
"changeLive"
style=
"width: 100%"
></AddLive>
</el-form-item>
</el-form>
</div>
<div
class=
"btn-box"
style=
"display: flex;justify-content: center;"
>
<el-button
type=
"primary"
@
click=
"createCourseForm"
>
下一步
</el-button>
</div>
<!-- 添加封面 -->
<AddCourseCover
@
change=
"coverChange"
v-model:dialogVisible=
"dialogVisible"
></AddCourseCover>
</
template
>
<
style
lang=
"scss"
>
.update-course-info
{
.cover
{
width
:
430px
;
height
:
270px
;
background-size
:
cover
;
background-image
:
url('https://iph.href.lu/430x270')
;
margin-right
:
20px
;
position
:
relative
;
.upload-btn
{
position
:
absolute
;
bottom
:
10px
;
right
:
10px
;
padding
:
5px
10px
;
font-size
:
14px
;
border-radius
:
25px
;
text-align
:
center
;
background
:
rgba
(
0
,
0
,
0
,
0
.3
);
color
:
#fff
;
cursor
:
pointer
;
}
}
}
</
style
>
src/modules/course/create/components/UploadImg.vue
0 → 100644
浏览文件 @
a5393832
<
script
lang=
"ts"
setup
>
import
{
ElMessage
}
from
'element-plus'
// import { Plus } from '@element-plus/icons-vue'
import
type
{
UploadProps
,
UploadUserFile
}
from
'element-plus'
import
md5
from
'blueimp-md5'
import
{
getSignature
}
from
'@/api/base'
const
props
=
withDefaults
(
defineProps
<
{
modelValue
:
string
|
[];
prefix
?:
string
}
>
(),
{
prefix
:
'upload/admin/'
})
const
emit
=
defineEmits
([
'update:modelValue'
])
const
uploadData
=
ref
()
const
fileList
=
ref
<
UploadUserFile
[]
>
([])
watch
(
()
=>
props
.
modelValue
,
value
=>
{
fileList
.
value
=
Array
.
isArray
(
value
)
?
[...
value
]
:
[]
}
)
const
showFileList
=
computed
(()
=>
{
return
Array
.
isArray
(
props
.
modelValue
)
})
// 上传之前
const
handleBeforeUpload
=
async
(
file
:
any
)
=>
{
const
fileName
=
file
.
name
const
key
=
props
.
prefix
+
md5
(
fileName
+
new
Date
().
getTime
())
+
fileName
.
substr
(
fileName
.
lastIndexOf
(
'.'
))
const
response
:
Record
<
string
,
any
>
=
await
getSignature
()
uploadData
.
value
=
{
key
,
OSSAccessKeyId
:
response
.
accessid
,
policy
:
response
.
policy
,
signature
:
response
.
signature
,
success_action_status
:
'200'
,
url
:
`
${
response
.
host
}
/
${
key
}
`
}
file
.
url
=
`
${
response
.
host
}
/
${
key
}
`
}
// 上传成功
const
handleSuccess
=
(
response
:
any
,
file
:
any
,
files
:
any
)
=>
{
if
(
!
files
.
every
((
item
:
any
)
=>
item
.
status
===
'success'
))
return
if
(
showFileList
.
value
)
{
emit
(
'update:modelValue'
,
files
.
map
((
item
:
any
)
=>
{
return
{
name
:
item
.
name
,
url
:
item
.
url
||
item
.
raw
.
url
}
})
)
}
else
{
emit
(
'update:modelValue'
,
file
.
raw
.
url
)
}
}
// 上传限制
const
handleExceed
:
UploadProps
[
'onExceed'
]
=
()
=>
{
ElMessage
.
warning
(
'文件超出个数限制'
)
}
// 删除
const
handleRemove
:
UploadProps
[
'onRemove'
]
=
(
file
,
files
)
=>
{
emit
(
'update:modelValue'
,
files
.
map
((
item
:
any
)
=>
{
return
{
name
:
item
.
name
,
url
:
item
.
url
||
item
.
raw
.
url
}
})
)
}
// 预览
const
handlePreview
:
UploadProps
[
'onPreview'
]
=
uploadFile
=>
{
console
.
log
(
uploadFile
)
}
</
script
>
<
template
>
<el-upload
action=
"https://webapp-pub.oss-cn-beijing.aliyuncs.com"
:data=
"uploadData"
:show-file-list=
"false"
:before-upload=
"handleBeforeUpload"
:on-exceed=
"handleExceed"
:on-remove=
"handleRemove"
:on-preview=
"handlePreview"
:on-success=
"handleSuccess"
:file-list=
"fileList"
class=
"uploader"
>
<el-button
type=
"primary"
>
选择本地图片
</el-button>
</el-upload>
</
template
>
<
style
lang=
"scss"
>
.uploader
{
flex
:
1
;
}
.avatar-uploader
{
width
:
178px
;
height
:
178px
;
border
:
1px
dashed
var
(
--
el-border-color
);
border-radius
:
6px
;
cursor
:
pointer
;
position
:
relative
;
overflow
:
hidden
;
transition
:
var
(
--
el-transition-duration-fast
);
.el-image
{
width
:
100%
;
height
:
100%
;
}
}
.avatar-uploader
:hover
{
border-color
:
var
(
--
el-color-primary
);
}
.avatar-uploader-icon
{
font-size
:
28px
;
color
:
#8c939d
;
width
:
100%
;
height
:
100%
;
text-align
:
center
;
}
</
style
>
src/modules/course/create/index.ts
0 → 100644
浏览文件 @
a5393832
import
type
{
RouteRecordRaw
}
from
'vue-router'
import
AppLayout
from
'@/components/layout/Index.vue'
export
const
routes
:
Array
<
RouteRecordRaw
>
=
[
{
path
:
'/course/create'
,
component
:
AppLayout
,
children
:
[
{
path
:
''
,
component
:
()
=>
import
(
'./views/Create.vue'
)
}
]
}
]
src/modules/course/create/views/Create.vue
0 → 100644
浏览文件 @
a5393832
<
script
setup
lang=
"ts"
>
// 第一步
import
StepOne
from
'../components/StepOne.vue'
// is 编辑 新建
const
route
=
useRoute
()
const
isUpdate
=
$computed
(()
=>
{
return
!!
route
.
query
.
id
})
</
script
>
<
template
>
<AppCard
:title=
"isUpdate ? '编辑课程' : '新建课程'"
>
<StepOne></StepOne>
</AppCard>
</
template
>
<
style
lang=
"scss"
>
</
style
>
vite.config.ts
浏览文件 @
a5393832
...
@@ -26,6 +26,11 @@ export default defineConfig(({ mode }) => ({
...
@@ -26,6 +26,11 @@ export default defineConfig(({ mode }) => ({
cert
:
fs
.
readFileSync
(
path
.
join
(
__dirname
,
'./https/dev.ezijing.com.pem'
))
cert
:
fs
.
readFileSync
(
path
.
join
(
__dirname
,
'./https/dev.ezijing.com.pem'
))
},
},
proxy
:
{
proxy
:
{
'/api/qbs'
:
{
target
:
'https://question-api.ezijing.com'
,
changeOrigin
:
true
,
rewrite
:
path
=>
path
.
replace
(
/^
\/
api
\/
qbs/
,
''
)
},
'/api'
:
'https://resource-center.ezijing.com'
'/api'
:
'https://resource-center.ezijing.com'
}
}
},
},
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论