课程计划
需求分析
什么是课程计划?
课程计划定义了课程的章节内容,学生通过课程计划进行在线学习,下图中右侧显示的就是课程计划。
课程计划包括两级,第一级是课程的大章节、第二级是大章节下属的小章节,每个小章节通常是一段视频,学生点
击小章节在线学习。
教学管理人员对课程计划如何管理?
功能包括:添加课程计划、删除课程计划、修改课程计划等。
课程计划查询接口
需求分析
课程计划查询是将某个课程的课程计划内容完整的显示出来,如下图所示:
左侧显示的就是课程计划,课程计划是一个树型结构,方便扩展课程计划的级别。
在上边页面中,点击“添加课程计划”即可对课程计划进行添加操作。
点击修改可对某个章节内容进行修改。
点击删除可删除某个章节。
页面原型
tree组件介绍
本功能使用element-ui 的tree组件来完成
在course_plan.vue文件中添加tree组件的代码,进行测试:
1、组件标签
<el-tree
:data="teachplanList"
:props="defaultProps"
node-key="id"
default-expand-all
:expand-on-click-node="false"
:render-content="renderContent">
</el-tree>
2、数据对象
teachplanList : [{
id: 1,
pname: '一级 1',
children: [{
id: 4,
pname: '二级 1-1',
children: [{
id: 9,
pname: '三级 1-1-1'
}, {
id: 10,
pname: '三级 1-1-2'
}]
}]
}],
webstorm配置JSX
本组件用到了JSX语法,如下所示:
JSX 是Javascript和XML结合的一种格式,它是React的核心组成部分,JSX和XML语法类似,可以定义属性以及子元
素。唯一特殊的是可以用大括号来加入JavaScript表达式。遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;
遇到代码块(以 { 开头),就用 JavaScript 规则解析。
下面是官方的一个例子:
设置方法 如下:
1、Javascript version 选择 React JSX (如果没有就选择JSX Harmony)
2、HTML 类型文件中增加vue
preferences -> Editor -> File Types 中找到上边框中HTML 在下边加一个 *.vue
如果已经在vue template 中已存在.vue 则把它改为*.vue2(*因为要在*Html*中添加.vue)
API接口
数据模型
1、表结构
2、模型类
课程计划为树型结构,由树根(课程)和树枝(章节)组成,为了保证系统的可扩展性,在系统设计时将课程计划
设置为树型结构。
package com.xuecheng.framework.domain.course;
import lombok.Data;
import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.io.Serializable;
/**
* Created by admin on 2018/2/7.
*/
@Data
@ToString
@Entity
@Table(name="teachplan")
@GenericGenerator(name = "jpa-uuid", strategy = "uuid")
public class Teachplan implements Serializable {
private static final long serialVersionUID = -916357110051689485L;
@Id
@GeneratedValue(generator = "jpa-uuid")
@Column(length = 32)
private String id;
private String pname;
private String parentid;
private String grade;
private String ptype;
private String description;
private String courseid;
private String status;
private Integer orderby;
private Double timelength;
private String trylearn;
}
自定义模型类
前端页面需要树型结构的数据来展示Tree组件,如下:
[{
id: 1,
label: '一级 1',
children: [{
id: 4,
label: '二级 1‐1'
}]
}]
自定义课程计划结点类如下:
@Data
@ToString
public class TeachplanNode extends Teachplan {
List<TeachplanNode> children;
}
接口定义
根据课程id查询课程的计划接口如下,在api工程创建course包,创建CourseControllerApi接口类并定义接口方法
如下:
public interface CourseControllerApi {
@ApiOperation("课程计划查询")
public TeachplanNode findTeachPlanList(String courseId);
}
课程管理服务
Sql
课程计划是树型结构,采用表的自连接方式进行查询,sql语句如下:
SELECT a.id one_id,
a.pname one_pname,
b.id two_id,
b.pname two_pname,
c.id three_id,
c.pname three_pname
FROM
teachplan a
LEFT JOIN teachplan b
ON a.id = b.parentid
LEFT JOIN teachplan c
ON b.id = c.parentid
WHERE
a.parentid = '0'
and a.courseid='402885816243d2dd016243f24c030002'
ORDER BY
a.orderby,
b.orderby,
c.orderby
Dao
1) mapper接口
@Mapper
@component
public interface TeachplanMapper {
public TeachplanNode selectList(String courseId);
}
2)mapper映射文件
<mapper namespace="com.xuecheng.manage_course.dao.TeachplanMapper">
<!-- 通过mybatis将查到的数据映射称为TeachplanNode类型的数据-->
<resultMap id="teachPlanMap" type="com.xuecheng.framework.domain.course.ext.TeachplanNode">
<id property="id" column="one_id"/>
<result property="pname" column="one_pname"/>
<!-- 一对多映射使用collection-->
<collection property="children" ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
<id property="id" column="two_id"/>
<result property="pname" column="two_pname"/>
<collection property="children" ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
<id property="id" column="three_id"/>
<result property="pname" column="three_pname"/>
</collection>
</collection>
</resultMap>
<!--通过表的自连接查到3级节点的信息-->
<select id="selectList" parameterType="java.lang.String"
resultMap="teachPlanMap">
SELECT a.id one_id,
a.pname one_pname,
b.id two_id,
b.pname two_pname,
c.id three_id,
c.pname three_pname
FROM
teachplan a
LEFT JOIN teachplan b
ON a.id = b.parentid
LEFT JOIN teachplan c
ON b.id = c.parentid
WHERE
a.parentid = '0'
<!-- mybatis内置_parameter参数,表示当前以基本数据类型传进来的那个数据,也就是courseId-->
<if test="_parameter!=null and _parameter!=''">
and a.courseid=#{courseId}
</if>
ORDER BY
a.orderby,
b.orderby,
c.orderby
</select>
</mapper>
说明:针对输入参数为简单类型#{}中可以是任意类型,判断参数是否为空要用 _parameter(它属于mybatis的内
置参数)
Service
创建CourseService类,定义查询课程计划方法。
@Service
public class CourseService {
@Autowired
TeachplanMapper teachplanMapper;
//查询课程计划
public TeachplanNode findTeachplanList(String courseId){
TeachplanNode teachplanNode = teachplanMapper.selectList(courseId);
return teachplanNode;
}
}
Controller
@RestController
@RequestMapping("/course")
public class CourseController implements CourseControllerApi {
@Autowired
CourseService courseService;
//查询课程计划
@Override
@GetMapping("/teachplan/list/{courseId}")
public TeachplanNode findTeachplanList(String courseId) {
return courseService.findTeachplanList(courseId);
}
}
前端页面
Api方法
定义课程计划查询的api方法:
/*查询课程计划*/
export const findTeachplanList = courseid => {
return http.requestQuickGet(apiUrl+'/course/teachplan/list/'+courseid)
}
Api调用
1、在mounted钩子方法 中查询 课程计划
定义查询课程计划的方法,赋值给数据对象teachplanList
findTeachplan(){
courseApi.findTeachplanList(this.courseid).then((res) => {
this.teachplanList = [];//清空树
if(res.children){
this.teachplanList = res.children;
}
});
2)在mounted钩子中查询课程计划
mounted(){
//课程id
this.courseid = this.$route.params.courseid;
//课程计划
this.findTeachplan();
}
3)修改树结点的标签属性
课程计划信息中pname为结点的名称,需要修改树结点的标签属性方可正常显示课程计划名称,如下:
defaultProps: {
children: 'children',
label: 'pname'
}
添加课程计划接口
需求分析
用户操作流程:
1、进入课程计划页面,点击“添加课程计划”
2、打开添加课程计划页面,输入课程计划信息
上级结点说明:
不选择上级结点表示当前课程计划为该课程的一级结点。
当添加该课程在课程计划中还没有节点时要自动添加课程的根结点。
3、点击提交。
页面原型说明
添加课程计划采用弹出窗口组件Dialog。
1、视图部分
在course_plan.vue页面添加添加课程计划的弹出窗口代码:
<el-dialog title="添加课程计划" :visible.sync="teachplayFormVisible" >
<el-form ref="teachplanForm" :model="teachplanActive" label-width="140px" style="width:600px;" :rules="teachplanRules" >
<el-form-item label="上级结点" >
<el-select v-model="teachplanActive.parentid" placeholder="不填表示根结点">
<el-option
v-for="item in teachplanList"
:key="item.id"
:label="item.pname"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="章节/课时名称" prop="pname">
<el-input v-model="teachplanActive.pname" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="课程类型" >
<el-radio-group v-model="teachplanActive.ptype">
<el-radio class="radio" label='1'>视频</el-radio>
<el-radio class="radio" label='2'>文档</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="学习时长(分钟) 请输入数字" >
<el-input type="number" v-model="teachplanActive.timelength" auto-complete="off" ></el-input>
</el-form-item>
<el-form-item label="排序字段" >
<el-input v-model="teachplanActive.orderby" auto-complete="off" ></el-input>
</el-form-item>
<el-form-item label="章节/课时介绍" prop="description">
<el-input type="textarea" v-model="teachplanActive.description" ></el-input>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="teachplanActive.status" >
<el-radio class="radio" label="0" >未发布</el-radio>
<el-radio class="radio" label='1'>已发布</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item >
<el-button type="primary" v-on:click="addTeachplan">提交</el-button>
<el-button type="primary" v-on:click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</el-dialog>
2、数据模型
在数据模型中添加如下变量:
teachplayFormVisible:false,//控制添加窗口是否显示
teachplanRules: {
pname: [
{required: true, message: '请输入课程计划名称', trigger: 'blur'}
],
status: [
{required: true, message: '请选择状态', trigger: 'blur'}
]
},
teachplanActive:{},
3、 添加按钮
通过变量teachplayFormVisible控制弹出窗口是否显示。
<el-button type="primary" @click="teachplayFormVisible = true">添加课程计划</el-button>
4、定义表单提交方法和重置方法
//提交课程计划
addTeachplan(){
alert()
},
//重置表单
resetForm(){
this.teachplanActive = {}
},
API接口
1)添加课程计划
@ApiOperation("添加课程计划")
public ResponseResult addTeachplan(Teachplan teachplan);
课程管理服务
Dao
public interface TeachplanRepository extends JpaRepository<Teachplan, String> {
//定义方法根据课程id和父结点id查询出结点列表,可以使用此方法实现查询根结点
public List<Teachplan> findByCourseidAndParentid(String courseId,String parentId);
}
Service
/**
* 添加课程计划
*/
@Transactional
public ResponseResult addTeachPlan(Teachplan teachplan) {
//先判断课程必要属性不为空
if (teachplan == null ||
StringUtils.isEmpty(teachplan.getCourseid()) ||
StringUtils.isEmpty(teachplan.getPname())) {
ExceptionCast.cast(CommonCode.INVALID_PARAM);
}
//获取到页面提交过来的课程id和父节点id
String courseId= teachplan.getCourseid();
String parentId = teachplan.getParentid();
//判断父节点id为是否为空
if (StringUtils.isEmpty(parentId)) {
//取出该课程的根节点,根节点id作为父id
parentId = this.getTeachplanRoot(courseId);
}
//根据父节点id查询上级节点信息
Optional<Teachplan> optional = teachPlanRepository.findById(parentId);
Teachplan parentNode = optional.get();
//获取父节点级别
String grade = parentNode.getGrade();
//新节点
Teachplan teachplanNew = new Teachplan();
//将页面提交的信息拷贝到teachplanNew对象中
BeanUtils.copyProperties(teachplan,teachplanNew);
teachplanNew.setParentid(parentId);
teachplanNew.setCourseid(courseId);
//设置新节点等级
if (grade.equals("1")) {
teachplanNew.setGrade("2");//级别
}else {
teachplanNew.setGrade("3");
}
//保存新节点信息
teachPlanRepository.save(teachplanNew);
return new ResponseResult(CommonCode.SUCCESS);
}
//查询课程的根节点,查不到自动添加根节点
private String getTeachplanRoot(String courseId) {
Optional<CourseBase> optional = courseBaseRepository.findById(courseId);
if (!optional.isPresent()){
return null;
}
CourseBase courseBase = optional.get();
//根据课程id查询到根节点
List<Teachplan> list = teachPlanRepository.findByCourseidAndParentid(courseId, "0");
if (list == null || list.size() <= 0) {
//查询不到,要手动添加一个根节点
Teachplan teachplan = new Teachplan();
teachplan.setParentid("0");
teachplan.setGrade("1");
//计划名采用课程名
teachplan.setPname(courseBase.getName());
teachplan.setCourseid(courseId);
teachplan.setStatus("0");
// 保存根节点信息到数据库
teachPlanRepository.save(teachplan);
// 返回根节点的id
return teachplan.getId();
}
return list.get(0).getId();
}
controller
//添加课程计划
@Override
@PostMapping("/teachplan/add")
public ResponseResult addTeachplan(@RequestBody Teachplan teachplan) {
return courseService.addTeachplan(teachplan);
}
前端页面
Api调用
1、定义 api方法
/*添加课程计划*/
export const addTeachplan = teachplah => {
return http.requestPost(apiUrl+'/course/teachplan/add',teachplah)
}
2、调用 api
//提交课程计划
addTeachplan(){
//校验表单
this.$refs.teachplanForm.validate((valid) => {
if (valid) {
//调用api方法
//将课程id设置到teachplanActive
this.teachplanActive.courseid = this.courseid
courseApi.addTeachplan(this.teachplanActive).then(res=>{
if(res.success){
this.$message.success("添加成功")
//刷新树
this.findTeachplan()
}else{
this.$message.error(res.message)
}
})
}
})
},
测试
测试流程:
1、新建一个课程
2、向新建课程中添加课程计划
添加一级结点
添加二级结点