一、课程管理
1.1 需求分析
在线教育平台的课程信息相当于电商平台的商品。课程管理是后台管理功能中最重要的模块。本项目为教学机构提供课程管理功能,教学机构可以添加属于自己的课程,供学生在线学习。
课程管理包括如下功能需求:
1、分类管理
2、新增课程
3、修改课程
4、预览课程
5、发布课程
1.2 环境搭建
1.2.1 前端工程
与系统管理类似,直接复制导入
1.2.2 后端工程
持久层技术介绍:
课程管理服务使用MySQL数据库存储课程信息,持久层技术如下:
1、spring data jpa:用于表的基本CRUD。
2、mybatis:用于复杂的查询操作。
3、druid:使用阿里巴巴提供的spring boot 整合druid包druid-spring-boot-starter管理连接池。
二、课程计划
一个树状列表:

涉及到的功能:添加课程计划、删除课程计划、修改课程计划
2.1 课程计划查询
2.1.1 需求分析
课程计划查询是将某个课程的课程计划内容完整的显示出来,如下图所示:

左侧显示的就是课程计划,课程计划是一个树型结构,方便扩展课程计划的级别。
点击“添加课程计划”即可对课程计划进行添加操作。
点击修改可对某个章节内容进行修改。
点击删除可删除某个章节。
2.1.2 页面原型
本功能使用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'
}]
}]
}],
3、renderContent
本组件用到了JSX语法,如下所示:
renderContent(h, { node, data, store }) {
return (
<span style="flex: 1; display: flex; align-items: center; justify-content: space-between; font-size: 14px; padding-right: 8px;">
<span>
<span>{node.label}</span>
</span>
<span>
<el-button style="font-size: 12px;" type="text" on-click={ () => this.choosevideo(data) }>{data.mediaFileOriginalName} 选择视频</el-button>
<el-button style="font-size: 12px;" type="text" on-click={ () => this.edit(data) }>修改</el-button>
<el-button style="font-size: 12px;" type="text" on-click={ () => this.remove(node, data) }>删除</el-button>
</span>
</span>);
}
JSX 是Javascript和XML结合的一种格式,它是React的核心组成部分,JSX和XML语法类似,可以定义属性以及子元素。唯一特殊的是可以用大括号来加入JavaScript表达式。遇到 HTML 标签(以 < 开头),就遇到代码块(以 { 开头),就用 JavaScript 规则解析。
2.1.3 API接口
2.1.3.1 数据模型
1、表结构

2、pojo类
课程计划为树型结构,由树根(课程)和树枝(章节)组成,为了保证系统的可扩展性,在系统设计时将课程计划设置为树型结构。
package com.xuecheng.framework.domain.course;
import lombok.Data;
import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author 98050
*/
@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;
}
2.1.3.2 自定义Tree组件数据
前端页面需要树型结构的数据来展示Tree组件,如下:
{
id: 1,
pname: '一级 1',
children: [{
id: 4,
pname: '二级 1-1',
children: [{
id: 9,
pname: '三级 1-1-1'
}, {
id: 10,
pname: '三级 1-1-2'
}]
}]
}
所以需要自定义数据类型:
package com.xuecheng.framework.domain.course.ext;
import com.xuecheng.framework.domain.course.Teachplan;
import lombok.Data;
import lombok.ToString;
import java.util.List;
/**
* @author 98050
*/
@Data
@ToString
public class TeachplanNode extends Teachplan {
List<TeachplanNode> children;
}
2.1.3.3 接口定义
根据课程id查询课程的计划接口如下,在api工程创建course包,创建CourseControllerApi接口类并定义接口方法如下:
package com.xuecheng.api.course;
import com.xuecheng.framework.domain.course.ext.TeachplanNode;
import io.swagger.annotations.*;
/**
* @Author: 98050
* @Time: 2019-04-03 19:59
* @Feature:
*/
@Api(value = "课程管理接口",description = "课程管理接口")
public interface CourseControllerApi {
@ApiOperation("课程计划查询")
@ApiImplicitParams({
@ApiImplicitParam(name = "id",value = "课程Id",required = true,paramType = "path",dataType = "String")
})
@ApiResponses({
@ApiResponse(code = 10000,message = "操作成功"),
@ApiResponse(code = 11111,message = "操作失败")
})
TeachplanNode findTeachplanList(String courseId);
}
2.1.4 课程管理服务
2.1.4.1 sql
课程计划是树型结构,采用表的自连接方式进行查询,sql语句如下:
SELECT
a.id one_id,
a.pname one_name,
b.id two_id,
b.pname two_name,
c.id three_id,
c.pname three_name
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 = '4028e581617f945f01617f9dabc40000';
ORDER BY a.orderby,b.orderby,c.orderby
结果:

2.1.4.2 Mapper
1、Mapper接口
package com.xuecheng.managecourse.dao;
import com.xuecheng.framework.domain.course.ext.TeachplanNode;
import org.apache.ibatis.annotations.Mapper;
/**
* @Author: 98050
* @Time: 2019-04-03 22:58
* @Feature:
*/
@Mapper
public interface TeachPlanMapper {
/**
* 树形查询所有节点
* @param courseId
* @return
*/
TeachplanNode selectList(String courseId);
}
2、Mapper映射文件
TeachPlanMapper.xml
方法一:采用连接查询
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xuecheng.managecourse.dao.TeachPlanMapper">
<resultMap id="teachPlanMap" type="com.xuecheng.framework.domain.course.ext.TeachplanNode">
<id property="id" column="one_id"></id>
<result property="pname" column="one_name"></result>
<collection property="children" ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
<id property="id" column="two_id"></id>
<result property="pname" column="two_name"></result>
<collection property="children" ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
<id property="id" column="three_id"></id>
<result property="pname" column="three_name"></result>
</collection>
</collection>
</resultMap>
<select id="selectList" parameterType="java.lang.String"
resultMap="teachPlanMap">
SELECT
a.id one_id,
a.pname one_name,
b.id two_id,
b.pname two_name,
c.id three_id,
c.pname three_name
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'
<if test="_parameter != null and _parameter !=''">
AND a.courseid = #{courseId}
</if>
ORDER BY a.orderby,b.orderby,c.orderby
</select>
</mapper>
方法二:递归查询
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xuecheng.managecourse.dao.TeachPlanMapper">
<resultMap id="BaseTreeResultMap" type="com.xuecheng.framework.domain.course.ext.TeachplanNode">
<id property="id" column="id"></id>
<result property="pname" column="pname"></result>
<collection column="id" property="children" select="getNextNodeTree" ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
</collection>
</resultMap>
<resultMap id="NextTreeResultMap" type="com.xuecheng.framework.domain.course.ext.TeachplanNode">
<id property="id" column="id"></id>
<result property="pname" column="pname"></result>
<collection column="id" property="children" select="getNextNodeTree" ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
</collection>
</resultMap>
<select id="getNextNodeTree" resultMap="NextTreeResultMap">
SELECT
<include refid="Base_Column_List"></include>
FROM teachplan
WHERE parentid = #{id}
ORDER BY orderby
</select>
<select id="selectList" parameterType="java.lang.String" resultMap="BaseTreeResultMap">
SELECT
<include refid="Base_Column_List"></include>
FROM teachplan
WHERE parentid = '0'
<if test="_parameter != null and _parameter !=''">
AND courseid = #{courseId}
</if>
ORDER BY orderby
</select>
<sql id="Base_Column_List">
id,pname
</sql>
</mapper>
通过 collection 节点继续调用 getNextNodeTree 方法进行循环调用,collection中的column代表会拿父结点id。
注意:
连接查询:连接表的数量有限
递归查询:嵌套 SQL 执行,这里就存在一个性能上的问题。比如 10 万条数据,需要执行 10 万次 SELECT 语句。所以不推荐数据量级大的树形结构。
2.1.4.3 Service
接口:
package com.xuecheng.managecourse.service;
import com.xuecheng.framework.domain.course.ext.TeachplanNode;
/**
* @Author: 98050
* @Time: 2019-04-03 22:57
* @Feature:
*/
public interface CourseService {
/**
* 查询课程计划
* @param courseId
* @return
*/
TeachplanNode findTeachplanList(String courseId);
}
实现:
package com.xuecheng.managecourse.service.impl;
import com.xuecheng.framework.domain.course.ext.TeachplanNode;
import com.xuecheng.managecourse.dao.TeachPlanMapper;
import com.xuecheng.managecourse.service.CourseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author: 98050
* @Time: 2019-04-03 22:57
* @Feature:
*/
@Service
public class CourseServiceImpl implements CourseService {
@Autowired
public TeachPlanMapper teachPlanMapper;
@Override
public TeachplanNode findTeachplanList(String courseId) {
return teachPlanMapper.selectList(courseId);
}
}
2.1.4.4 Controller
package com.xuecheng.managecourse.controller;
import com.xuecheng.api.course.CourseControllerApi;
import com.xuecheng.framework.domain.course.ext.TeachplanNode;
import com.xuecheng.managecourse.service.CourseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: 98050
* @Time: 2019-04-03 22:53
* @Feature:
*/
@RestController
@RequestMapping("/course")
public class CourseController implements CourseControllerApi {
@Autowired
private CourseService courseService;
@Override
@GetMapping("/teachplan/list/{courseId}")
public TeachplanNode findTeachplanList(@PathVariable("courseId") String courseId) {
return courseService.findTeachplanList(courseId);
}
}
2.1.4.5 测试
请求:http://localhost:31200/course/teachplan/list/4028e581617f945f01617f9dabc40000
2.1.5 前端页面
2.1.5.1 API方法
// 查询课程计划
export const findTeachplanList = courseid => {
return http.requestQuickGet(apiUrl + '/course/teachplan/list/' + courseid)
}
2.1.5.2 API调用
1、在mounted钩子方法 中查询 课程计划
定义查询课程计划的方法,赋值给数据对象teachplanList
findTeachplan(){
this.teachplanList = []
//查询课程计划
courseApi.findTeachplanList(this.courseid).then(res=>{
if(res && 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'
}
2.1.5.3 测试
修改course_list.vue中课程静态数据:
courses: [
{
id:'4028e581617f945f01617f9dabc40000',
name:'test01',
pic:''
},
{
id:'test02',
name:'test02',
pic:''
}
]
测试结果:

2.2 添加课程计划
2.2.1 需求分析
用户操作流程:
1、进入课程计划页面,点击“添加课程计划”
2、打开添加课程计划页面,输入课程计划信息

上级节点说明:
不选择上级节点表示当前课程计划为该课程的一级节点
当添加该课程在课程计划中还没有节点时要自动添加课程的根节点
3、点击提交
2.2.2 页面原型
添加课程计划采用弹出窗口组件Dialog。
1、视图部分
在course_plan.vue页面添加课程计划的弹出窗口代码:
<el-dialog title="添加课程计划" :visible.sync="teachplayFormVisible" >
<el-form ref="teachplanForm" label-position="left" :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、定义表单提交方法和重置方法
//重置表单
resetForm(){
this.teachplanActive = {}
}
//提交课程计划
addTeachplan(){
}
2.2.3 API接口
添加课程计划
/**
* 课程计划添加
* @param teachplan
* @return
*/
@ApiOperation("课程计划添加")
@ApiResponses({
@ApiResponse(code = 10000,message = "操作成功"),
@ApiResponse(code = 11111,message = "操作失败")
})
TeachPlanResult add(Teachplan teachplan);
在xc-framework-model中com.xuecheng.framework.domain.course.response包下定义TeachPlanResult:
package com.xuecheng.framework.domain.course.response;
import com.xuecheng.framework.domain.course.Teachplan;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.framework.model.response.ResultCode;
/**
* @Author: 98050
* @Time: 2019-04-04 21:38
* @Feature:
*/
public class TeachPlanResult extends ResponseResult {
Teachplan teachplan;
public TeachPlanResult(ResultCode resultCode,Teachplan teachplan){
super(resultCode);
this.teachplan = teachplan;
}
}
2.2.4 Service
课程计划添加流程分析:
1、判断传入后台的Teachplan对象的parentid字段是否为空,如果为空的话,那么就需要根据课程id获取课程计划的根节点,如果无法获取,说明是第一次为该课程添加课程计划,那么就创建课程计划的一级节点,并且返回id。
2、设置parentid字段
3、设置当前课程计划的级别
4、插入数据库
注意:
在新增课程计划的过程中,涉及到多个对象的持久化,所以要进行事务管理,使用
@Transactional注解。
接口:
/**
* 课程计划添加
* @param teachplan
* @return
*/
TeachPlanResult add(Teachplan teachplan);
实现:
package com.xuecheng.managecourse.service.impl;
import com.xuecheng.framework.domain.course.CourseBase;
import com.xuecheng.framework.domain.course.Teachplan;
import com.xuecheng.framework.domain.course.ext.TeachplanNode;
import com.xuecheng.framework.domain.course.response.CourseCode;
import com.xuecheng.framework.domain.course.response.TeachPlanResult;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.managecourse.dao.CourseBaseRepository;
import com.xuecheng.managecourse.dao.TeachPlanMapper;
import com.xuecheng.managecourse.dao.TeachPlanRepository;
import com.xuecheng.managecourse.service.CourseService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.util.List;
import java.util.Optional;
/**
* @Author: 98050
* @Time: 2019-04-03 22:57
* @Feature:
*/
@Service
public class CourseServiceImpl implements CourseService {
private final TeachPlanMapper teachPlanMapper;
private final CourseBaseRepository courseBaseRepository;
private final TeachPlanRepository teachPlanRepository;
@Autowired
public CourseServiceImpl(TeachPlanMapper teachPlanMapper, CourseBaseRepository courseBaseRepository, TeachPlanRepository teachPlanRepository) {
this.teachPlanMapper = teachPlanMapper;
this.courseBaseRepository = courseBaseRepository;
this.teachPlanRepository = teachPlanRepository;
}
/**
* 课程计划查询
* @param courseId
* @return
*/
@Override
public TeachplanNode findTeachplanList(String courseId) {
return teachPlanMapper.selectList(courseId);
}
/**
* 课程计划添加
* 说明:如果当前teachplan对象中parentId为空,说明这是一个二级节点,那么parentId应该为根节点,也就是一级节点
* @param teachplan
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public TeachPlanResult add(Teachplan teachplan) {
//1.校验参数对象
if (teachplan == null) {
ExceptionCast.cast(CourseCode.COURSE_PLAN_ADD_PARAMETERISNULL);
}
//2.取出课程id
String courseId = teachplan.getCourseid();
if (StringUtils.isEmpty(courseId)) {
ExceptionCast.cast(CourseCode.COURSE_PLAN_ADD_COURSEIDISNULL);
}
//3.根据课程id和课程计划名称进行校验
List<Teachplan> list = this.teachPlanRepository.findByCourseidAndPname(courseId, teachplan.getPname());
if (list.size() != 0){
ExceptionCast.cast(CourseCode.COURSE_PLAN_ADD_PLANNAMEISEXISTS);
}
//4.取出父节点id
String parentId = teachplan.getParentid();
if (StringUtils.isEmpty(parentId)) {
parentId = getTeachPlanRoot(courseId);
}
//5.获取父节点信息
Optional<Teachplan> optional = this.teachPlanRepository.findById(parentId);
if (!optional.isPresent()) {
ExceptionCast.cast(CourseCode.COURSE_PLAN_ADD_PARENTNODEISNULL);
}
Teachplan parentNode = optional.get();
//6.设置父节点
teachplan.setParentid(parentId);
//7.未发布
teachplan.setStatus("0");
//8.设置子节点的级别,根据父节点来判断
String level1 = "1";
String level2 = "2";
String level3 = "3";
if (level1.equals(parentNode.getGrade())) {
teachplan.setGrade(level2);
} else if (level2.equals(parentNode.getGrade())) {
teachplan.setGrade(level3);
}
//9.设置课程id
teachplan.setCourseid(parentNode.getCourseid());
//10.保存
Teachplan save = this.teachPlanRepository.save(teachplan);
return new TeachPlanResult(CommonCode.SUCCESS, save);
}
/**
* 根据课程id获取一级节点
* 如果没有找到,说明是第一次添加课程计划,需要构造根节点
* @param courseId
* @return
*/
private String getTeachPlanRoot(String courseId) {
//1.校验课程
Optional<CourseBase> optional = this.courseBaseRepository.findById(courseId);
if (!optional.isPresent()){
ExceptionCast.cast(CourseCode.COURSE_PLAN_ADD_COURSEISNULL);
}
CourseBase courseBase = optional.get();
//2.获取课程计划根节点
List<Teachplan> teachplanList = this.teachPlanRepository.findByCourseidAndParentid(courseId, "0");
if (teachplanList == null || teachplanList.size() == 0){
//3.新增根节点
Teachplan teachplanRoot = new Teachplan();
teachplanRoot.setCourseid(courseId);
teachplanRoot.setPname(courseBase.getName());
teachplanRoot.setParentid("0");
//层级:一级
teachplanRoot.setGrade("1");
//未发布
teachplanRoot.setStatus("0");
Teachplan save = teachPlanRepository.save(teachplanRoot);
return save.getId();
}
Teachplan teachplan = teachplanList.get(0);
return teachplan.getId();
}
}
2.2.5 DAO
根据课程id和父节点id查询根节点
根据课程id和pname查询
package com.xuecheng.managecourse.dao;
import com.xuecheng.framework.domain.course.Teachplan;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/**
* @Author: 98050
* @Time: 2019-04-04 22:31
* @Feature:
*/
public interface TeachPlanRepository extends JpaRepository<Teachplan,String> {
/**
* 根据课程id查询课程计划的根节点
* @param courseId
* @param parentId
* @return
*/
List<Teachplan> findByCourseidAndParentid(String courseId, String parentId);
/**
* 根据课程id和pname进行查询
* @param courseId
* @param panme
* @return
*/
List<Teachplan> findByCourseidAndPname(String courseId,String panme);
}
2.2.6 Controller
@Override
@PostMapping("/teachplan/add")
public TeachPlanResult add(@RequestBody Teachplan teachplan) {
return this.courseService.add(teachplan);
}
2.2.7 测试
使用接口文档进行测试
2.2.8 前端
2.2.8.1 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()
// 重置表单
this.resetForm()
// 关闭表单
this.teachplayFormVisible = false;
}else if (res.message) {
this.$message.error(res.message)
}else {
this.$message.error("添加失败")
}
})
console.log(this.teachplanActive)
}
})
},
2.2.8.2 测试

结果:




被折叠的 条评论
为什么被折叠?



