在线教育开发项目之课程信息业务

举报
tea_year 发表于 2025/06/30 23:43:10 2025/06/30
【摘要】 课程发布之课程信息课程目标1) 课程分类删除实现2) 编写课程基本信息显示3) 课程分类二级联动4) 讲师下拉表显示1、 课程分类删除请求路径:http://localhost:8001/ebs/subject/1263632523339075586结果:1. 后端实现1.1. 添加删除业务逻辑EbsSubjectController/** * 删除分类 * * @param id * @r...

课程发布之课程信息

课程目标

1) 课程分类删除实现

2) 编写课程基本信息显示

3) 课程分类二级联动

4) 讲师下拉表显示

1、 课程分类删除

请求路径:

http://localhost:8001/ebs/subject/1263632523339075586

结果:

1. 后端实现

1.1. 添加删除业务逻辑

EbsSubjectController


/**
* 删除分类
*
* @param id
* @return
*/
@DeleteMapping("/{id}")
public Result deleteSubjectById(@PathVariable("id") String id) {

boolean flag = subjectService.deleteSubjectById(id);
//响应数据
if (flag) {
return Result.ok();

} else {
return Result.error();
}


}


EbsSubjectServiceImpl

/**
* 删除分类
*
* @param id
* @return
*/
@Override
public boolean deleteSubjectById(String id) {
//根据id作为parent_id,从数据库中查询是否有子菜单
QueryWrapper<EbsSubject> wrapper=new QueryWrapper<>();
//拼装条件
wrapper.eq("parent_id",id);
//查询
List<EbsSubject> list = baseMapper.selectList(wrapper);
//判断
if(list.size()==0){
//执行删除
int i = baseMapper.deleteById(id);
if(i>0){
//表示删除成功
return true;
}
}
//表示删除失败
return false;
}



1.2. api 接口


/**

* 课程分类删除功能

* @param {*} subjectId

*/

removeById(subjectId) {

return request({

url: `${api_name}/${subjectId}`,

method: 'delete'

})

}


2. 前端实现

2.1. 准备

安装Chrome插件 Vue.js devtools

参考Element-UI Tree组件

https://element.eleme.cn/#/zh-CN/component/tree

2.2. api

参考Teacher模块

2.3. 组件模板

<el-tree

ref="subjectTree"

:data="subjectList"

:props="defaultProps"

:filter-node-method="filterNode"

:expand-on-click-node="false"

class="filter-tree"

default-expand-all

node-key="id">

<span slot-scope="{ node, data }" class="custom-tree-node">

<span>{{ node.label }}</span>

<span>

<!-- 使用Chrome的Vue插件调试 -->

<el-button

v-if="node.level == 1"

type="text"

size="mini"

@click="() => append(data)">添加</el-button>

<el-button

v-if="node.level == 2"

type="text"

size="mini"

@click="() => remove(node, data)">删除</el-button>

</span>

</span>

</el-tree>


2.4. 组件代码

添加remove方法

remove(node, data) {

console.log(node)

console.log(data)

this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {

confirmButtonText: '确定',

cancelButtonText: '取消',

type: 'warning'

}).then(() => {

return subject.removeById(data.id)

}).then(() => {

// this.fetchNodeList()// 刷新列表

this.$refs.subjectTree.remove(node) // 删除节点(效率高)

this.$message({

type: 'success',

message: '删除成功!'

})

}).catch((response) => { // 失败

if (response === 'cancel') {

this.$message({

type: 'info',

message: '已取消删除'

})

} else {

this.$message({

type: 'error',

message: '删除失败'

})

}

})

},


 2、 课程一级分类添加

1. 后端实现

1.1. controller

EbsSubjectController

@ApiOperation(value = "新增一级分类")
@PostMapping("saveLevelOne")
public Result saveLevelOne(
@ApiParam(name = "subject", value = "课程分类对象", required = true)
@RequestBody EbsSubject subject) {


boolean result = subjectService.saveLevelOne(subject);
if (result) {
return Result.ok();
} else {
return Result.error().message("新增分类失败");
}
}


 

1.2. service

接口 EbsSubjectService

/**
* 新增一级分类
*
* @param subject
* @return
*/
@Override
public boolean saveLevelOne(EbsSubject subject) {

EbsSubject subjectLevelOne = this.existOneSubject(subject.getTitle());
if(subjectLevelOne == null){

return this.save(subject);
}
return false;
}


辅助方法:判断一级分类是否存在

// 判断数据库表是否存在一级分类
private EbsSubject existOneSubject(String name) {
QueryWrapper<EbsSubject> wrapper = new QueryWrapper<>();
//拼接条件
wrapper.eq("title",name);
wrapper.eq("parent_id","0");
//调用方法
EbsSubject ebsSubject = baseMapper.selectOne(wrapper);
return ebsSubject;
}

2. 前端实现

2.1. api

src/api/subject.js 

/**

* 新增分类

* @param {*} subject

*/

saveLevelOne(subject) {

return request({

url: `${api_name}/saveLevelOne`,

method: 'post',

data: subject

})

}

2.2. 组件模板

sec/views/edu/subject/list.vue

添加新增按钮

 <el-button type="text" @click="dialogFormVisible = true">添加一级分类</el-button>

添加新增弹窗表单 

<el-dialog :visible.sync="dialogFormVisible" title="添加分类">

<el-form :model="subject" label-width="120px">

<el-form-item label="分类标题">

<el-input v-model="subject.title"/>

</el-form-item>

</el-form>

<div slot="footer" class="dialog-footer">

<el-button @click="dialogFormVisible = false">取 消</el-button>

<el-button type="primary" @click="appendLevelOne()">确 定</el-button>

</div>

</el-dialog>


2.3. 组件js

sec/views/edu/subject/list.vue

定义表单数据

dialogFormVisible: false,

subject: {

title: '',

parentId: ''

}

 

保存方法

 

appendLevelOne() {

subject.saveLevelOne(this.subject).then(response => {

this.$message({

type: 'success',

message: '保存成功!'

})

this.dialogFormVisible = false// 如果保存成功则关闭对话框

this.fetchNodeList()// 刷新列表

this.subject.title = ''//置空弹窗内容

}).catch((response) => {

// console.log(response)

this.$message({

type: 'error',

message: '保存失败'

})

})

},


3、 课程二级分类添加

1. 后端实现

2. 前端实现

2.1. api

src/api/ebs/subject.js

 


saveLevelTwo(subject) {

return request({

url: `${api_name}/saveLeveTwo`,

method: 'post',

data: subject

})

}



2.2. 组件模板

sec/views/ebs/subject/list.vue

按钮

<el-button

v-if="node.level == 1"

type="text"

size="mini"

@click="() => {dialogFormVisible = true; subject.parentId = data.id}">添加二级分类</el-button>


 

表单:

修改弹出表单的确定按钮绑定的事件


 

<el-button type="primary" @click="append()">确 定</el-button>



2.3. 组件js

sec/views/ebs/subject/list.vue

添加方法 append

 append(data) {

if (!this.subject.parentId) {

this.appendLevelOne()

} else {

this.appendLevelTwo()

}

},


保存方法

 

appendLevelTwo() {

subject.saveLevelTwo(this.subject).then(response => {

this.$message({

type: 'success',

message: '保存成功!'

})

this.dialogFormVisible = false// 如果保存成功则关闭对话框

this.fetchNodeList()// 刷新列表

this.subject.title = ''// 重置类别标题

this.subject.parentId = '' // 重置表单parentId

}).catch((response) => {

// console.log(response)

this.$message({

type: 'error',

message: response.data.message

})

})

}


2.4. controller

EbsSubjectController

@ApiOperation(value = "新增二级分类")
@PostMapping("saveLevelTwo")
public Result saveLevelTwo(
@ApiParam(name = "subject", value = "课程分类对象", required = true)
@RequestBody EbsSubject subject){
boolean result = subjectService.saveLevelTwo(subject);
if(result){
return Result.ok();
}else{
return Result.error().message("保存失败");
}
}


2.5. service

接口 EbsSubjectService

 

boolean saveLevelTwo(Subject subject);


实现 EbsSubjectServiceImpl

/**
* 新增二级分类
*
* @param subject
* @return
*/
@Override
public boolean saveLevelTwo(EbsSubject subject) {


EbsSubject subjectLevelTwo = this.existTwoSubject(subject.getTitle(), subject.getParentId());
if(subjectLevelTwo == null){
return this.save(subject);
}else{
throw new EbsException(20001, "类别已存在");
}

}


辅助方法:判断二级分类是否存在

//判断数据库表是否存在二级分类
private EbsSubject existTwoSubject(String name,String parentid) {
QueryWrapper<EbsSubject> wrapper = new QueryWrapper<>();
//拼接条件
wrapper.eq("title",name);
wrapper.eq("parent_id",parentid);
EbsSubject ebsSubject = baseMapper.selectOne(wrapper);
return ebsSubject;
}



4、 课程发布表单-步骤导航

1. 需求

第一步:编辑课程基本信息

第二步:编辑课程大纲

第三步:课程的发布

2. 配置路由

2.1. 添加路由

 

// 课程管理

{

path: '/ebs/course',

component: Layout,

redirect: '/ebs/course/list',

name: 'Course',

meta: { title: '课程管理', icon: 'form' },

children: [

{

path: 'list',

name: 'EbsCourseList',

component: () => import('@/views/ebs/course/list'),

meta: { title: '课程列表' }

},

{

path: 'info',

name: 'ebsCourseInfo',

component: () => import('@/views/ebs/course/info'),

meta: { title: '发布课程' }

},

{

path: 'info/:id',

name: 'EbsCourseInfoEdit',

component: () => import('@/views/ebs/course/info'),

meta: { title: '编辑课程基本信息', noCache: true },

hidden: true

},

{

path: 'chapter/:id',

name: 'ebsCourseChapterEdit',

component: () => import('@/views/ebs/course/chapter'),

meta: { title: '编辑课程大纲', noCache: true },

hidden: true

},

{

path: 'publish/:id',

name: 'ebsCoursePublishEdit',

component: () => import('@/views/ebs/course/publish'),

meta: { title: '发布课程', noCache: true },

hidden: true

}

]

},


2.2. 添加vue组件


3. 整合步骤条组件

参考 http://element-cn.eleme.io/#/zh-CN/component/steps

3.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() {

console.log('info created')

},

methods: {

next() {

console.log('next')

this.$router.push({ path: '/edu/course/chapter/1' })

}

}

}

</script>



3.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() {

console.log('chapter created')

},

methods: {

previous() {

console.log('previous')

this.$router.push({ path: '/edu/course/info/1' })

},

next() {

console.log('next')

this.$router.push({ path: '/edu/course/publish/1' })

}

}

}

</script>



3.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: '/edu/course/chapter/1' })

},

publish() {

console.log('publish')

this.$router.push({ path: '/edu/course/list' })

}

}

}

</script>



5、 编辑课程基本信息

1. 后台api

1.1. 定义form表单对象

CourseInfoForm.java

 

package com.yxzx.ebs.teacher.entity.form;

@ApiModel(value = "课程基本信息", description = "编辑课程基本信息的表单对象")
@Data
public class CourseInfoForm implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty(value = "课程ID")
@TableId(value = "id", type = IdType.INPUT)
private String id;

@ApiModelProperty(value = "课程讲师ID")
private String teacherId;

@ApiModelProperty(value = "课程专业ID")
private String subjectId;

@ApiModelProperty(value = "课程标题")
private String title;

@ApiModelProperty(value = "课程销售价格,设置为0则可免费观看")
private BigDecimal price;

@ApiModelProperty(value = "总课时")
private Integer lessonNum;

@ApiModelProperty(value = "课程封面图片路径")
private String cover;

@ApiModelProperty(value = "课程简介")
private String description;
}

1.2. 修改CourseDescription主键生成策略

 

@ApiModelProperty(value = "课程ID")

@TableId(value = "id", type = IdType.INPUT)

private String id;



1.3. 定义常量

实体类Course.Java中定义 

package com.yxzx.ebs.teacher.constent;

public class Course {
public static final String COURSE_DRAFT = "Draft";//未发布
public static final String COURSE_NORMAL = "Normal";//已发布
}


1.4. 定义控制层接口

EbsCourseController.java

package com.yxzx.ebs.teacher.controller;


import com.yxzx.ebs.common.Result;
import com.yxzx.ebs.teacher.entity.form.CourseInfoForm;
import com.yxzx.ebs.teacher.service.EbsCourseService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

@Api(description="课程管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/ebs/course")
public class EbsCourseController {

@Autowired
private EbsCourseService courseService;
@ApiOperation(value = "新增课程")
@PostMapping("saveCourseInfo")
public Result saveCourseInfo(
@ApiParam(name = "CourseInfoForm", value = "课程基本信息", required = true)
@RequestBody CourseInfoForm courseInfoForm){

String courseId = courseService.saveCourseInfo(courseInfoForm);
if(!StringUtils.isEmpty(courseId)){
return Result.ok().data("courseId", courseId);
}else{
return Result.error().message("保存失败");
}
}
}

 1.5. 定义业务层方法

接口:EbsCourseService.java 

/**

* 保存课程和课程详情信息

*

*/

String saveCourseInfo(CourseInfoForm courseInfoForm);



实现:CourseServiceImpl.java

注意这里使用了事物

/**
* 新增课程
*
* @param courseInfoForm
* @return
*/
@Autowired
private EbsCourseDescriptionService courseDescriptionService;

@Transactional
@Override
public String saveCourseInfo(CourseInfoForm courseInfoForm) {

//保存课程基本信息
EbsCourse course = new EbsCourse();
course.setStatus(Course.COURSE_DRAFT);
BeanUtils.copyProperties(courseInfoForm, course);
boolean resultCourseInfo = this.save(course);
if(!resultCourseInfo){
throw new EbsException(20001, "课程信息保存失败");
}

//保存课程详情信息
EbsCourseDescription courseDescription = new EbsCourseDescription();
courseDescription.setDescription(courseInfoForm.getDescription());
courseDescription.setId(course.getId());
boolean resultDescription = courseDescriptionService.save(courseDescription);
if(!resultDescription){
throw new EbsException(20001, "课程详情信息保存失败");
}
return course.getId();
}



1.6. Swagger测试

2. 前端实现

2.1. 定义api

 

import request from '@/utils/request'

const api_name = '/ebs/course'

export default {

saveCourseInfo(courseInfo) {

return request({

url: `${api_name}/saveCourseInfo`,

method: 'post',

data: courseInfo

})

}

}

2.2. 组件模板info.vue 

<el-form label-width="120px">

<el-form-item label="课程标题">

<el-input v-model="courseInfo.title" placeholder=" 示例:机器学习项目课:从基础到搭建项目视频课程。专业名称注意大小写"/>

</el-form-item>

<!-- 所属分类 TODO -->

<!-- 课程讲师 TODO -->

<el-form-item label="总课时">

<el-input-number :min="0" v-model="courseInfo.lessonNum" controls-position="right" placeholder="请填写课程的总课时数"/>

</el-form-item>

<!-- 课程简介 TODO -->

<!-- 课程封面 TODO -->

<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="next">保存并下一步</el-button>

</el-form-item>

</el-form>



2.3. 组件js

 

<script>

import course from '@/api/ebs/course'

const defaultForm = {

title: '',

subjectId: '',

teacherId: '',

lessonNum: 0,

description: '',

cover: '',

price: 0

}

export default {

data() {

return {

courseInfo: defaultForm,

saveBtnDisabled: false // 保存按钮是否禁用

}

},

watch: {

$route(to, from) {

console.log('watch $route')

this.init()

}

},

created() {

console.log('info created')

this.init()

},

methods: {

init() {

if (this.$route.params && this.$route.params.id) {

const id = this.$route.params.id

console.log(id)

} else {

this.courseInfo = { ...defaultForm }

}

},

next() {

console.log('next')

this.saveBtnDisabled = true

if (!this.courseInfo.id) {

this.saveData()

} else {

this.updateData()

}

},

// 保存

saveData() {

course.saveCourseInfo(this.courseInfo).then(response => {

this.$message({

type: 'success',

message: '保存成功!'

})

return response// 将响应结果传递给then

}).then(response => {

this.$router.push({ path: '/ebs/course/chapter/' + response.data.courseId })

}).catch((response) => {

this.$message({

type: 'error',

message: response.message

})

})

},

updateData() {

this.$router.push({ path: '/ebs/course/chapter/1' })

}

}

}

</script>



6、 课程分类多级联动的实现

1. 需求


2. 获取一级分类

2.1. 组件数据定义

定义在data中

 

subjectNestedList: [],//一级分类列表

subSubjectList: []//二级分类列表



2.2. 组件模板

 

<!-- 所属分类:级联下拉列表 -->

<!-- 一级分类 -->

<el-form-item label="课程类别">

<el-select

v-model="courseInfo.subjectParentId"

placeholder="请选择">

<el-option

v-for="subject in subjectNestedList"

:key="subject.id"

:label="subject.title"

:value="subject.id"/>

</el-select>

</el-form-item>



2.3. 组件脚本

表单初始化时获取一级分类嵌套列表,引入subject api

 

import subject from '@/api/ebs/subject'



定义方法初始化数据

 

init() {

......

// 初始化分类列表

this.initSubjectList()

},

initSubjectList() {

subject.getNestedTreeList().then(response => {

this.subjectNestedList = response.data.items

})

},



3. 级联显示二级分类

3.1. 组件模板

 

<!-- 二级分类 -->

<el-select v-model="courseInfo.subjectId" placeholder="请选择">

<el-option

v-for="subject in subSubjectList"

:key="subject.value"

:label="subject.title"

:value="subject.id"/>

</el-select>



3.2. 注册change事件

在一级分类的<el-select>组件中注册change事件

 

<el-select @change="subjectLevelOneChanged" ......


3.3. 定义change事件方法

 

subjectLevelOneChanged(value) {

console.log(value)

for (let i = 0; i < this.subjectNestedList.length; i++) {

if (this.subjectNestedList[i].id === value) {

this.subSubjectList = this.subjectNestedList[i].children

this.courseInfo.subjectId = ''

}

}

},


7、 讲师下拉列表

1. 前端实现

1.1. 组件模板

<!-- 课程讲师 -->

<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>



1.2. 定义api

api/ebs/teacher.js

getList() {

return request({

url: api_name,

method: 'get'

})

},


 

Info.vue组件中引入teacher api

 

import teacher from '@/api/ebs/teacher'



1.3. 组件脚本

定义data

 

teacherList: [] // 讲师列表


表单初始化时获取讲师列表

 

init() {

......

// 获取讲师列表

this.initTeacherList()

},

initTeacherList() {

teacher.getList().then(response => {

this.teacherList = response.data.items

})

},



【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。