目录
一、内容介绍
1.1 添加课程分类前端实现
1.2 课程分类列表显示功能(树形)
1.3 课程管理模块需求
1.4 添加课程基本信息功能
二、添加课程分类前端
2.1 添加课程分类路由
{
path: '/subjext',
component: Layout,
redirect: '/subjext/list',
name: '课程分类管理',
meta: { title: '课程分类管理', icon: 'example' },
children: [
{
path: 'list',
name: '课程分类列表',
component: () => import('@/views/edu/teacher/list'),
meta: { title: '课程分类列表', icon: 'table' }
},
{
path: 'sava',
name: '添加课程分类',
component: () => import('@/views/edu/teacher/save'),
meta: { title: '添加课程分类', icon: 'tree' }
}
]
},
页面效果
2.2 创建课程分类页面,修改路由对应的页面
修改路径
2.3 在添加课程分类页面 实现效果
添加上传组件实现
2.3.1模板
<template>
<div class="app-container">
<el-form label-width="120px">
<el-form-item label="信息描述">
<el-tag type="info">excel模版说明</el-tag>
<el-tag>
<i class="el-icon-download" />
<a
:href="
'/static/01.xlsx'
"
>点击下载模版</a
>
</el-tag>
</el-form-item>
<el-form-item label="选择Excel">
<el-upload
ref="upload"
:auto-upload="false"
:on-success="fileUploadSuccess"
:on-error="fileUploadError"
:disabled="importBtnDisabled"
:limit="1"
:action="BASE_API + '/admin/edu/subject/import'"
name="file"
accept="application/vnd.ms-excel"
>
<el-button slot="trigger" size="small" type="primary"
>选取文件</el-button
>
<el-button
:loading="loading"
style="margin-left: 10px"
size="small"
type="success"
@click="submitUpload"
>上传到服务器</el-button
>
</el-upload>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
BASE_API: process.env.BASE_API, // 接口API地址
importBtnDisabled: false, // 按钮是否禁用,
loading: false,
};
},
created() {},
methods: {},
};
</script>
2.3.2 js上传方法
2.3.3 回调函数
2.3.4 save代码
<template>
<div class="app-container">
<el-form label-width="120px">
<el-form-item label="信息描述">
<el-tag type="info">excel模版说明</el-tag>
<el-tag>
<i class="el-icon-download" />
<a :href="'/static/01.xlsx'">点击下载模版</a>
</el-tag>
</el-form-item>
<el-form-item label="选择Excel">
<el-upload
ref="upload"
:auto-upload="false"
:on-success="fileUploadSuccess"
:on-error="fileUploadError"
:disabled="importBtnDisabled"
:limit="1"
:action="BASE_API + '/eduservice/subject/addSubject'"
name="file"
accept="application/vnd.ms-excel"
>
<el-button slot="trigger" size="small" type="primary"
>选取文件</el-button
>
<el-button
:loading="loading"
style="margin-left: 10px"
size="small"
type="success"
@click="submitUpload"
>上传到服务器</el-button
>
</el-upload>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
BASE_API: process.env.BASE_API, // 接口API地址
importBtnDisabled: false, // 按钮是否禁用,
loading: false,
};
},
created() {},
methods: {
//点击按钮上传文件到接口里面
submitUpload() {
this.importBtnDisabled = true;
this.loading = true;
// js: document.getElementById("upload").submit()
this.$refs.upload.submit();
},
//上传成功
fileUploadSuccess() {
//提示信息
this.$message({
type: "success",
message: "添加课程分类成功",
});
//跳转课程分类列表
},
//上传失败
fileUploadError() {
this.loading = false;
this.$message({
type: "error",
message: "添加课程分类失败",
});
},
},
};
</script>
2.3.5测试
2.4 课程列表功能
2.4.1 参考tree模块把前端整合出来
需要做的事情,创建接口,把分类按照要求的格式返回数据就可以了
返回这种格式的数据
OneSubject代码
//一级分类
@Data
public class OneSubject {
private String id;
private String title;
//一个一级分类有多个二级分类
private List<TwoSubject> children = new ArrayList<>();
}
TwoSubject代码
//二级分类
@Data
public class TwoSubject {
private String id;
private String title;
}
第三步 编写具体封装代码
1 Cotroller层
EduSubjectController
//课程分类列表(树形)
@GetMapping("getAllSubject")
public R getAllSubject(){
//list集合泛型是一级分类
List<OneSubject> list = subjectService.getAllOneTwoSubject();
return R.ok().data("list",list);
}
2 service
EduSubjectService
//课程分类列表(树形)
List<OneSubject> getAllOneTwoSubject();
EduSubjectServiceImpl
//课程分类列表(树形)
@Override
public List<OneSubject> getAllOneTwoSubject() {
//1、查出所有一级分裂 parentid=0
QueryWrapper<EduSubject> wrapperOne = new QueryWrapper<>();
wrapperOne.eq("parent_id","0");
List<EduSubject> oneSubjectList = baseMapper.selectList(wrapperOne);
//2、查出所有二级分类 parentid!=0
QueryWrapper<EduSubject> wrapperTwo = new QueryWrapper<>();
wrapperTwo.ne("parent_id","0");
List<EduSubject> twoSubjectList = baseMapper.selectList(wrapperTwo);
//创建list集合,用于存储最终封装数据
List<OneSubject> finalSubjectList = new ArrayList<>();
//3、封装一级分类
//查出出所有的一级分类list集合遍历,得到每一个一级分类对象,获取每一个分类对象值
//封装到要求的list集合里面List<OneSubject> finalSubjectList
for (int i = 0; i < oneSubjectList.size(); i++) {
//得到oneSubjectList每个eduSubject
EduSubject eduSubject = oneSubjectList.get(i);
//把多个eduSubject里面值取出来,放到OneSubject对象里面
OneSubject oneSubject = new OneSubject();
// oneSubject.setId(eduSubject.getId());
// oneSubject.setTitle(eduSubject.getTitle());
//eduSubject值复制到oneSubject里面
BeanUtils.copyProperties(eduSubject,oneSubject);
//多个OneSubject放到finalSubjectList里面
finalSubjectList.add(oneSubject);
//4、封装二级分类
//在一级分类循环遍历查询所有的二级分类
//在创建list集合封装每一个一级分类的二级分类
List<TwoSubject> twoFinalSubjectList = new ArrayList<>();
//遍历二级分类list集合
for (int m = 0; m < twoSubjectList.size(); m++) {
//获取每个二级分类
EduSubject tSubject = twoSubjectList.get(m);
//判断二级分类parentid和一级分类id是否一样
if(tSubject.getParentId().equals(eduSubject.getId())){
//把tSubject值复制到TwoSubject里面,放到twoFinalSubject里面
TwoSubject twoSubject = new TwoSubject();
BeanUtils.copyProperties(tSubject,twoSubject);
twoFinalSubjectList.add(twoSubject);
}
}
//把一级下面所有的二级分类放到一级分类里面
oneSubject.setChildren(twoFinalSubjectList);
}
return finalSubjectList;
}
3 Swagger测试
2.5 前端优化
1 先创建subjcect.js
代码如下
/* eslint-disable no-undef */
import request from '@/utils/request'
export default {
// 1 课程分类列表
// current当前页 limit分页 teacherQuery条件对象
getSubjectList() {
return request({
url: '/eduservice/subject/getAllSubject',
method: 'get'
})
}
}
2 修改list.vue
代码如下
<template>
<div class="app-container">
<el-input v-model="filterText" placeholder="Filter keyword" style="margin-bottom:30px;" />
<el-tree
ref="tree2"
:data="data2"
:props="defaultProps"
:filter-node-method="filterNode"
class="filter-tree"
default-expand-all
/>
</div>
</template>
<script>
import subject from '@/api/edu/subject'
export default {
data() {
return {
filterText: '',
data2: [], // 返回所有分类数据
defaultProps: {
children: 'children',
label: 'title'
}
}
},
created() {
this.getAllSubjectList()
},
watch: {
filterText(val) {
this.$refs.tree2.filter(val)
}
},
methods: {
getAllSubjectList(){
subject.getSubjectList()
.then(response =>{
this.data2 = response.data.list
})
},
filterNode(value, data) {
if (!value) return true
return data.title.toLowerCase().indexOf(value.toLowerCase()) !== -1
}
}
}
</script>
3 效果展示
4 在save.vue中添加路由跳转
2.6 课程管理
2.6.1 课程添加
细节问题
(1)创建vi实体类用于表单数据封装
(2)把表单提交过来的数据添加到数据库(向两张表添加数据:课程表和课程描述表)
(3)把讲师和分类使用下拉列表显示(课程分类做成二级联动效果)
edu_course 课程表:存储课程基本信息
edu_course_collect 课程简介表:存储课程简介信息
edu_chapter 课程章节表:存储课程章节信息
edu_video 课程小节表:存储章节里面的小节信息
课程管理-添加课程基本信息
第一步 使用代码生成器生成课程相关的代码
第二步 创建vo类封装表单提交的数据
代码如下
@Data
public class CourseInfoVo {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "课程ID")
private String id;
@ApiModelProperty(value = "课程讲师ID")
private String teacherId;
@ApiModelProperty(value = "课程专业ID")
private String subjectId;
@ApiModelProperty(value = "课程标题")
private String title;
@ApiModelProperty(value = "课程销售价格,设置为0则可免费观看")
// 0.01
private BigDecimal price;
@ApiModelProperty(value = "总课时")
private Integer lessonNum;
@ApiModelProperty(value = "课程封面图片路径")
private String cover;
@ApiModelProperty(value = "课程简介")
private String description;
}
第三步 编写controller和service部分
1 创建controller EduSubjectController
@RestController
@RequestMapping("/eduservice/subject")
@CrossOrigin
public class EduSubjectController {
@Autowired
private EduSubjectService subjectService;
//添加课程分类
//获取上传过来的文件,把文件内容读取出来
@PostMapping("addSubject")
public R addSubject(MultipartFile file){
//上传excel文件
subjectService.saveSubject(file,subjectService);
return R.ok();
}
//课程分类列表(树形)
@GetMapping("getAllSubject")
public R getAllSubject(){
//list集合泛型是一级分类
List<OneSubject> list = subjectService.getAllOneTwoSubject();
return R.ok().data("list",list);
}
}
2 service EduCourseService
public interface EduCourseService extends IService<EduCourse> {
//添加课程基本信息的方法
void saveCourseInfo(CourseInfoVo courseInfoVo);
}
3 serviceImpl EduCourseServiceImpl
@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {
//课程描述注入
@Autowired
private EduCourseDescriptionService courseDescriptionService;
//添加课程基本信息的方法
@Override
public void saveCourseInfo(CourseInfoVo courseInfoVo) {
//1 向课程表 添加课程基本信息
//CourseInfoVo对象转换eduCourse对象
EduCourse eduCourse = new EduCourse();
BeanUtils.copyProperties(courseInfoVo,eduCourse);
int insert = baseMapper.insert(eduCourse);
if(insert == 0){
//添加失败
throw new GuliException(20001,"添加课程信息失败");
}
//2 向课程简介表添加课程简介
//edu_course_description
EduCourseDescription courseDescription = new EduCourseDescription();
courseDescription.setDescription(courseInfoVo.getDescription());
courseDescriptionService.save(courseDescription);
}
}
4 进行测试
(1)先删除数据表中内容
DELETE FROM edu_course_description
(2)Swagger
注意:一定要取消设置 subject_parent_id 非空 否则测试报错
5 存在的问题
课程和描述是一对一关系,添加之后,id值是一样的
修改描述实体类id生成策略
前端部分:
第一步 添加课程管理路由
添加隐藏路由,做页面跳转
{
path: '/course',
component: Layout,
redirect: '/course/list',
name: '课程管理',
meta: { title: '课程管理', icon: 'example' },
children: [
{
path: 'list',
name: '课程列表',
component: () => import('@/views/edu/course/list'),
meta: { title: '课程列表', icon: 'table' }
},
{
path: 'info',
name: '添加课程',
component: () => import('@/views/edu/course/info'),
meta: { title: '添加课程', icon: 'tree' }
},
{
path: 'info/:id',
name: 'EduCourseInfoEdit',
component: () => import('@/views/edu/course/info'),
meta: { title: '编辑课程基本信息', noCache: true },
hidden: true
},
{
path: 'chapter/:id',
name: 'EduCourseChapterEdit',
component: () => import('@/views/edu/course/chapter'),
meta: { title: '编辑课程大纲', noCache: true },
hidden: true
},
{
path: 'publish/:id',
name: 'EduCoursePublishEdit',
component: () => import('@/views/edu/course/publish'),
meta: { title: '发布课程', noCache: true },
hidden: true
}
]
},
1、课程信息页面 info.vue
<template>
<div class="app-container">
<h2 style="text-align: center">发布新课程</h2>
<el-steps
:active="1"
process-status="wait"
align-center
style="margin-bottom: 40px"
>
<el-step title="填写课程基本信息" />
<el-step title="创建课程大纲" />
<el-step title="提交审核" />
</el-steps>
<el-form label-width="120px">
<el-form-item>
<el-button :disabled="saveBtnDisabled" type="primary" @click="next"
>保 存并下一步</el-button
>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data(){
return {
saveBtnDisabled:false
}
},
created() {
},
methods: {
next(){
//跳转到第二步
this.$router.push({path:'/course/chapter/1'})
}
},
};
</script>
2、课程大纲页面 chapter.vue
<template>
<div class="app-container">
<h2 style="text-align: center">发布新课程</h2>
<el-steps
:active="2"
process-status="wait"
align-center
style="margin-bottom: 40px"
>
<el-step title="填写课程基本信息" />
<el-step title="创建课程大纲" />
<el-step title="最终发布" />
</el-steps>
<el-form label-width="120px">
<el-form-item>
<el-button @click="previous">上一步</el-button>
<el-button :disabled="saveBtnDisabled" type="primary" @click="next"
>下 一步</el-button
>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data(){
return {
saveBtnDisabled:false
}
},
created() {
},
methods: {
previous(){
this.$router.push({path:'/course/info/1'})
},
next(){
//跳转到第二步
this.$router.push({path:'/course/publish/1'})
}
},
};
</script>
3、课程发布页面 publish.vue
<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>
<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>
<script>
export default {
data() {
return {
saveBtnDisabled: false, // 保存按钮是否禁用
};
},
created() {
console.log("publish created");
},
methods: {
previous() {
console.log("previous");
this.$router.push({ path: "/course/chapter/1" });
},
publish() {
console.log("publish");
this.$router.push({ path: "/course/list" });
},
},
};
</script>
4.创建course.js
/* eslint-disable no-undef */
import request from '@/utils/request'
export default {
// 1 添加课程信息功能
addCourseInfo(courseInfo) {
return request({
url: '/eduservice/course/addCourseInfo',
method: 'post',
data:courseInfo
})
}
}
5.在info.vue引入 并修改模板
info.vue代码
<template>
<div class="app-container">
<h2 style="text-align: center">发布新课程</h2>
<el-steps
:active="1"
process-status="wait"
align-center
style="margin-bottom: 40px"
>
<el-step title="填写课程基本信息" />
<el-step title="创建课程大纲" />
<el-step title="最终发布" />
</el-steps>
<el-form label-width="120px">
<el-form-item label="课程标题">
<el-input
v-model="courseInfo.title"
placeholder=" 示例:机器学习项目课:从基础到搭建项目视频课程。专业名称注意大小写"
/>
</el-form-item>
<!-- 所属分类 TODO -->
<el-form-item label="课程分类">
<el-select
v-model="courseInfo.subjectParentId"
placeholder="一级分类"
@change="subjectLevelOneChanged"
>
<el-option
v-for="subject in subjectOneList"
:key="subject.id"
:label="subject.title"
:value="subject.id"
/>
</el-select>
<!-- 二级分类 -->
<el-select v-model="courseInfo.subjectId" placeholder="二级分类">
<el-option
v-for="subject in subjectTwoList"
:key="subject.id"
:label="subject.title"
:value="subject.id"
/>
</el-select>
</el-form-item>
<!-- 课程讲师 TODO -->
<!-- 课程讲师 -->
<el-form-item label="课程讲师">
<el-select v-model="courseInfo.teacherId" placeholder="请选择">
<el-option
v-for="teacher in teacherList"
:key="teacher.id"
:label="teacher.name"
:value="teacher.id"
/>
</el-select>
</el-form-item>
<el-form-item label="总课时">
<el-input-number
:min="0"
v-model="courseInfo.lessonNum"
controls-position="right"
placeholder="请填写课程的总课时数"
/>
</el-form-item>
<!-- 课程简介 TODO -->
<el-form-item label="课程简介">
<el-input v-model="courseInfo.description" placeholder=" " />
</el-form-item>
<!-- 课程封面 TODO -->
<!-- 课程封面-->
<el-form-item label="课程封面">
<el-upload
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
:action="BASE_API + '/eduoss/fileoss'"
class="avatar-uploader"
>
<img :src="courseInfo.cover" />
</el-upload>
</el-form-item>
<el-form-item label="课程价格">
<el-input-number
:min="0"
v-model="courseInfo.price"
controls-position="right"
placeholder="免费课程请设置为0元"
/>
元
</el-form-item>
<el-form-item>
<el-button
:disabled="saveBtnDisabled"
type="primary"
@click="saveOrUpdate"
>保存并下一步</el-button
>
</el-form-item>
</el-form>
</div>
</template>
<script>
import course from '@/api/edu/course'
export default {
data() {
return {
saveBtnDisabled: false,
courseInfo: {
title: "",
subjectId: "",
teacherId: "",
lessonNum: 0,
description: "",
cover: "",
price: 0,
},
};
},
created() {},
methods: {
saveOrUpdate() {
course.addCourseInfo(this.courseInfo)
.then(response => {
//提示消息
this.$message({
type: "success",
message: "添加课程信息成功!"
});
//跳转到第二步
this.$router.push({ path: "/course/chapter/1" });
})
},
},
};
</script>
6 页面测试效果
第三步 添加之后返回id
1 修改接口
2 修改info.vue
3 测试
下拉列表显示所有讲师
1 修改course.js
2 在页面进行调用
3 封装数据
4 初始化讲师
5 效果显示
一级、二级分类做二级联动
1 在info.vue引入subject
2 方法调用接口得到值 初始化 然后赋值
3 在下拉列表遍历显示
4 效果展示
级联显示二级分类
思路:给一级分类绑定change事件 值一改变就会取到二级分类的值
1 模板
<el-select v-model="courseInfo.subjectId" placeholder="二级分类">
<el-option
v-for="subject in subjectTwoList"
:key="subject.id"
:label="subject.title"
:value="subject.id"
/>
</el-select>
2 注册change事件
3 定义change事件
4 每次点击一级分类 清空二级分类
5 效果
上传封面功能
1 上传组件
<!-- 课程封面 TODO -->
<!-- 课程封面-->
<el-form-item label="课程封面">
<el-upload
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
:action="BASE_API + '/eduoss/fileoss'"
class="avatar-uploader"
>
<img :src="courseInfo.cover" />
</el-upload>
</el-form-item>
2 定义方法
//上传封面成功调用的方法
handleAvatarSuccess(res,file) {
this.courseInfo.cover = res.data.url
},
//上传直接调用的方法
beforeAvatarUpload(file) {
const isJPG = file.type === "image/jpeg";
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error("上传头像图片只能是 JPG 格式!");
}
if (!isLt2M) {
this.$message.error("上传头像图片大小不能超过 2MB!");
}
return isJPG && isLt2M;
},
3 设置默认图片
4效果4 效果显示
2.6.2 课程程列表
·