参考:https://panjiachen.gitee.io/vue-element-admin/#/example/create
Tinymce是一个传统javascript插件,默认不能用于Vue.js因此需要做一些特殊的整合步骤
将脚本库复制到项目的static目录下(在vue-element-admin-master的static路径下)
在 guli-admin/build/webpack.dev.conf.js 中添加配置
使在html页面中可是使用这里定义的BASE_URL变量
new HtmlWebpackPlugin({
......,
templateParameters: {
BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
}
})
在guli-admin/index.html 中引入js脚本
为了让Tinymce能用于Vue.js项目,vue-element-admin-master对Tinymce进行了封装,下面我们将它引入到我们的课程信息页面
src/components/Tinymce
课程信息组件中引入 Tinymce
//引入Tinymce富文本编辑器组件
import Tinymce from '@/components/Tinymce';
export default {
....
components: {
Tinymce },
}
<el-form-item label="课程简介">
<tinymce :height="300" v-model="courseInfo.description"/>
el-form-item>
在info.vue文件的最后添加如下代码,调整上传图片按钮的高度
图片的base64编码
Tinymce中的图片上传功能直接存储的是图片的base64编码,因此无需图片服务器
要求跟前端对应的数据名字一致,不然无法获取
@ApiModelProperty(value = "一级分类ID")
private String subjectParentId;
章节和小节,在章节实体类使用list表示小节
@Data
public class VideoVo implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String title;
private Boolean free;
}
@Data
public class ChapterVo implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String title;
//表示小节
private List<VideoVo> children = new ArrayList<VideoVo>();
}
@RestController
@RequestMapping("/eduservice/edu-chapter")
public class EduChapterController {
@Autowired
private EduChapterService eduChapterService;
//获取课程大纲列表,根据课程id进行查询
@GetMapping("/getChapterVideo/{courseId}")
public R getChapterVideo(@PathVariable String courseId){
List<ChapterVo> list = eduChapterService.getChapterVideoByCourseId(courseId);
return R.ok().data("allChapterVideo",list);
}
}
public interface EduChapterService extends IService<EduChapter> {
List<ChapterVo> getChapterVideoByCourseId(String courseId);
}
@Service
public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService {
@Autowired
private EduVideoService eduVideoService;
@Override
public List<ChapterVo> getChapterVideoByCourseId(String courseId) {
//最终要的数据列表
ArrayList<ChapterVo> finalChapterVos = new ArrayList<>();
//查询章节信息
QueryWrapper<EduChapter> wrapper = new QueryWrapper<>();
wrapper.eq("course_id",courseId);
List<EduChapter> eduChapters = baseMapper.selectList(wrapper);
//查询小节信息
QueryWrapper<EduVideo> wrapper1 = new QueryWrapper<>();
wrapper1.eq("course_id",courseId);
List<EduVideo> eduVideos = eduVideoService.list(wrapper1);
//填充章节vo数据
for (int i = 0; i < eduChapters.size(); i++) {
EduChapter chapter = eduChapters.get(i);
//创建章节vo对象
ChapterVo chapterVo = new ChapterVo();
BeanUtils.copyProperties(chapter,chapterVo);
finalChapterVos.add(chapterVo);
//填充课时vo对象
ArrayList<VideoVo> finalVideoVos = new ArrayList<>();
for (int j = 0; j < eduVideos.size(); j++) {
EduVideo video = eduVideos.get(j);
if (chapter.getId().equals(video.getChapterId())){
VideoVo videoVo = new VideoVo();
BeanUtils.copyProperties(video,videoVo);
finalVideoVos.add(videoVo);
}
}
chapterVo.setChildren(finalVideoVos);
}
return finalChapterVos;
}
}
chapter.js
import request from '@/utils/request' //引入已经封装好的axios 和 拦截器
export default{
//根据课程id获取章节和小节数据列表
getChapterVideoByCourseId(courseId){
return request({
url:`/eduservice/edu-chapter/getChapterVideo/${
courseId}`,
method: 'get',
})
},
}
import chapter from '@/api/teacher/chapter.js';
export default {
data() {
return {
courseId:'',
chapterVideoList:[],
....
};
},
methods: {
//根据课程id查询对应的课程章节和小结
getChapterVideoByCourseId(){
chapter.getChapterVideoByCourseId(this.courseId)
.then(resp=>{
this.chapterVideoList = resp.data.allChapterVideo
})
},
...
},
created() {
//获取路由里的id值
if(this.$route.params && this.$route.params.id){
this.courseId = this.¥route.params.id
}
//根据课程id查询对应的课程章节和小结
this.getChapterVideoByCourseId();
},
};
</script>
<ul>
<li v-for="chapter in chapterVideoList" :key="chapter.id">
<p>
{
{ chapter.title }}
<span>
<el-button type="text">添加课时el-button>
<el-button style="" type="text">编辑el-button>
<el-button type="text">删除el-button>
span>
p>
<ul>
<li v-for="video in chapter.children" :key="video.id">
{
{ video.title }}
li>
ul>
li>
<li>
<ul class="chanpterList videoList">
<li v-for="video in chapter.children" :key="video.id">
<p>
{
{ video.title }}
<span class="acts">
<el-button type="text">编辑el-button>
<el-button type="text">删除el-button>
span>
p>
li>
ul>
li>
ul>
//根据课程id查询课程基本信息
@GetMapping("/getCourseInfoById/{courseId}")
public R getCourseInfoById(@PathVariable String courseId){
CourseInfoForm courseInfoForm = eduCourseService.getCourseInfo(courseId);
return R.ok().data("courseInfoForm",courseInfoForm);
}
//根据课程id查询课程基本信息
CourseInfoForm getCourseInfo(String courseId);
//课程描述注入
@Autowired
private EduCourseDescriptionService eduCourseDescriptionService;
@Override
public CourseInfoForm getCourseInfo(String courseId) {
//查询课程表
EduCourse eduCourse = baseMapper.selectById(courseId);
CourseInfoForm courseInfoForm = new CourseInfoForm();
BeanUtils.copyProperties(eduCourse,courseInfoForm);
//查询简介表
EduCourseDescription courseDescription = eduCourseDescriptionService.getById(courseId);
courseInfoForm.setDescription(courseDescription.getDescription());
return courseInfoForm;
}
//修改课程信息
@PostMapping("/updateCourseInfo")
public R updateCourseInfo(@RequestBody CourseInfoForm courseInfoForm){
eduCourseService.updateCourseInfo(courseInfoForm);
return R.ok();
}
//修改课程信息
void updateCourseInfo(CourseInfoForm courseInfoForm);
@Override
public void updateCourseInfo(CourseInfoForm courseInfoForm) {
//1、修改课程表
EduCourse eduCourse = new EduCourse();
BeanUtils.copyProperties(courseInfoForm,eduCourse);
int update = baseMapper.updateById(eduCourse);
if (update <= 0){
throw new AchangException(20001,"修改课程信息失败");
}
//2、修改描述信息
EduCourseDescription eduCourseDescription = new EduCourseDescription();
eduCourseDescription.setDescription(courseInfoForm.getDescription());
eduCourseDescription.setId(courseInfoForm.getId());
eduCourseDescriptionService.updateById(eduCourseDescription);
}
guli-admin\src\api\teacher\course.js
//根据课程id 查询课程基本信息
getCourseInfoById(courseId){
return request({
url:`/eduservice/edu-course/getCourseInfoById/${
courseId}`,
method: 'get',
})
},
//修改课程信息
updateCourseInfo(courseInfoForm){
return request({
url:"/eduservice/edu-course/updateCourseInfo",
method: 'post',
data: courseInfoForm,
})
}
guli-admin\src\views\edu\course\chapter.vue
//跳转到上一步
previous() {
this.$router.push({
path: "/course/info/"+this.courseId});
},
next() {
//跳转到第三步
this.$router.push({
path: "/course/publish/"+this.courseId});
}
created() {
//判断路径中是否有课程id
if (this.$route.params && this.$route.params.id) {
this.courseId = this.$route.params.id;
//根据课程id 查询课程基本信息
this.getCourseInfo()
},
....
}
methods: {
//获取课程信息
getCourseInfo() {
course.getCourseInfoById(this.courseId).then((resp) => {
this.courseInfo = resp.data.courseInfoForm
})
},
....
}
data() {
return {
...
courseId: "",
};
}
created() {
//判断路径中是否有课程id
if (this.$route.params && this.$route.params.id) {
this.courseId = this.$route.params.id;
//根据课程id 查询课程基本信息
this.getCourseInfo();
} else {
//初始化所有讲师
this.getListTeacher();
//初始化一级分类
this.getOneSubject();
}
}
//获取课程信息
getCourseInfo() {
course.getCourseInfoById(this.courseId).then((resp) => {
this.courseInfo = resp.data.courseInfoForm;
//查询所有分类,包含一级和二级所有
subject.getSubjectList().then((resp) => {
//获取所有一级分类
this.subjectOneLists = resp.data.list;
//把所有一级分类数组进行遍历
for (var i = 0; i < this.subjectOneLists.length; i++) {
//获取每个一级分类
var oneSubject = this.subjectOneLists[i];
//比较当前courseInfo里面的一级分类id和所有的一级分类id是否一样
if (this.courseInfo.subjectParentId == oneSubject.id) {
//获取一级分类中所有的二级分类
this.subjectTwoLists = oneSubject.children;
}
}
});
//初始化所有讲师
this.getListTeacher();
});
}
添加监听器,监听路由,如果路由变化,就将courseInfo的数据清空
watch: {
$route(to, from) {
//路由变化方式,当路由发送变化,方法就执行
console.log("watch $route");
this.courseInfo={
}
},
}
guli-admin\src\views\edu\course\info.vue
//添加课程
saveCourse() {
course.addCourseInfo(this.courseInfo).then((resp) => {
this.$message({
message: "添加课程信息成功",
type: "success",
});
//跳转到第二步,并带着这个课程生成的id
this.$router.push({
path: "/course/chapter/" + resp.data.courseId });
});
},
//修改课程
updateCourse() {
course.updateCourseInfo(this.courseInfo).then((resp) => {
this.$message({
message: "修改课程信息成功",
type: "success",
});
//跳转到第二步,并带着这个课程生成的id
this.$router.push({
path: "/course/chapter/" + this.courseId });
});
},
//判断是修改还是新增
saveOrUpdate() {
//判断courseInfo中是否有id值
if (this.courseInfo.id) {
//有id值,为修改
this.updateCourse();
} else {
//没id值,为添加
this.saveCourse();
}
}
//添加章节
@PostMapping("addChapter")
public R addChapter(@RequestBody EduChapter eduChapter){
eduChapterService.save(eduChapter);
return R.ok();
}
//根据章节id查询
@GetMapping("getChapter/{chapterId}")
public R getChapter(@PathVariable String chapterId){
EduChapter eduChapter = eduChapterService.getById(chapterId);
return R.ok().data("chapter",eduChapter);
}
//修改章节
@PostMapping("updateChapter")
public R updateChapter(@RequestBody EduChapter eduChapter){
eduChapterService.updateById(eduChapter);
return R.ok();
}
//删除章节
@DeleteMapping("deleteById/{chapterId}")
public R deleteById(@PathVariable String chapterId){
boolean flag = eduChapterService.deleteChapter(chapterId);
if (flag){
return R.ok();
}else {
return R.error();
}
}
boolean deleteChapter(String chapterId);
//删除章节的方法
@Override
public boolean deleteChapter(String chapterId) {
//根据chapter章节id 查询查询小节表,如果查询有数据,则不删除
QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();
wrapper.eq("chapter_id",chapterId);
int count = eduVideoService.count(wrapper);
//判断
if (count>0){
//能查询出来小节,不进行删除
throw new AchangException(20001,"还有小节数据,不能删除");
}else {
//不能查询出小节,进行删除
int delete = baseMapper.deleteById(chapterId);
return delete>0;
}
}
//添加章节
addChapter(chapter) {
return request({
url: `/eduservice/edu-chapter/addChapter`,
method: `post`,
data: chapter
})
},
//根据id查询章节
updateChapterById(chapterID) {
return request({
url: `/eduservice/edu-chapter/getChapter/${
chapterID}`,
method: `get`,
})
},
//修改章节
updateChapter(chapter) {
return request({
url: `/eduservice/edu-chapter/updateChapter`,
method: `post`,
data: chapter
})
},
//删除章节
deleteById(chapterID) {
return request({
url: `/eduservice/edu-chapter/deleteById/${
chapterID}`,
method: `delete`,
})
}
import chapter from "@/api/teacher/chapter.js";
methods: {
//添加章节
saveChapter() {
//设置课程id到chapter对象中
this.chapter.courseId = this.courseId
chapter.addChapter(this.chapter).then((resp) => {
//关闭弹框
this.dialogChapterFormVisible = false;
//提示信息
this.$message({
message: "添加章节成功",
type: "success",
});
//刷新页面
this.getChapterVideoByCourseId()
});
},
saveOrUpdate() {
this.saveChapter()
}
}
//弹出添加章节表单
openChapterDialog(){
//清空之前的数据
this.chapter={
}
//显示弹框
this.dialogChapterFormVisible = true
}
//修改章节
updateChapter() {
//设置课程id到chapter对象中
this.chapter.courseId = this.courseId;
chapter.updateChapter(this.chapter).then((resp) => {
//关闭弹框
this.dialogChapterFormVisible = false;
//提示信息
this.$message({
message: "修改章节成功",
type: "success",
});
//刷新页面
this.getChapterVideoByCourseId();
});
}
saveOrUpdate() {
if (this.chapter.id) {
//修改章节
this.updateChapter();
} else {
//新增章节
this.saveChapter();
}
}
//删除章节
removeById(chapterId) {
this.$confirm("此操作将永久删除章节信息, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
//点击确定,执行then方法
chapter.deleteById(chapterId).then((resp) => {
//删除成功
//提示信息
this.$message({
type: "success",
message: "删除成功!",
});
//刷新页面
this.getChapterVideoByCourseId();
});
});
}
@RestController
@RequestMapping("/eduservice/edu-video")
@CrossOrigin //解决跨域问题
public class EduVideoController {
@Autowired
private EduVideoService eduVideoService;
//添加小节
@PostMapping("/addVideo")
public R addVideo(@RequestBody EduVideo eduVideo){
eduVideoService.save(eduVideo);
return R.ok();
}
//删除小节
// TODO 后面这个方法需要完善,删除小节的时候,同时也要把视频删除
@DeleteMapping("/deleteVideo/{id}")
public R deleteVideo(@PathVariable String id){
eduVideoService.removeById(id);
return R.ok();
}
//修改小节
@PostMapping("/updateVideo")
public R updateVideo(@RequestBody EduVideo eduVideo){
eduVideoService.updateById(eduVideo);
return R.ok();
}
//根据小节id查询
@GetMapping("/getVideoById/{videoId}")
public R getVideoById(@PathVariable String videoId){
EduVideo eduVideo = eduVideoService.getById(videoId);
return R.ok().data("video",eduVideo);
}
}
<el-dialog :visible.sync="dialogVideoFormVisible" title="添加课时">
<el-form :model="video" label-width="120px">
<el-form-item label="课时标题">
<el-input v-model="video.title" />
el-form-item>
<el-form-item label="课时排序">
<el-input-number
v-model="video.sort"
:min="0"
controls-
position="right"
/>
el-form-item>
<el-form-item label="是否免费">
<el-radio-group v-model="video.free">
<el-radio :label="true">免费el-radio>
<el-radio :label="false">默认el-radio>
el-radio-group>
el-form-item>
<el-form-item label="上传视频">
el-form-item>
el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVideoFormVisible = false">取 消el-button>
<el-button
:disabled="saveVideoBtnDisabled"
type="primary"
@click="saveOrUpdateVideo(video.id)"
>确 定el-button
>
div>
el-dialog>
guli-admin\src\api\teacher\video.js
import request from '@/utils/request' //引入已经封装好的axios 和 拦截器
export default {
//添加小节
addVideo(video) {
return request({
url: `/eduservice/edu-video/addVideo`,
method: `post`,
data: video
})
},
//根据id查询小节
getVideoById(videoId) {
return request({
url: `/eduservice/edu-video/getVideoById/${
videoId}`,
method: `get`,
})
},
//修改小节
updateVideo(video) {
return request({
url: `/eduservice/edu-video/updateVideo`,
method: `post`,
data: video
})
},
//删除小节
deleteById(videoId) {
return request({
url: `/eduservice/edu-video/deleteVideo/${
videoId}`,
method: `delete`,
})
},
}
import video from "@/api/teacher/video.js";
//添加小节弹框的方法
openEditVideo(chapterId) {
//清空之前的数据
this.video = {
};
//显示弹框
this.dialogVideoFormVisible = true;
//设置章节id
this.video.chapterId = chapterId;
},
//添加小节
addVideo() {
//设置课程id
this.video.courseId = this.courseId;
video.addVideo(this.video).then((resp) => {
//关闭弹框
this.dialogVideoFormVisible = false;
//提示信息
this.$message({
message: "添加小节成功",
type: "success",
});
//刷新页面
this.getChapterVideoByCourseId();
});
},
//删除小节
removeVideo(videoId) {
this.$confirm("此操作将永久删除小节信息, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
//点击确定,执行then方法
video.deleteById(videoId).then((resp) => {
//删除成功
//提示信息
this.$message({
type: "success",
message: "删除成功!",
});
//刷新页面
this.getChapterVideoByCourseId();
});
});
},
//修改小节表单回显
getVideoById(videoId) {
//弹出小节弹窗
this.dialogVideoFormVisible = true;
video.getVideoById(videoId).then((resp) => {
this.video = resp.data.video;
});
},
//小节修改
updateVideorById(videoId) {
//设置小节id到video对象中
this.video.id = videoId;
video.updateVideo(this.video).then((resp) => {
//关闭弹框
this.dialogVideoFormVisible = false;
//提示信息
this.$message({
message: "修改小节成功",
type: "success",
});
//刷新页面
this.getChapterVideoByCourseId();
});
},
@Data
public class CoursePublishVo implements Serializable {
private static final long serialVersionUID = 1L;
private String id;//课程id
private String title; //课程名称
private String cover; //封面
private Integer lessonNum;//课时数
private String subjectLevelOne;//一级分类
private String subjectLevelTwo;//二级分类
private String teacherName;//讲师名称
private String price;//价格 ,只用于显示
}
接口:EduCourseMapper
public interface EduCourseMapper extends BaseMapper<EduCourse> {
public CoursePublishVo getPublishCourseInfo(String courseId);
}
实现:EduCourseMapper.xml
<mapper namespace="com.achang.eduservice.mapper.EduCourseMapper">
<select id="getPublishCourseInfo" resultType="com.achang.eduservice.entity.vo.CoursePublishVo">
SELECT
ec.id,
ec.title,
ec.cover,
ec.lesson_num AS lessonNum,
ec.price,
s1.title AS subjectLevelOne,
s2.title AS subjectLevelTwo,
t.name AS teacherName
FROM
edu_course ec
LEFT JOIN edu_teacher t ON ec.id = t.id
LEFT JOIN edu_subject s1 ON ec.subject_parent_id = s1.id
LEFT JOIN edu_subject s2 ON ec.id = s2.id
WHERE
ec.id = #{id}
select>
mapper>
@Component
public interface EduCourseMapper extends BaseMapper<EduCourse> {
public CoursePublishVo getPublishCourseInfo(String courseId);
}
根据课程id查询课程确认信息
public CoursePublishVo getPublishCourseInfo(String courseId);
//根据课程id查询课程确认信息
@Override
public CoursePublishVo getPublishCourseInfo(String courseId) {
return eduCourseMapper.getPublishCourseInfo(courseId);
}
//根据课程id查询课程确认信息
@GetMapping("/getpublishCourseInfo/{id}")
public R getpublishCourseInfo(@PathVariable String id){
CoursePublishVo publishCourseInfo = eduCourseService.getPublishCourseInfo(id);
return R.ok().data("publishCourse",publishCourseInfo);
}
测试,访问:http://localhost:8801/swagger-ui.html
报错
#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/achang/eduservice/mapper/xml/*.xml
guli-admin\src\api\teacher\course.js
//课程确认信息显示
getPublishCourseInfo(courseId){
return request({
url:"/eduservice/edu-course/getpublishCourseInfo/"+courseId,
method: 'get',
})
}
import course from '@/api/teacher/course.js'
export default {
data() {
return {
...
courseId:'',
publishCourseinfo:{
},
};
},
methods: {
//根据课程id查询
getPublishCourseInfo(){
course.getPublishCourseInfo(this.courseId)
.then(resp=>{
this.publishCourseinfo = resp.data.publishCourse
})
},
....
},
created() {
//获取路由中的id值
if(this.$route.params && this.$route.params.id){
this.courseId = this.$route.params.id
//调用接口方法根据课程id查询课程信息
this.getPublishCourseInfo()
}
},
};
<template>
<div class="app-container">
<h2 style="text-align: center">发布新课程h2>
<el-steps
:active="3"
process-status="wait"
align-center
style="margin-
bottom: 40px;"
>
<el-step title="填写课程基本信息" />
<el-step title="创建课程大纲" />
<el-step title="最终发布" />
el-steps>
<div class="ccInfo">
<img :src="publishCourseinfo.cover" />
<div class="main">
<h2>{
{ publishCourseinfo.title }}h2>
<p class="gray">
<span>共{
{ publishCourseinfo.lessonNum }}课时span>
p>
<p>
<span
>所属分类:{
{ publishCourseinfo.subjectLevelOne }} —
{
{ publishCourseinfo.subjectLevelTwo }}span
>
p>
<p>课程讲师:{
{ publishCourseinfo.teacherName }}p>
<h3 class="red">¥{
{ publishCourseinfo.price }}h3>
div>
div>
<el-form label-width="120px">
<el-form-item>
<el-button @click="previous">返回修改el-button>
<el-button :disabled="saveBtnDisabled" type="primary" @click="publish"
>发布课程el-button
>
el-form-item>
el-form>
div>
template>
//课程最终发布
//修改课程状态
@PostMapping("publishCourse/{id}")
public R publishCourse(@PathVariable String id){
EduCourse eduCourse = new EduCourse();
eduCourse.setStatus("Normal"); //设置课程发布状态
eduCourse.setId(id);
boolean flag = eduCourseService.updateById(eduCourse);
if (flag){
return R.ok();
}else {
return R.error();
}
}
guli-admin\src\api\teacher\course.js
//课程最终发布
publishCourse(courseId) {
return request({
url: "/eduservice/edu-course/publishCourse/" + courseId,
method: 'post',
})
}
//发布课程
publish() {
this.$confirm("你确定要发布此课程, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
course.publishCourse(this.courseId).then((resp) => {
//提示信息
this.$message({
message: "课程发布成功",
type: "success",
});
//跳转课程列表页面
this.$router.push({
path: "/course/list" });
});
});
}