目录
目录
2.22 找到guli-admin/build/webpack.dev.conf.js 中添加配置
2.1.4 页面使用文本编辑器组件(使用标签实现文本编辑组件)
3.3.1 接口实现(后端) EduVideoController
一、内容介绍
1、添加课程基本信息完善
(1)整合文本编辑器
2、课程大纲管理
(1)课程大纲列表显示
(2)章节添加 修改 删除
(3)小节添加 修改 删除
3、课程信息确认
(1)编写sql语句实现
(2)课程最终发布
二、添加课程基本信息完善
2.1 整合文本编辑器
2.1.1 复制文本编辑器组件
2.22 找到guli-admin/build/webpack.dev.conf.js 中添加配置
templateParameters: {
BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
}
2.1.3 引入js脚本
在guli-admin/index.html 中引入js脚本
<script src=<%= BASE_URL %>/tinymce4.7.5/tinymce.min.js></script>
<script src=<%= BASE_URL %>/tinymce4.7.5/langs/zh_CN.js></script>
爆红无影响 框架解析的问题
2.1.4 页面使用文本编辑器组件(使用标签实现文本编辑组件)
1 在vue.info引入组件
2 声明组件
3 课程组件
<!-- 课程简介-->
<el-form-item label="课程简介">
<tinymce :height="300" v-model="courseInfo.description" />
</el-form-item>
4 组件样式
在info.vue文件的最后添加如下代码,调整上传图片按钮的高度
<style scoped>
.tinymce-container {
line-height: 29px;
}
</style>
scoped 代表只在当前页面有效
2.1.5 最终效果展示
最终测试
如果执行了全局异常处理(图片太大换个小的)
存在的问题
一级分类id为空
解决方法
在CouresInfoVo中加上一级分类字段
三、课程大纲管理
3.1 修改课程基本信息功能(编写封装代码)
3.1.1 第一步
创建两个实体类,章节和小节,只在章节实体类中使用list表示小节
chapterVo
@Data
public class ChapterVo {
private String id;
private String title;
//表示小节
private List<VideoVo> children = new ArrayList<>();
}
VideoVo
@Data
public class VideoVo {
private String id;
private String title;
}
3.1.2 第二步
1 controller层
@RestController
@RequestMapping("/eduservice/chapter")
@CrossOrigin
public class EduChapterController {
@Autowired
private EduChapterService chapterService;
//课程大纲列表,根据课程id进行查询
@GetMapping("getChapterVideo/{courseId}")
public R getChapterVideo(@PathVariable String courseId){
List<ChapterVo> list = chapterService.getChapterVideoByCourseId(courseId);
return R.ok().data("allChapterVideo",list);
}
}
2 service层
public interface EduChapterService extends IService<EduChapter> {
//课程大纲列表,根据课程id进行查询
List<ChapterVo> getChapterVideoByCourseId(String courseId);
3 serviceImpl
@Service
public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService {
@Autowired
private EduVideoService videoService;//注入小节的service
//课程大纲列表,根据课程id进行查询
@Override
public List<ChapterVo> getChapterVideoByCourseId(String courseId) {
//1 根据课程id查询课程里面所有的章节
QueryWrapper<EduChapter> wrapperChapter = new QueryWrapper<>();
wrapperChapter.eq("course_id",courseId);
List<EduChapter> eduChapterList = baseMapper.selectList(wrapperChapter);
//2 查询小节
QueryWrapper<EduVideo> wrapperVideo = new QueryWrapper<>();
wrapperVideo.eq("course_id",courseId);
List<EduVideo> eduVideoList = videoService.list(wrapperVideo);
//创建list集合,用于最终封装数据
List<ChapterVo> finalList = new ArrayList<>();
//3 遍历查询章节list集合进行封装
//遍历查询章节list集合
for (int i = 0; i < eduChapterList.size(); i++) {
//每个章节
EduChapter eduChapter = eduChapterList.get(i);
//eduChapter对象值复制到ChapterVo里面
ChapterVo chapterVo = new ChapterVo();
BeanUtils.copyProperties(eduChapter,chapterVo);
//把chapterVo放到最终list集合里面
finalList.add(chapterVo);
//创建集合,用于封装章节的小节
List<VideoVo> videoList = new ArrayList<>();
//4 遍历查询小节list集合,进行封装
for (int m = 0; m < eduVideoList.size(); m++) {
//得到每个小节
EduVideo eduVideo = eduVideoList.get(m);
//判断:小节里面chapterid和章节里面id是否一样
if(eduVideo.getChapterId().equals(eduChapter.getId())){
//进行封装
VideoVo videoVo = new VideoVo();
BeanUtils.copyProperties(eduVideo,videoVo);
//放到小节封装集合
videoList.add(videoVo);
}
}
//把封装之后小节list集合,放到章节对象里面
chapterVo.setChildren(videoList);
}
return finalList;
}
}
3.1.3 前端整合
1 创建 chapter.js
import request from '@/utils/request'
export default {
// 1 根据课程id获取章节集合小节数据列表
getAllChapterVideo(courseId) {
return request({
url: '/eduservice/chapter/getChapterVideo/'+courseId,
method: 'get'
})
}
}
2 在chapter.vue引入chapter
3 定义方法
4 定义变量进行赋值
5 调用方法
6 获取路由id值
7 页面数据显示
7.1 章节
(1)代码如下
(2)效果展示 (ctrl+f5 强制刷新浏览器)可能因为缓存问题没有显示
7.2 小节
(1)代码
(2)效果展示
7.3 用模板
<!-- 章节 -->
<ul class="chanpterList">
<li v-for="chapter in chapterVideoList" :key="chapter.id">
<p>
{{ chapter.title }}
</p>
<!-- 视频 -->
<ul class="chanpterList videoList">
<li v-for="video in chapter.children" :key="video.id">
<p>
{{ video.title }}
</p>
</li>
</ul>
</li>
</ul>
<div>
<el-button @click="previous">上一步</el-button>
<el-button :disabled="saveBtnDisabled" type="primary" @click="next"
>下一步</el-button
>
</div>
添加个css样式
</script>
<style scoped>
.chanpterList {
position: relative;
list-style: none;
margin: 0;
padding: 0;
}
.chanpterList li {
position: relative;
}
.chanpterList p {
float: left;
font-size: 20px;
margin: 10px 0;
padding: 10px;
height: 70px;
line-height: 50px;
width: 100%;
border: 1px solid #ddd;
}
.chanpterList .acts {
float: right;
font-size: 14px;
}
.videoList {
padding-left: 50px;
}
.videoList p {
float: left;
font-size: 14px;
margin: 10px 0;
padding: 10px;
height: 50px;
line-height: 30px;
width: 100%;
border: 1px dotted #ddd;
}
</style>
效果展示
3.3 修改课程基本信息
3.3.1 开发后端接口
1、根据课程id查询课程基本信息接口
(1)cotroller层(EduCourseController)
(2)service层(EduCourseService)
(3)serviceImpl层(EduCourseServiceImpl)
2、修改课程信息接口
(1)cotroller层(EduCourseController)
(2)service层(EduCourseService)
(3)serviceImpl层(EduCourseServiceImpl)
3.3.2 前端接口修改
1 修改course.js
2 修改chapter.vue中的方法
3.3.3 在info页面实现数据回显
1 定义方法
2 变量赋值
3 方法调用
4 测试出错
403 出现的可能清空 跨域 或者路径写错
修改后的course.js
5 存在的问题(二级分类没有回显)
下拉列表数据回显
根据存储id和分类所有id进行比较,如果比较相同,让相同值进行数据回显
<select>
<option selected="selected">前端开发</option>
<select/>
<input type="checkbox" checked/>
<input type="radio" checked/>
5.1 先修改create的方法
5.2 在method创建方法
5.3 测试
3.3.4 修改信息后保存下一步按钮实现
1 在method中写方法
//添加课程
addCourse(){
course.addCourseInfo(this.courseInfo).then((response) => {
//提示消息
this.$message({
type: "success",
message: "添加课程信息成功!",
});
//跳转到第二步
this.$router.push({
path: "/course/chapter/" + response.data.courseId})
});
},
//修改课程
updateCourse() {
course.updateCourseInfo(this.courseInfo)
.then(response => {
//提示
this.$message({
type:'success',
message:'修改课程信息成功!',
});
//跳转到第二步
this.$router.push({
path: "/course/chapter/" + thus.courseId})
})
},
saveOrUpdate() {
//判断试试添加还是修改
if(!this.courseInfo.id){
//添加
this.addCourse()
}else{
this.updateCourse()
}
}
测试效果
3.2 章节添加 修改 删除
3.2.1 添加章节的实现(页面)
1、 添加按钮 添加章节
(1)先添加按钮 chapter.vue
(2)章节表单dialog
<!-- 添加和修改章节表单 -->
<el-dialog :visible.sync="dialogChapterFormVisible" title="添加章节">
<el-form :model="chapter" label-width="120px">
<el-form-item label="章节标题">
<el-input v-model="chapter.title" />
</el-form-item>
<el-form-item label="章节排序">
<el-input-number
v-model="chapter.sort"
:min="0"
controls-position="right"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogChapterFormVisible = false">取 消</el-button>
<el-button type="primary" @click="saveOrUpdate">确 定</el-button>
</div>
</el-dialog>
(3)初始化数据
(4)给按钮绑定事件
(5)效果
2、点击添加章节按钮,弹出添加框,输入章节信息,点击保存添加
3.2.2 章节的实现(后端接口)
删除章节
如果章节里面没有小节,直接删除
如果章节里面有小节,如何删除?
第一种 删除章节的时候,把章节里面所有的小节都删除
第二种 如果删除的章节下面有小节,不让进行删除
1、controller EduChapterController
@RestController
@RequestMapping("/eduservice/chapter")
@CrossOrigin
public class EduChapterController {
@Autowired
private EduChapterService chapterService;
//课程大纲列表,根据课程id进行查询
@GetMapping("getChapterVideo/{courseId}")
public R getChapterVideo(@PathVariable String courseId) {
List<ChapterVo> list = chapterService.getChapterVideoByCourseId(courseId);
return R.ok().data("allChapterVideo", list);
}
//添加章节
@PostMapping("addChapter")
public R addChapter(@RequestBody EduChapter eduChapter) {
chapterService.save(eduChapter);
return R.ok();
}
//根据章节id查询
@GetMapping("getChapterInfo/{chapterId}")
public R getChapterInfo(@PathVariable String chapterId) {
EduChapter eduChapter = chapterService.getById(chapterId);
return R.ok().data("chapter", eduChapter);
}
//修改章节
@PostMapping("updateChapter")
public R updateChapter(@RequestBody EduChapter eduChapter) {
chapterService.updateById(eduChapter);
return R.ok();
}
//删除的方法
@DeleteMapping("{chapterId}")
public R deleteChapter(@PathVariable String chapterId) {
boolean flag = chapterService.deleteChapter(chapterId);
if (flag) {
return R.ok();
} else {
return R.error();
}
}
}
2、service EduChapterService
public interface EduChapterService extends IService<EduChapter> {
//课程大纲列表,根据课程id进行查询
List<ChapterVo> getChapterVideoByCourseId(String courseId);
//删除章节的方法
boolean deleteChapter(String chapterId);
}
3、 serviceImpl EduChapterServiceImpl
3.2.2 章节的实现(前端接口)
1、在api下的chapter.js创建接口
import request from '@/utils/request'
export default {
// 1 根据课程id获取章节集合小节数据列表
getAllChapterVideo(courseId) {
return request({
url: '/eduservice/chapter/getChapterVideo/'+courseId,
method: 'get'
})
},
//添加章节
addChapter(chapter) {
return request({
url: '/eduservice/chapter/addChapter',
method: 'post',
data: chapter
})
},
//根据id查询章节
getChapter(chapterId){
return request({
url: '/eduservice/chapter/getChapterInfo/'+chapterId,
method: 'get'
})
},
//修改章节
updateChapter(chapter){
return request({
url: '/eduservice/chapter/updateChapter/',
method: 'post',
data: chapter
})
},
//删除章节
deleteChapter(chapterId){
return request({
url: '/eduservice/chapter/'+chapterId,
method: 'delete'
})
}
}
2 添加章节
(1)定义data数据
(2)创建方法
(3)测试
修改前端方法(chapter.vue)
后端(EduChapter加上时间自动填充)
(4)再次测试
(4)存在小bug 重新添加数据 表单没有清空
绑定方法
创建修改方法
测试
3、修改章节
(1)先增加修改按钮 chapter.vue
(2)实现编辑
先绑定事件(编辑需要查询然后修改 传入id进行查询)
创建方法
效果显示 成功回显数据
修改功能实现
测试
4 删除实现
(1)给删除绑定事件
(2)定义方法
3.3 小节功能实现
3.3.1 接口实现(后端) EduVideoController
3.3.2 接口实现(前端)
1 先添加按钮
2 定义方法
3 添加小节弹框
<!-- 添加和修改课时表单 -->
<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="上传视频">
<!-- TODO -->
</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"
>确 定</el-button
>
</div>
</el-dialog>
4 给初始值 dialogVideoFormVisible
5 效果显示
6 创建video.js
import request from '@/utils/request'
export default {
//添加小节
addVideo(video) {
return request({
url: '/eduservice/video/addVideo',
method: 'post',
data: video
})
},
//删除小节
deleteVideo(id){
return request({
url: '/eduservice/video/'+id,
method: 'delete'
})
}
}
7 去页面调用 chapter.vue
8 设置章节id和课程id
9 效果显示
10 创建按钮
11 定义方法
12 效果
四、 课程信息确认
sql代码
SELECT ec.`id`,ec.`title`,ec.`lesson_num`,
ecd.`description`,
et.`name`,
es1.title AS oneSubject,
es2.title AS twoSubject
FROM edu_course ec LEFT JOIN edu_course_description ecd ON ec.`id`=ecd.`id`
LEFT JOIN edu_teacher et ON ec.`teacher_id` = et.`id`
LEFT JOIN edu_subject es1 ON ec.`subject_parent_id`=es1.`id`
LEFT JOIN edu_subject es2 ON ec.`subject_id`=es2.`id`
WHERE ec.`id`='1441326613974253570'
4.1 定义Vo
1 定义Vo
@Data
public class CoursePublishVo {
private String id;
private String title;
private String cover;
private Integer lessonNum;
private String subjectLevelOne;
private String subjectLevelTwo;
private String teacherName;
private String price;//只用于显示
}
2、数据访问层
3实现:CourseMapper.xml
<!-- sql语句:根据课程id查询课程确认信息 -->
<select id="getPublishCourseInfo" resultType="com.atguigu.eduservice.entity.vo.CoursePublishVo">
SELECT ec.`id`,ec.`title`,ec.`price`,ec.`lesson_num` AS lesson_num,ec.`cover`,
et.`name` AS teacherName,
es1.`title` AS subjectLevelOne,
es2.`title` AS subjectLevelTwo
FROM edu_course ec LEFT JOIN edu_course_description ecd ON ec.`id`=ecd.`id`
LEFT JOIN edu_teacher et ON ec.`teacher_id` = et.`id`
LEFT JOIN edu_subject es1 ON ec.`subject_parent_id`=es1.`id`
LEFT JOIN edu_subject es2 ON ec.`subject_id`=es2.`id`
WHERE ec.`id`=#{courseId}
</select>
4 业务层
EduCourseService
实现:CourseServiceImpl
5、 测试(报错)
由maven默认加载机制造成的问题
maven加载时候,把java文件夹里面,java类型文件进行编译,如果换其他类型文件,不会加载
解决方式:
1、赋值xml到tagrget目录中
2、把xml放到resource目录下
3、推荐使用:通过配置实现
(1)pom.xml
(2)项目application.properties