课程管理-course_plan课程计划接口

课程计划

需求分析

什么是课程计划?

课程计划定义了课程的章节内容,学生通过课程计划进行在线学习,下图中右侧显示的就是课程计划。

课程计划包括两级,第一级是课程的大章节、第二级是大章节下属的小章节,每个小章节通常是一段视频,学生点

击小章节在线学习。

教学管理人员对课程计划如何管理?

功能包括:添加课程计划、删除课程计划、修改课程计划等。

1566121049657

课程计划查询接口

需求分析

课程计划查询是将某个课程的课程计划内容完整的显示出来,如下图所示:

1566121033992

左侧显示的就是课程计划,课程计划是一个树型结构,方便扩展课程计划的级别。

在上边页面中,点击“添加课程计划”即可对课程计划进行添加操作。

点击修改可对某个章节内容进行修改。

点击删除可删除某个章节。

页面原型

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语法,如下所示:

1566121011502

JSX 是Javascript和XML结合的一种格式,它是React的核心组成部分,JSX和XML语法类似,可以定义属性以及子元

素。唯一特殊的是可以用大括号来加入JavaScript表达式。遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;

遇到代码块(以 { 开头),就用 JavaScript 规则解析。

下面是官方的一个例子:

设置方法 如下:

1、Javascript version 选择 React JSX (如果没有就选择JSX Harmony)

1566120994294

2、HTML 类型文件中增加vue

1566120977328

preferences -> Editor -> File Types 中找到上边框中HTML 在下边加一个 *.vue

如果已经在vue template 中已存在.vue 则把它改为*.vue2(*因为要在*Html*中添加.vue)

1566120958242

API接口

数据模型

1、表结构

1566120941368

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、向新建课程中添加课程计划

添加一级结点

添加二级结点

HuangRui

Every man dies, not every man really lives.

HaungRui, China suixinblog.cn