01-课程大纲列表显示
一、后端实现
1、定义vo
ChapterVo
package com.guli.edu.vo;
@ApiModel(value = "章节信息")
@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
package com.guli.edu.vo;
@ApiModel(value = "课时信息")
@Data
public class VideoVo implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String title;
private Boolean free;
}
2、服务层
接口
package com.guli.edu.service;
public interface ChapterService extends IService<Chapter> {
List<ChapterVo> nestedList(String courseId);
}
实现
package com.guli.edu.service.impl;
@Service
public class ChapterServiceImpl extends ServiceImpl<ChapterMapper, Chapter> implements ChapterService {
@Autowired
private VideoService videoService;
@Override
public List<ChapterVo> nestedList(String courseId) {
//最终要的到的数据列表
ArrayList<ChapterVo> chapterVoArrayList = new ArrayList<>();
//获取章节信息
QueryWrapper<Chapter> queryWrapper1 = new QueryWrapper<>();
queryWrapper1.eq("course_id", courseId);
queryWrapper1.orderByAsc("sort", "id");
List<Chapter> chapters = baseMapper.selectList(queryWrapper1);
//获取课时信息
QueryWrapper<Video> queryWrapper2 = new QueryWrapper<>();
queryWrapper2.eq("course_id", courseId);
queryWrapper2.orderByAsc("sort", "id");
List<Video> videos = videoService.list(queryWrapper2);
//填充章节vo数据
int count1 = chapters.size();
for (int i = 0; i < count1; i++) {
Chapter chapter = chapters.get(i);
//创建章节vo对象
ChapterVo chapterVo = new ChapterVo();
BeanUtils.copyProperties(chapter, chapterVo);
chapterVoArrayList.add(chapterVo);
//填充课时vo数据
ArrayList<VideoVo> videoVoArrayList = new ArrayList<>();
int count2 = videos.size();
for (int j = 0; j < count2; j++) {
Video video = videos.get(j);
if(chapter.getId().equals(video.getChapterId())){
//创建课时vo对象
VideoVo videoVo = new VideoVo();
BeanUtils.copyProperties(video, videoVo);
videoVoArrayList.add(videoVo);
}
}
chapterVo.setChildren(videoVoArrayList);
}
return chapterVoArrayList;
}
}
3、web层
package com.guli.edu.controller.admin;
@Api(description="课程章节管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/admin/edu/chapter")
public class ChapterAdminController {
@Autowired
private ChapterService chapterService;
@ApiOperation(value = "嵌套章节数据列表")
@GetMapping("nested-list/{courseId}")
public R nestedListByCourseId(
@ApiParam(name = "courseId", value = "课程ID", required = true)
@PathVariable String courseId){
List<ChapterVo> chapterVoList = chapterService.nestedList(courseId);
return R.ok().data("items", chapterVoList);
}
}
4、Swagger测试
二、前端实现
1、定义api
chapter.js
import request from '@/utils/request'
const api_name = '/admin/edu/chapter'
export default {
getNestedTreeList(courseId) {
return request({
url: `${api_name}/nested-list/${courseId}`,
method: 'get'
})
}
}
2、定义组件脚本
定义data
courseId: '', // 所属课程
chapterNestedList: [] // 章节嵌套课时列表
created中调用init方法
created() {
console.log('chapter created')
this.init()
},
定义相关methods获取章节和课时列表
init() {
if (this.$route.params && this.$route.params.id) {
this.courseId = this.$route.params.id
// 根据id获取课程基本信息
this.fetchChapterNestedListByCourseId()
}
},
fetchChapterNestedListByCourseId() {
chapter.getNestedTreeList(this.courseId).then(response => {
this.chapterNestedList = response.data.items
})
},
3、定义组件模板
<el-button type="text">添加章节</el-button>
<!-- 章节 -->
<ul class="chanpterList">
<li
v-for="chapter in chapterNestedList"
:key="chapter.id">
<p>
{{ chapter.title }}
<span class="acts">
<el-button type="text">添加课时</el-button>
<el-button style="" type="text">编辑</el-button>
<el-button type="text">删除</el-button>
</span>
</p>
<!-- 视频 -->
<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>
<div>
<el-button @click="previous">上一步</el-button>
<el-button :disabled="saveBtnDisabled" type="primary" @click="next">下一步</el-button>
</div>
4、定义样式
将样式的定义放在页面的最后
scope表示这里定义的样式只在当前页面范围内生效,不会污染到其他的页面
<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>
02-章节管理后端接口开发
一、新增章节
web层
@ApiOperation(value = "新增章节")
@PostMapping
public R save(
@ApiParam(name = "chapterVo", value = "章节对象", required = true)
@RequestBody Chapter chapter){
chapterService.save(chapter);
return R.ok();
}
二、根据id查询
web层
@ApiOperation(value = "根据ID查询章节")
@GetMapping("{id}")
public R getById(
@ApiParam(name = "id", value = "章节ID", required = true)
@PathVariable String id){
Chapter chapter = chapterService.getById(id);
return R.ok().data("item", chapter);
}
三、更新
web层
@ApiOperation(value = "根据ID修改章节")
@PutMapping("{id}")
public R updateById(
@ApiParam(name = "id", value = "章节ID", required = true)
@PathVariable String id,
@ApiParam(name = "chapter", value = "章节对象", required = true)
@RequestBody Chapter chapter){
chapter.setId(id);
chapterService.updateById(chapter);
return R.ok();
}
四、删除
1、web层
@ApiOperation(value = "根据ID删除章节")
@DeleteMapping("{id}")
public R removeById(
@ApiParam(name = "id", value = "章节ID", required = true)
@PathVariable String id){
boolean result = chapterService.removeChapterById(id);
if(result){
return R.ok();
}else{
return R.error().message("删除失败");
}
}
2、Service
ChapterService层:接口
boolean removeChapterById(String id);
ChapterService层:实现
@Override
public boolean removeChapterById(String id) {
//根据id查询是否存在视频,如果有则提示用户尚有子节点
if(videoService.getCountByChapterId(id)){
throw new GuliException(20001,"该分章节下存在视频课程,请先删除视频课程");
}
Integer result = baseMapper.deleteById(id);
return null != result && result > 0;
}
VideoService:接口
boolean getCountByChapterId(String chapterId);
VideoService:实现
@Override
public boolean getCountByChapterId(String chapterId) {
QueryWrapper<Video> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("chapter_id", chapterId);
Integer count = baseMapper.selectCount(queryWrapper);
return null != count && count > 0;
}
五、Swagger测试
一、新增章节
web层
@ApiOperation(value = "新增章节")
@PostMapping
public R save(
@ApiParam(name = "chapterVo", value = "章节对象", required = true)
@RequestBody Chapter chapter){
chapterService.save(chapter);
return R.ok();
}
二、根据id查询
web层
@ApiOperation(value = "根据ID查询章节")
@GetMapping("{id}")
public R getById(
@ApiParam(name = "id", value = "章节ID", required = true)
@PathVariable String id){
Chapter chapter = chapterService.getById(id);
return R.ok().data("item", chapter);
}
三、更新
web层
@ApiOperation(value = "根据ID修改章节")
@PutMapping("{id}")
public R updateById(
@ApiParam(name = "id", value = "章节ID", required = true)
@PathVariable String id,
@ApiParam(name = "chapter", value = "章节对象", required = true)
@RequestBody Chapter chapter){
chapter.setId(id);
chapterService.updateById(chapter);
return R.ok();
}
四、删除
1、web层
@ApiOperation(value = "根据ID删除章节")
@DeleteMapping("{id}")
public R removeById(
@ApiParam(name = "id", value = "章节ID", required = true)
@PathVariable String id){
boolean result = chapterService.removeChapterById(id);
if(result){
return R.ok();
}else{
return R.error().message("删除失败");
}
}
2、Service
ChapterService层:接口
boolean removeChapterById(String id);
ChapterService层:实现
@Override
public boolean removeChapterById(String id) {
//根据id查询是否存在视频,如果有则提示用户尚有子节点
if(videoService.getCountByChapterId(id)){
throw new GuliException(20001,"该分章节下存在视频课程,请先删除视频课程");
}
Integer result = baseMapper.deleteById(id);
return null != result && result > 0;
}
VideoService:接口
boolean getCountByChapterId(String chapterId);
VideoService:实现
@Override
public boolean getCountByChapterId(String chapterId) {
QueryWrapper<Video> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("chapter_id", chapterId);
Integer count = baseMapper.selectCount(queryWrapper);
return null != count && count > 0;
}
五、Swagger测试# 一、新增章节
web层
@ApiOperation(value = "新增章节")
@PostMapping
public R save(
@ApiParam(name = "chapterVo", value = "章节对象", required = true)
@RequestBody Chapter chapter){
chapterService.save(chapter);
return R.ok();
}
二、根据id查询
web层
@ApiOperation(value = "根据ID查询章节")
@GetMapping("{id}")
public R getById(
@ApiParam(name = "id", value = "章节ID", required = true)
@PathVariable String id){
Chapter chapter = chapterService.getById(id);
return R.ok().data("item", chapter);
}
三、更新
web层
@ApiOperation(value = "根据ID修改章节")
@PutMapping("{id}")
public R updateById(
@ApiParam(name = "id", value = "章节ID", required = true)
@PathVariable String id,
@ApiParam(name = "chapter", value = "章节对象", required = true)
@RequestBody Chapter chapter){
chapter.setId(id);
chapterService.updateById(chapter);
return R.ok();
}
四、删除
1、web层
@ApiOperation(value = "根据ID删除章节")
@DeleteMapping("{id}")
public R removeById(
@ApiParam(name = "id", value = "章节ID", required = true)
@PathVariable String id){
boolean result = chapterService.removeChapterById(id);
if(result){
return R.ok();
}else{
return R.error().message("删除失败");
}
}
2、Service
ChapterService层:接口
boolean removeChapterById(String id);
ChapterService层:实现
@Override
public boolean removeChapterById(String id) {
//根据id查询是否存在视频,如果有则提示用户尚有子节点
if(videoService.getCountByChapterId(id)){
throw new GuliException(20001,"该分章节下存在视频课程,请先删除视频课程");
}
Integer result = baseMapper.deleteById(id);
return null != result && result > 0;
}
VideoService:接口
boolean getCountByChapterId(String chapterId);
VideoService:实现
@Override
public boolean getCountByChapterId(String chapterId) {
QueryWrapper<Video> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("chapter_id", chapterId);
Integer count = baseMapper.selectCount(queryWrapper);
return null != count && count > 0;
}
五、Swagger测试
03-章节管理前端页面实现
一、定义api
removeById(id) {
return request({
url: `${api_name}/${id}`,
method: 'delete'
})
},
save(chapter) {
return request({
url: api_name,
method: 'post',
data: chapter
})
},
getById(id) {
return request({
url: `${api_name}/${id}`,
method: 'get'
})
},
updateById(chapter) {
return request({
url: `${api_name}/${chapter.id}`,
method: 'put',
data: chapter
})
}
二、新增章节页面功能
1、定义data数据
dialogChapterFormVisible: false, //是否显示章节表单
chapter: {// 章节对象
title: '',
sort: 0
}
2、添加章节按钮
<el-button type="text" @click="dialogChapterFormVisible = true">添加章节</el-button>
3、章节表单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>
4、添加章节methods
saveOrUpdate() {
this.saveBtnDisabled = true
if (!this.chapter.id) {
this.saveData()
} else {
this.updateData()
}
},
saveData() {
this.chapter.courseId = this.courseId
chapter.save(this.chapter).then(response => {
this.$message({
type: 'success',
message: '保存成功!'
})
this.helpSave()
}).catch((response) => {
this.$message({
type: 'error',
message: response.message
})
})
},
updateData() {
},
helpSave(){
this.dialogChapterFormVisible = false// 如果保存成功则关闭对话框
this.fetchChapterNestedListByCourseId()// 刷新列表
this.chapter.title = ''// 重置章节标题
this.chapter.sort = 0// 重置章节标题
this.saveBtnDisabled = false
},
三、修改章节信息
1、编辑章节按钮
<el-button type="text" @click="editChapter(chapter.id)">编辑</el-button>
2、定义编辑方法
editChapter(chapterId) {
this.dialogChapterFormVisible = true
chapter.getById(chapterId).then(response => {
this.chapter = response.data.item
})
},
3、定义更新方法
updateData() {
chapter.updateById(this.chapter).then(response => {
this.$message({
type: 'success',
message: '修改成功!'
})
this.helpSave()
}).catch((response) => {
// console.log(response)
this.$message({
type: 'error',
message: response.message
})
})
},
四、删除章节
1、按钮
<el-button type="text" @click="removeChapter(chapter.id)">删除</el-button>
2、定义删除方法
removeChapter(chapterId) {
this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return chapter.removeById(chapterId)
}).then(() => {
this.fetchChapterNestedListByCourseId()// 刷新列表
this.$message({
type: 'success',
message: '删除成功!'
})
}).catch((response) => { // 失败
if (response === 'cancel') {
this.$message({
type: 'info',
message: '已取消删除'
})
} else {
this.$message({
type: 'error',
message: response.message
})
}
})
},
04-课时管理后端开发
一、定义Form表单对象
VideoInfoForm.java
package com.guli.edu.form;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author helen
* @since 2019/3/5
*/
@ApiModel(value = "课时基本信息", description = "编辑课时基本信息的表单对象")
@Data
public class VideoInfoForm {
@ApiModelProperty(value = "视频ID")
private String id;
@ApiModelProperty(value = "节点名称")
private String title;
@ApiModelProperty(value = "课程ID")
private String courseId;
@ApiModelProperty(value = "章节ID")
private String chapterId;
@ApiModelProperty(value = "视频资源")
private String videoSourceId;
@ApiModelProperty(value = "显示排序")
private Integer sort;
@ApiModelProperty(value = "是否可以试听:0默认 1免费")
private Boolean free;
}
二、课时保存
1、web层接口的定义
VideoAdminController.java
package com.guli.edu.controller.admin;
@Api(description="课时管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/admin/edu/video")
public class VideoAdminController {
@Autowired
private VideoService videoService;
@ApiOperation(value = "新增课时")
@PostMapping("save-video-info")
public R save(
@ApiParam(name = "videoForm", value = "课时对象", required = true)
@RequestBody VideoInfoForm videoInfoForm){
videoService.saveVideoInfo(videoInfoForm);
return R.ok();
}
}
2、业务层
VideoService.java
void saveVideoInfo(VideoInfoForm videoInfoForm);
VideoServiceImpl.java
@Override
public void saveVideoInfo(VideoInfoForm videoInfoForm) {
Video video = new Video();
BeanUtils.copyProperties(videoInfoForm, video);
boolean result = this.save(video);
if(!result){
throw new GuliException(20001, "课时信息保存失败");
}
}
三、课时的修改
1、web层接口的定义
VideoAdminController.java
@ApiOperation(value = "根据ID查询课时")
@GetMapping("video-info/{id}")
public R getVideInfoById(
@ApiParam(name = "id", value = "课时ID", required = true)
@PathVariable String id){
VideoInfoForm videoInfoForm = videoService.getVideoInfoFormById(id);
return R.ok().data("item", videoInfoForm);
}
@ApiOperation(value = "更新课时")
@PutMapping("update-video-info/{id}")
public R updateCourseInfoById(
@ApiParam(name = "VideoInfoForm", value = "课时基本信息", required = true)
@RequestBody VideoInfoForm videoInfoForm,
@ApiParam(name = "id", value = "课时ID", required = true)
@PathVariable String id){
videoService.updateVideoInfoById(videoInfoForm);
return R.ok();
}
2、业务层
VideoService.java
VideoInfoForm getVideoInfoFormById(String id);
void updateVideoInfoById(VideoInfoForm videoInfoForm);
VideoServiceImpl.java
@Override
public VideoInfoForm getVideoInfoFormById(String id) {
//从video表中取数据
Video video = this.getById(id);
if(video == null){
throw new GuliException(20001, "数据不存在");
}
//创建videoInfoForm对象
VideoInfoForm videoInfoForm = new VideoInfoForm();
BeanUtils.copyProperties(video, videoInfoForm);
return videoInfoForm;
}
@Override
public void updateVideoInfoById(VideoInfoForm videoInfoForm) {
//保存课时基本信息
Video video = new Video();
BeanUtils.copyProperties(videoInfoForm, video);
boolean result = this.updateById(video);
if(!result){
throw new GuliException(20001, "课时信息保存失败");
}
}
四、课时的删除
1、web层接口的定义
VideoAdminController.java
@ApiOperation(value = "根据ID删除课时")
@DeleteMapping("{id}")
public R removeById(
@ApiParam(name = "id", value = "课时ID", required = true)
@PathVariable String id){
boolean result = videoService.removeVideoById(id);
if(result){
return R.ok();
}else{
return R.error().message("删除失败");
}
}
2、业务层
VideoService.java
boolean removeVideoById(String id);
VideoServiceImpl.java
@Override
public boolean removeVideoById(String id) {
//删除视频资源 TODO
Integer result = baseMapper.deleteById(id);
return null != result && result > 0;
}
05-课时管理前端开发
一、定义api
创建video.js
参考course.js
import request from '@/utils/request'
const api_name = '/admin/edu/video'
export default {
saveVideoInfo(videoInfo) {
return request({
url: `${api_name}/save-video-info`,
method: 'post',
data: videoInfo
})
},
getVideoInfoById(id) {
return request({
url: `${api_name}/video-info/${id}`,
method: 'get'
})
},
updateVideoInfoById(videoInfo) {
return request({
url: `${api_name}/update-video-info/${videoInfo.id}`,
method: 'put',
data: videoInfo
})
},
removeById(id) {
return request({
url: `${api_name}/${id}`,
method: 'delete'
})
}
}
二、新增课时页面功能
1、定义data数据
saveVideoBtnDisabled: false, // 课时按钮是否禁用
dialogVideoFormVisible: false, // 是否显示课时表单
chapterId: '', // 课时所在的章节id
video: {// 课时对象
title: '',
sort: 0,
free: 0,
videoSourceId: ''
},
2、添加课时按钮
<el-button type="text" @click="dialogVideoFormVisible = true; chapterId = chapter.id">添加课时</el-button>
3、课时表单dialog
<!-- 添加和修改课时表单 -->
<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、添加课时methods
引入video模块
import video from '@/api/edu/video'
方法的定义
saveOrUpdateVideo() {
this.saveVideoBtnDisabled = true
if (!this.video.id) {
this.saveDataVideo()
} else {
this.updateDataVideo()
}
},
saveDataVideo() {
this.video.courseId = this.courseId
this.video.chapterId = this.chapterId
video.saveVideoInfo(this.video).then(response => {
this.$message({
type: 'success',
message: '保存成功!'
})
this.helpSaveVideo()
})
},
updateDataVideo() {
},
helpSaveVideo() {
this.dialogVideoFormVisible = false// 如果保存成功则关闭对话框
this.fetchChapterNestedListByCourseId()// 刷新列表
this.video.title = ''// 重置章节标题
this.video.sort = 0// 重置章节标题
this.video.videoSourceId = ''// 重置视频资源id
this.saveVideoBtnDisabled = false
},
三、修改课时信息
1、编辑课时按钮
<el-button type="text" @click="editVideo(video.id)">编辑</el-button>
2、定义编辑方法
editVideo(videoId) {
this.dialogVideoFormVisible = true
video.getVideoInfoById(videoId).then(response => {
this.video = response.data.item
})
},
3、定义更新方法
updateDataVideo() {
video.updateVideoInfoById(this.video).then(response => {
this.$message({
type: 'success',
message: '修改成功!'
})
this.helpSaveVideo()
})
},
四、删除课时
1、按钮
<el-button type="text" @click="removeVideo(video.id)">删除</el-button>
2、定义删除方法
removeVideo(videoId) {
this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return video.removeById(videoId)
}).then(() => {
this.fetchChapterNestedListByCourseId()// 刷新列表
this.$message({
type: 'success',
message: '删除成功!'
})
}).catch((response) => { // 失败
if (response === 'cancel') {
this.$message({
type: 'info',
message: '已取消删除'
})
}
})
}
06-课程最终发布前端
一、前端代码
1、定义api
分析这个页面一共有两个远程方法:一个是根基课程id获取课程基本预览信息,第二个是发布课程
getCoursePublishInfoById(id) {
return request({
url: `${api_name}/course-publish-info/${id}`,
method: 'get'
})
},
publishCourse(id) {
return request({
url: `${api_name}/publish-course/${id}`,
method: 'put'
})
}
2、定义数据模型
data() {
return {
saveBtnDisabled: false, // 保存按钮是否禁用
courseId: '', // 所属课程
coursePublish: {}
}
},
3、完善步骤导航
edu/course/chapter.js
previous() {
console.log('previous')
this.$router.push({ path: '/edu/course/info/' + this.courseId })
},
next() {
console.log('next')
this.$router.push({ path: '/edu/course/publish/' + this.courseId })
}
edu/course/pubish.js
<div>
<el-button @click="previous">返回修改</el-button>
<el-button :disabled="saveBtnDisabled" type="primary" @click="publish">发布课程</el-button>
</div>
previous() {
console.log('previous')
this.$router.push({ path: '/edu/course/chapter/' + this.courseId })
},
publish() {
console.log('publish')
course.publishCourse(this.courseId).then(response => {
this.$router.push({ path: '/edu/course/list' })
})
}
4、组件方法定义
import
import course from '@/api/edu/course'
created
created() {
console.log('chapter created')
this.init()
},
获取数据的方法
init() {
if (this.$route.params && this.$route.params.id) {
this.courseId = this.$route.params.id
// 根据id获取课程基本信息
this.fetchCoursePublishInfoById()
}
},
fetchCoursePublishInfoById() {
course.getCoursePublishInfoById(this.courseId).then(response => {
this.coursePublish = response.data.item
})
},
5、组件模板
<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="coursePublish.cover">
<div class="main">
<h2>{{ coursePublish.title }}</h2>
<p class="gray"><span>共{{ coursePublish.lessonNum }}课时</span></p>
<p><span>所属分类:{{ coursePublish.subjectLevelOne }} — {{ coursePublish.subjectLevelTwo }}</span></p>
<p>课程讲师:{{ coursePublish.teacherName }}</p>
<h3 class="red">¥{{ coursePublish.price }}</h3>
</div>
</div>
<div>
<el-button @click="previous">返回修改</el-button>
<el-button :disabled="saveBtnDisabled" type="primary" @click="publish">发布课程</el-button>
</div>
</div>
</template>
6、css样式
<style scoped>
.ccInfo {
background: #f5f5f5;
padding: 20px;
overflow: hidden;
border: 1px dashed #DDD;
margin-bottom: 40px;
position: relative;
}
.ccInfo img {
background: #d6d6d6;
width: 500px;
height: 278px;
display: block;
float: left;
border: none;
}
.ccInfo .main {
margin-left: 520px;
}
.ccInfo .main h2 {
font-size: 28px;
margin-bottom: 30px;
line-height: 1;
font-weight: normal;
}
.ccInfo .main p {
margin-bottom: 10px;
word-wrap: break-word;
line-height: 24px;
max-height: 48px;
overflow: hidden;
}
.ccInfo .main p {
margin-bottom: 10px;
word-wrap: break-word;
line-height: 24px;
max-height: 48px;
overflow: hidden;
}
.ccInfo .main h3 {
left: 540px;
bottom: 20px;
line-height: 1;
font-size: 28px;
color: #d32f24;
font-weight: normal;
position: absolute;
}
</style>
07-课程最终发布后端
一、根据id查询课程发布信息
方式一:业务层组装多个表多次的查询结果
方式二:数据访问层进行关联查询
我们使用第二种方式实现
1、定义vo
package com.guli.edu.vo;
@ApiModel(value = "课程发布信息")
@Data
public class CoursePublishVo implements Serializable {
private static final long serialVersionUID = 1L;
private String title;
private String cover;
private Integer lessonNum;
private String subjectLevelOne;
private String subjectLevelTwo;
private String teacherName;
private String price;//只用于显示
}
2、数据访问层
接口:CourseMapper.java
package com.guli.edu.mapper;
public interface CourseMapper extends BaseMapper<Course> {
CoursePublishVo selectCoursePublishVoById(String id);
}
实现:CourseMapper.xml
<select id="getCoursePublishVoById" resultType="com.guli.edu.vo.CoursePublishVo">
SELECT
c.title,
c.cover,
c.lesson_num AS lessonNum,
CONVERT(c.price, DECIMAL(8,2)) AS price,
s1.title AS subjectLevelOne,
s2.title AS subjectLevelTwo,
t.name AS teacherName
FROM
edu_course c
LEFT JOIN edu_teacher t ON c.teacher_id = t.id
LEFT JOIN edu_subject s1 ON c.subject_parent_id = s1.id
LEFT JOIN edu_subject s2 ON c.subject_id = s2.id
WHERE
c.id = #{id}
</select>
3、业务层
接口:CourseService.java
CoursePublishVo getCoursePublishVoById(String id);
实现:CourseServiceImpl.java
@Override
public CoursePublishVo getCoursePublishVoById(String id) {
return baseMapper.getCoursePublishVoById(id);
}
4、web层
@ApiOperation(value = "根据ID获取课程发布信息")
@GetMapping("course-publish-info/{id}")
public R getCoursePublishVoById(
@ApiParam(name = "id", value = "课程ID", required = true)
@PathVariable String id){
CoursePublishVo courseInfoForm = courseService.getCoursePublishVoById(id);
return R.ok().data("item", courseInfoForm);
}
测试:报告异常
AbstractHandlerExceptionResolver.java:194 |org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver |Resolved exception caused by handler execution: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.guli.edu.mapper.CourseMapper.getCoursePublishVoById
问题分析:
dao层编译后只有class文件,没有mapper.xml,因为maven工程在默认情况下src/main/java目录下的所有资源文件是不发布到target目录下的,
解决方案:
1、在guli_edu的pom中配置如下节点
<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
重新打包项目会发现target目录下出现了xml文件夹
2、在Spring Boot配置文件中添加配置
#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/guli/edu/mapper/xml/*.xml
二、根据id发布课程
1、web层
@ApiOperation(value = "根据id发布课程")
@PutMapping("publish-course/{id}")
public R publishCourseById(
@ApiParam(name = "id", value = "课程ID", required = true)
@PathVariable String id){
courseService.publishCourseById(id);
return R.ok();
}
2、service层
接口
void publishCourseById(String id);
实现
@Override
public boolean publishCourseById(String id) {
Course course = new Course();
course.setId(id);
course.setStatus(Course.COURSE_NORMAL);
Integer count = baseMapper.updateById(course);
return null != count && count > 0;
}