尚硅谷谷粒学院学习笔记6--EasyExcel,课程相关模块,章节小节

EasyExcel,课程相关模块

EasyExcel

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)

Excel导入导出的应用场景

1、数据导入:减轻录入工作量
2、数据导出:统计信息归档
3、数据传输:异构系统之间数据传输

引入依赖

<dependencies>
    <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.1.1</version>
    </dependency>
</dependencies>

这个又依赖与poi的依赖,而且版本也一定要对应

<dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>3.17</version>
    </dependency>

    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>3.17</version>
    </dependency>

创建实体类

和excel数据对应

@Data
public class ExcelData {
    
    

    //设置excel表头名称
    @ExcelProperty("学生编号")
    private Integer sno;
    @ExcelProperty("学生姓名")
    private String sname;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;


}

EasyExcel对Excel写操作

public static void main(String[] args) throws Exception {
    
    
        //实现excel写的操作
        // 设置写入文件夹地址和excel文件名
        String fileName = "D:\\java.xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        //write方法两个参数 ,第一个参数文件路径名称,第二个参数实体类class
        EasyExcel.write(fileName, ExcelData.class).sheet("第一个sheet").doWrite(data());
    }

    //循环设置要添加的数据,最终封装到list集合中
    private static List<ExcelData> data() {
    
    
        List<ExcelData> list = new ArrayList<ExcelData>();
        for (int i = 0; i < 10; i++) {
    
    
            ExcelData data = new ExcelData();
            data.setSno(i);
            data.setSname("dyk"+i);
            list.add(data);
        }
        return list;
    }

在这里插入图片描述

EasyExcel对Excel读操作

@Data
public class ExcelData {
    
    

    //设置excel表头名称,index设置列对应的属性
    @ExcelProperty(value = "学生编号",index = 0)
    private Integer sno;
    @ExcelProperty(value = "学生姓名",index = 1)
    private String sname;

}

创建读取操作的监听器

package com.atguigu.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
 
//创建读取excel监听器
public class ExcelListener extends AnalysisEventListener<ExcelData> {
    
    
 
    //创建list集合封装最终的数据
    List<ExcelData> list = new ArrayList<ExcelData>();
    private final ArrayList<ExcelData> ExcelData = new ArrayList<ExcelData>();

    //一行一行去读取excle内容
    @Override
    public void invoke(ExcelData user, AnalysisContext analysisContext) {
    
    
       System.out.println("***"+user);
        list.add(user);
    }
 
    //读取excel表头信息
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
    
    
        System.out.println("表头信息:"+headMap);
    }
 
    //读取完成后执行
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    
    
    }
}

读取

public static void main(String[] args) {
    
    
        String fileName = "D:\\java.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, ExcelData.class, new ExcelListener()).sheet().doRead();
    }

在这里插入图片描述

课程分类管理接口

service-edu模块配置依赖

		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.6</version>
        </dependency>

创建和Excel对应的实体类

在entity下新建一个excel包,在excel包下新建一个SubjectData类

@Data
public class SubjectData {
    
    
    @ExcelProperty(index = 0)
    private String oneSubjectName;
    @ExcelProperty(index = 1)
    private String twoSubjectName;
}

EduSubjectController

@RestController
@Api(tags="课程分类管理")
@RequestMapping("/eduservice/subject")
@CrossOrigin
public class EduSubjectController {
    
    

    @Autowired
    private EduSubjectService subjectService;

    //添加课程分类
    //获取上传过来的文件,把内容读取出来,就不用上传到服务器
    @ApiOperation(value = "Excel批量导入")
    @PostMapping("addSubject")
    public ResultVo addSubject(MultipartFile file){
    
    
        // 获取上传的excel文件 MultipartFile
    subjectService.saveSubject(file,subjectService);

        return ResultVo.ok();
    }


}

EduSubjectService

public interface EduSubjectService extends IService<EduSubject> {
    
    

    void saveSubject(MultipartFile file,EduSubjectService subjectService);
}

EduSubjectServiceImpl

@Service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {
    
    

    //添加课程分类
    @Override
    public void saveSubject(MultipartFile file,EduSubjectService subjectService) {
    
    
        try {
    
    
            //文件输入流
            InputStream in= file.getInputStream();
            //调用方法进行读取

            EasyExcel.read(in, SubjectData.class,new SubjectExcelListener(subjectService)).sheet().doRead();
        }catch (Exception e){
    
    
            throw new GuliException(20002,"添加课程分类失败");
        }
    }
}

创建读取Excel监听器

在eduservice下新建一个listener包,SubjectExcelListener 类

package com.atguigu.eduservice.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.atguigu.eduservice.entity.EduSubject;
import com.atguigu.eduservice.entity.excel.SubjectData;
import com.atguigu.eduservice.service.EduSubjectService;
import com.atguigu.servicebase.exceptionhandler.GuliException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {
    
    

    public EduSubjectService subjectService;

    public SubjectExcelListener() {
    
    
    }
    //创建有参数构造,传递subjectService用于操作数据库
    public SubjectExcelListener(EduSubjectService subjectService) {
    
    
        this.subjectService = subjectService;
    }

    @Override
    public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {
    
    
          if(subjectData==null){
    
    
              throw new GuliException(20001,"文件数据为空");
          }
          //一行一行读取,每次读取有两个值,第一个值一级分类,第二个值二级分类
        EduSubject existOneSubject = this.existOneSubject(subjectService, subjectData.getOneSubjectName());
          if(existOneSubject==null){
    
    
              //没有相同的一级分类就进行添加
             existOneSubject=new EduSubject();
             existOneSubject.setParentId("0");
             existOneSubject.setTitle(subjectData.getOneSubjectName());//设置一级分类名称
             subjectService.save(existOneSubject);
          }
          //获取一级分类id值
          String pid=existOneSubject.getId();
          //添加二级分类
        //判断二级分类是否重复

        EduSubject existTwoSubject = this.existTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid);
        if(existTwoSubject==null){
    
    
            //没有相同的一级分类就进行添加
            existTwoSubject=new EduSubject();
            existTwoSubject.setParentId(pid);
            existTwoSubject.setTitle(subjectData.getTwoSubjectName());//设置二级分类名称
            subjectService.save(existTwoSubject);
        }
    }


    //判断一级分类不能重复添加
    private EduSubject existOneSubject(EduSubjectService subjectService,String name){
    
    
        QueryWrapper<EduSubject> wrapper=new QueryWrapper<>();
        wrapper.eq("title",name);
        wrapper.eq("parent_id","0");
        EduSubject oneSubject = subjectService.getOne(wrapper);
        return oneSubject;

    }

    //判断二级分类不能重复添加
    private EduSubject existTwoSubject(EduSubjectService subjectService,String name,String pid){
    
    
        QueryWrapper<EduSubject> wrapper=new QueryWrapper<>();
        wrapper.eq("title",name);
        wrapper.eq("parent_id",pid);
        EduSubject twoSubject = subjectService.getOne(wrapper);
        return twoSubject;

    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    
    

    }
}

课程分类前端

添加路由

{
    
    
    path: '/subject',
    component: Layout,
    redirect: '/subject/list',
    name: '课程分类管理',
    meta: {
    
    
      title: '课程分类管理',
      icon: 'example'
    },
    children: [{
    
    
        path: 'list',
        name: '课程分类列表',
        component: () => import('@/views/edu/subject/list'),
        meta: {
    
    
          title: '课程分类列表',
          icon: 'table'
        }
      },
      {
    
    
        path: 'save',
        name: '添加课程分类',
        component: () => import('@/views/edu/subject/save'),
        meta: {
    
    
          title: '添加课程分类',
          icon: 'tree'
        }
      },
    ]
  },

save.vue

js定义数据

<script>
export default {
    
    
  data() {
    
    
    return {
    
    
      BASE_API: process.env.BASE_API, // 接口API地址
      importBtnDisabled: false, // 按钮是否禁用,
      loading: false,
    };
  }
 } 

template

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

回调函数

methods: {
    
    
    //点击按钮上传文件到接口里面
    submitUpload() {
    
    
      this.importBtnDisabled = true;
      this.loading = true;
      // js: document.getElementById("upload").submit()
      this.$refs.upload.submit();
    },
    //上传成功
    fileUploadSuccess(response) {
    
    
      //提示信息
      this.loading = false;
      this.$message({
    
    
        type: "success",
        message: "添加课程分类成功",
      });
      //跳转课程分类列表
      //路由跳转
      this.$router.push({
    
     path: "/subject/list" });
    },
    //上传失败
    fileUploadError() {
    
    
      this.loading = false;
      this.$message({
    
    
        type: "error",
        message: "添加课程分类失败",
      });
    },
  },

完整版

<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(response) {
    
    
      //提示信息
      this.loading = false;
      this.$message({
    
    
        type: "success",
        message: "添加课程分类成功",
      });
      //跳转课程分类列表
      //路由跳转
      this.$router.push({
    
     path: "/subject/list" });
    },
    //上传失败
    fileUploadError() {
    
    
      this.loading = false;
      this.$message({
    
    
        type: "error",
        message: "添加课程分类失败",
      });
    },
  },
};
</script>

分类列表展示后端

一级分类实体类

@Data
public class OneSubject {
    
    
    private String id;
    private String title;

    //一个一级分类有多个二级分类
    private List<TwoSubject> children=new ArrayList<>();
}

二级分类实体类

@Data
public class TwoSubject {
    
    

    private String id;
    private String title;
}

controller

 @ApiOperation(value = "嵌套数据列表")
    @GetMapping("/getAllSubject")
    //课程分类列表(树形)
    public ResultVo getAllSubject(){
    
    
        //list集合泛型是一级分类,因为一级分类有他本身和二级分类的集合
      List<OneSubject> list= subjectService.getAllOneTwoSubject();
        return ResultVo.ok().data("list",list);
    }

service

List<OneSubject> getAllOneTwoSubject();

impl

@Override
    public List<OneSubject> getAllOneTwoSubject() {
    
    
        //查询所有一级分类 parentid=0
        QueryWrapper<EduSubject> wrapperOne = new QueryWrapper<>();
        wrapperOne.eq("parent_id","0");
        List<EduSubject> oneSubjectList = baseMapper.selectList(wrapperOne);
        //查询所有二级分类 parentid!=0

        QueryWrapper<EduSubject> wrapperTwo = new QueryWrapper<>();
        wrapperOne.ne("parent_id","0");
        List<EduSubject> twoSubjectList = baseMapper.selectList(wrapperTwo);
        //创建list集合,用于存放最终封装的数据
        List<OneSubject> finalSubjectList=new ArrayList<>();
        //封装一级分类
        //查询出来所有的一级分类list集合遍历,得到每一级分类对象,获得每个一级分类对象值
        //封装到要求的list集合里面
        for (int i = 0; i < oneSubjectList.size(); i++) {
    
    
            EduSubject eduSubject = oneSubjectList.get(i);
            OneSubject oneSubject = new OneSubject();
//            oneSubject.setId(eduSubject.getId());
//            oneSubject.setTitle(eduSubject.getTitle());
            //把eduSubject值复制到对应的oneSubject对象里面,两个对象里面的属性相同对应的的自动赋值
            BeanUtils.copyProperties(eduSubject,oneSubject);

            //在一级分类循环遍历查询所有的二级分类
            //创建list集合封装每个一级分类的二级分类
            List<TwoSubject> twoFinalSubjectList=new ArrayList<>();
            //遍历二级分类list集合
            for (int j = 0; j < twoSubjectList.size(); j++) {
    
    
                EduSubject tSubject = twoSubjectList.get(j);
                //如果二级分类的parentid和一级分类的id一样,就把它加入到一级分类
                if(tSubject.getParentId().equals(eduSubject.getId())){
    
    
                    TwoSubject twoSubject = new TwoSubject();
                    BeanUtils.copyProperties(tSubject,twoSubject);
                    twoFinalSubjectList.add(twoSubject);
                }
            }

            //把一级下面所有的二级分类放到一级分类里面
            oneSubject.setChildren(twoFinalSubjectList);
            finalSubjectList.add(oneSubject);

        }

        return finalSubjectList;
    }

分类列表展示前端

创建api

api/edu/subject.js

import request from '@/utils/request'
export default {
    
    
  //1 课程分类列表
  getSubjectList() {
    
    
    return request({
    
    
      url: '/eduservice/subject/getAllSubject',
      method: 'get'
    })
  }
}

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>

优化前端过滤功能

不区分大小写

filterNode(value, data) {
    
    
      if (!value) return true;
      return data.title.toLowerCase().indexOf(value.toLowerCase()) !== -1;
    },

课程发布步骤导航

后台接口

CourseInfoVo

因为课程基本信息和课程简介不在一张表上,

package com.atguigu.eduservice.entity.vo;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.math.BigDecimal;

@Data
public class CourseInfoVo {
    
    
    @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;
}

修改CourseDescription主键生成策略

因为课程简介的id和课程信息的id是对应的,需要自己手动设置成课程信息的id不能自动生成

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

EduCourseController

@RestController
@RequestMapping("/eduservice/course")
@CrossOrigin

@Api(tags="课程管理")
public class EduCourseController {
    
    

    @Autowired
    private EduCourseService courseService;

    //添加课程基本信息
    @ApiOperation(value = "新增课程")
    @PostMapping("/addCourseInfo")
    public ResultVo addCourseInfo(@RequestBody CourseInfoVo courseInfoVo){
    
    
        String id = courseService.saveCourseInfo(courseInfoVo);
        return ResultVo.ok().data("courseId",id);
    }

}

定义业务层方法

String saveCourseInfo(CourseInfoVo courseInfoVo);
 @Autowired
    private EduCourseDescriptionService courseDescriptionService;
    @Override
    public String 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,"添加课程信息失败");
        }
        //获取添加之后的课程id
        String cid=eduCourse.getId();

        //向课程简介表添加课程介绍
        EduCourseDescription CourseDescription = new EduCourseDescription();
        BeanUtils.copyProperties(courseInfoVo,CourseDescription);
        //设置描述id就是课程id
        CourseDescription.setId(cid);
        courseDescriptionService.save(CourseDescription);

        return cid;
    }

前端实现

路由

{
    
    
    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
      }
    ]
  },

定义api

import request from '@/utils/request'
export default {
    
    
    //1 添加课程信息
    addCourseInfo(courseInfo) {
    
    
        return request({
    
    
            url: '/eduservice/course/addCourseInfo',
            method: 'post',
            data:courseInfo
          })
    },
   
}

课程信息页面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: "/course/chapter/" + response.data.courseId,
        });
    },
  },
};
</script>

课程大纲页面 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>

课程发布页面 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>

讲师下拉列表

前端实现

组件模板

 <!-- 课程讲师 -->
      <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>

定义api

api/edu/course.js

import request from '@/utils/request'
export default {
    
    
    //1 添加课程信息
    addCourseInfo(courseInfo) {
    
    
        return request({
    
    
            url: '/eduservice/course/addCourseInfo',
            method: 'post',
            data:courseInfo
          })
    },
    //2 查询所有讲师
    getListTeacher() {
    
    
        return request({
    
    
            url: '/eduservice/teacher/findAll',
            method: 'get'
          })
    }
}

引入course

import course from "@/api/edu/course";

定义data

teacherList: [], //封装所有的讲师

定义方法

//查询所有的讲师
    getListTeacher() {
    
    
      course.getListTeacher().then((response) => {
    
    
        this.teacherList = response.data.items;
      });
    },

调用方法

created() {
    
    
    //初始化所有讲师
    this.getListTeacher();
    //初始化一级分类
    this.getOneSubject();
  }

课程分类多级联动

获取一级分类

组件模板

 <!-- 所属分类 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>

组件数据定义

定义在data中

subjectOneList: [], //一级分类
subjectTwoList: [], //二级分类
定义方法

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

import subject from "@/api/edu/subject";
 //查询所有的一级分类
    getOneSubject() {
    
    
      subject.getSubjectList().then((response) => {
    
    
        this.subjectOneList = response.data.list;
      });

级联显示二级分类

组件模板

<!-- 二级分类 -->
        <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>

注册change事件

在一级分类的组件中注册change事件

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

定义change事件方法

//点击某个一级分类,触发change,显示对应二级分类
    subjectLevelOneChanged(value) {
    
    
      //value就是一级分类id值
      //遍历所有的分类,包含一级和二级
      for (var i = 0; i < this.subjectOneList.length; i++) {
    
    
        //每个一级分类
        var oneSubject = this.subjectOneList[i];
        //判断:所有一级分类id 和 点击一级分类id是否一样
        if (value === oneSubject.id) {
    
    
          //从一级分类获取里面所有的二级分类
          this.subjectTwoList = oneSubject.children;
        }
      }
      //把二级分类id值清空
      this.courseInfo.subjectId = "";
    },

课程封面

组件

 <!-- 课程封面-->
      <el-form-item label="课程封面">
        <el-upload
          :show-file-list="false"
          :on-success="handleAvatarSuccess"
          :before-upload="beforeAvatarUpload"
          :action="BASE_API + '/eduoss/fileoss/upload'"
          class="avatar-uploader"
        >
          <img :src="courseInfo.cover" />
        </el-upload>
      </el-form-item>

定义data数据

BASE_API: process.env.BASE_API, // 接口API地址

上传方法

//上传封面成功调用的方法
    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;
    },

Tinymce可视化编辑器

Tinymce是一个传统javascript插件,默认不能用于Vue.js因此需要做一些特殊的整合步骤

复制脚本库

将脚本库复制到项目的static目录下(在vue-element-admin-master的static路径下)
在这里插入图片描述

配置html变量

在 guli-admin/build/webpack.dev.conf.js 中添加配置
使在html页面中可是使用这里定义的BASE_URL变量

templateParameters: {
    
    
        BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
      }

在这里插入图片描述

引入js脚本

在guli-admin/index.html 中引入js脚本

<script src=<%= BASE_URL %>/tinymce4.7.5/tinymce.min.js></script>

<script src=<%= BASE_URL %>/tinymce4.7.5/langs/zh_CN.js></script>

组件引入

为了让Tinymce能用于Vue.js项目,vue-element-admin-master对Tinymce进行了封装,下面我们将它引入到我们的课程信息页面

复制组件

src/components/Tinymce
在这里插入图片描述

引入组件

课程信息组件中引入 Tinymce

import Tinymce from '@/components/Tinymce'
export default {
    
    
  components: {
    
     Tinymce },

  ......

}

组件模板

<!-- 课程简介-->

      <el-form-item label="课程简介">
        <tinymce :height="300" v-model="courseInfo.description" />
      </el-form-item>

组件样式

在info.vue文件的最后添加如下代码,调整上传图片按钮的高度

<style scoped>
.tinymce-container {
    
    
  line-height: 29px;
}

</style>

章节小节列表显示

后端

在service_edu下面的entity下新建一个chapter包

定义vo

@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(value = "章节信息")
public class ChapterVo {
    
    
    private String id;
    private String title;
    //小结
    private List<VideoVo> children = new ArrayList<>();
}
@ApiModel(value = "小结信息")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class VideoVo {
    
    
    private String id;
    private String title;
}

service

public interface EduChapterService extends IService<EduChapter> {
    
    

    List<ChapterVo> getChapterVideoByCourseId(String courseId);
}

impl

@Autowired
    private EduVideoService videoService;//注入小节service

    @Override
    public List<ChapterVo> getChapterVideoByCourseId(String courseId) {
    
    

       //根据课程id查询课程里面所有章节
        QueryWrapper<EduChapter> eduChapterQueryWrapper = new QueryWrapper<EduChapter>();
        eduChapterQueryWrapper.eq("course_id",courseId);
        List<EduChapter> eduChapterList = baseMapper.selectList(eduChapterQueryWrapper);

        //根据课程id查询课程里面所有的小节
        QueryWrapper<EduVideo> eduVideoQueryWrapper = new QueryWrapper<>();
        eduVideoQueryWrapper.eq("course_id",courseId);
        List<EduVideo> eduVideoList = videoService.list(eduVideoQueryWrapper);

        List<ChapterVo> finalList=new ArrayList<>();

        //遍历查询章节list集合进行封装
        for (int i = 0; i < eduChapterList.size(); i++) {
    
    
            //每个章节
            ChapterVo chapterVo = new ChapterVo();
            EduChapter eduChapter = eduChapterList.get(i);
            BeanUtils.copyProperties(eduChapter,chapterVo);
            //创建集合,用于封装章节的小节
            List<VideoVo> videoList=new ArrayList<>();
            for (int j = 0; j < eduVideoList.size(); j++) {
    
    
                EduVideo eduVideo = eduVideoList.get(j);
                //判断 小节里面的chapterid和章节里面的id是否一样
                if(eduVideo.getChapterId().equals(eduChapter.getId())){
    
    
                        //进行封装
                        VideoVo videoVo=new VideoVo();
                        BeanUtils.copyProperties(eduVideo,videoVo);
                        //放到封装到小节集合
                        videoList.add(videoVo);
                }
            }
            chapterVo.setChildren(videoList);
            finalList.add(chapterVo);

        }
        //遍历查询小结list集合进行封装
        return finalList;
    }

前端

定义api

import request from '@/utils/request'
export default {
    
    
    //1 根据课程id获取章节和小节数据列表
    getAllChapterVideo(courseId) {
    
    
        return request({
    
    
            url: '/eduservice/chapter/getChapterVideo/'+courseId,
            method: 'get'
          })
    },
}    

定义组件脚本


courseId: '', // 所属课程
chapterNestedList: [] // 章节嵌套课时列表
//根据课程id查询章节和小节
    getChapterVideo() {
    
    
      chapter.getAllChapterVideo(this.courseId).then((response) => {
    
    
        this.chapterVideoList = response.data.allChapterVideo;
      });
    },
created() {
    
    
    //获取路由的id值
    if (this.$route.params && this.$route.params.id) {
    
    
      this.courseId = this.$route.params.id;
      //根据课程id查询章节和小节
      this.getChapterVideo();
    }
  },

组件模板

  <el-button type="text" @click="openChapterDialog()">添加章节</el-button>

    <!-- 章节 -->
    <ul class="chanpterList">
      <li v-for="chapter in chapterVideoList" :key="chapter.id">
        <p>
          {
    
    {
    
     chapter.title }}

          <span class="acts">
            <el-button style="" type="text" @click="openVideo(chapter.id)"
              >添加小节</el-button
            >
            <el-button style="" type="text" @click="openEditChatper(chapter.id)"
              >编辑</el-button
            >
            <el-button type="text" @click="removeChapter(chapter.id)"
              >删除</el-button
            >
          </span>
        </p>

        <!-- 视频 -->
        <ul class="chanpterList videoList">
          <li v-for="video in chapter.children" :key="video.id">
            <p>
              {
    
    {
    
     video.title }}

              <span class="acts">
                <el-button style="" type="text">编辑</el-button>
                <el-button type="text" @click="removeVideo(video.id)"
                  >删除</el-button
                >
              </span>
            </p>
          </li>
        </ul>
      </li>
    </ul>
    <div>
      <el-button @click="previous">上一步</el-button>
      <el-button :disabled="saveBtnDisabled" type="primary" @click="next"
        >下一步</el-button
      >
    </div>

定义样式

将样式的定义放在页面的最后
scope表示这里定义的样式只在当前页面范围内生效,不会污染到其他的页面

<style scoped>
.chanpterList {
    
    
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
}
.chanpterList li {
    
    
  position: relative;
}
.chanpterList p {
    
    
  float: left;
  font-size: 20px;
  margin: 10px 0;
  padding: 10px;
  height: 70px;
  line-height: 50px;
  width: 100%;
  border: 1px solid #ddd;
}
.chanpterList .acts {
    
    
  float: right;
  font-size: 14px;
}

.videoList {
    
    
  padding-left: 50px;
}
.videoList p {
    
    
  float: left;
  font-size: 14px;
  margin: 10px 0;
  padding: 10px;
  height: 50px;
  line-height: 30px;
  width: 100%;
  border: 1px dotted #ddd;
}
</style>

课程信息回显

后端

controller

//根据课程id查询课程基本信息

    @ApiOperation(value = "根据ID查询课程")
    @GetMapping("getCourseInfo/{courseId}")
    public ResultVo getCourseInfo(@PathVariable String courseId){
    
    
         CourseInfoVo courseInfoVo=courseService.getCourseInfo(courseId);
         return ResultVo.ok().data("courseInfoVo",courseInfoVo);
    }

service

//通过id查询课程信息
    @Override
    public CourseInfoVo getCourseInfo(String courseId) {
    
    
        //查询课程表
        EduCourse eduCourse=baseMapper.selectById(courseId);
        CourseInfoVo courseInfoVo = new CourseInfoVo();
        BeanUtils.copyProperties(eduCourse,courseInfoVo);

        //查询描述表
        EduCourseDescription courseDescription=courseDescriptionService.getById(courseId);
        BeanUtils.copyProperties(courseDescription,courseInfoVo);


        return courseInfoVo;
    }
   

前端

定义api

api/edu/course.js

//根据课程id查询课程基本信息
  getCourseInfoId(id) {
    
    
    return request({
    
    
      url: '/eduservice/course/getCourseInfo/' + id,
      method: 'get'
    })
  },

修改created方法

created() {
    
    
    //获取路由id值
    if (this.$route.params && this.$route.params.id) {
    
    
      this.courseId = this.$route.params.id;
      //调用根据id查询课程的方法
      this.getInfo();
    } else {
    
    
      //初始化所有讲师
      this.getListTeacher();
      //初始化一级分类
      this.getOneSubject();
    }
  },

修改getInfo()

 //根据课程id查询
    getInfo() {
    
    
      course.getCourseInfoId(this.courseId).then((response) => {
    
    
        //在courseInfo课程基本信息,包含 一级分类id 和 二级分类id
        this.courseInfo = response.data.courseInfoVo;
        //1 查询所有的分类,包含一级和二级
        subject.getSubjectList().then((response) => {
    
    
          //2 获取所有一级分类
          this.subjectOneList = response.data.list;
          //3 把所有的一级分类数组进行遍历,
          for (var i = 0; i < this.subjectOneList.length; i++) {
    
    
            //获取每个一级分类
            var oneSubject = this.subjectOneList[i];
            //比较当前courseInfo里面一级分类id和所有的一级分类id
            if (this.courseInfo.subjectParentId == oneSubject.id) {
    
    
              //获取一级分类所有的二级分类
              this.subjectTwoList = oneSubject.children;
            }
          }
        });
        //初始化所有讲师
        this.getListTeacher();
      });
    },

更新课程信息

后端

service

public void updateCourseInfo(CourseInfoVo courseInfoVo) {
    
    
         //修改课程表
        EduCourse eduCourse=new EduCourse();
        BeanUtils.copyProperties(courseInfoVo,eduCourse);
        int i = baseMapper.updateById(eduCourse);
        if(i==0){
    
    
            throw new GuliException(20001,"修改课程信息失败");

        }
        EduCourseDescription courseDescription = new EduCourseDescription();
        BeanUtils.copyProperties(courseInfoVo,courseDescription);

        courseDescriptionService.updateById(courseDescription);

    }

controller

@ApiOperation(value = "更新课程")
    @PostMapping("/updateCourseInfo")
    public ResultVo updateCourseInfo(@RequestBody CourseInfoVo courseInfoVo){
    
    
        courseService.updateCourseInfo(courseInfoVo);
        return ResultVo.ok();
    }

前端

api

//修改课程信息
  updateCourseInfo(courseInfo) {
    
    
    return request({
    
    
      url: '/eduservice/course/updateCourseInfo',
      method: 'post',
      data: courseInfo
    })
  }

updateCourse方法

//修改课程
    updateCourse() {
    
    
      course.updateCourseInfo(this.courseInfo).then((response) => {
    
    
        //提示
        this.$message({
    
    
          type: "success",
          message: "修改课程信息成功!",
        });
        //跳转到第二步
        this.$router.push({
    
     path: "/course/chapter/" + this.courseId });
      });
    },

完整版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="课程简介">
        <tinymce :height="300" v-model="courseInfo.description" />
      </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";
import subject from "@/api/edu/subject";
import Tinymce from "@/components/Tinymce"; //引入组件

export default {
    
    
  //声明组件
  components: {
    
     Tinymce },
  data() {
    
    
    return {
    
    
      saveBtnDisabled: false,
      courseInfo: {
    
    
        title: "",
        subjectId: "", //二级分类id
        subjectParentId: "", //一级分类id
        teacherId: "",
        lessonNum: 0,
        description: "",
        cover: "/static/01.jpg",
        price: 0,
      },
      courseId: "",
      BASE_API: process.env.BASE_API, // 接口API地址
      teacherList: [], //封装所有的讲师
      subjectOneList: [], //一级分类
      subjectTwoList: [], //二级分类
    };
  },
  created() {
    
    
    //获取路由id值
    if (this.$route.params && this.$route.params.id) {
    
    
      this.courseId = this.$route.params.id;
      //调用根据id查询课程的方法
      this.getInfo();
    } else {
    
    
      //初始化所有讲师
      this.getListTeacher();
      //初始化一级分类
      this.getOneSubject();
    }
  },
  methods: {
    
    
    //根据课程id查询
    getInfo() {
    
    
      course.getCourseInfoId(this.courseId).then((response) => {
    
    
        //在courseInfo课程基本信息,包含 一级分类id 和 二级分类id
        this.courseInfo = response.data.courseInfoVo;
        //1 查询所有的分类,包含一级和二级
        subject.getSubjectList().then((response) => {
    
    
          //2 获取所有一级分类
          this.subjectOneList = response.data.list;
          //3 把所有的一级分类数组进行遍历,
          for (var i = 0; i < this.subjectOneList.length; i++) {
    
    
            //获取每个一级分类
            var oneSubject = this.subjectOneList[i];
            //比较当前courseInfo里面一级分类id和所有的一级分类id
            if (this.courseInfo.subjectParentId == oneSubject.id) {
    
    
              //获取一级分类所有的二级分类
              this.subjectTwoList = oneSubject.children;
            }
          }
        });
        //初始化所有讲师
        this.getListTeacher();
      });
    },
    //上传封面成功调用的方法
    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;
    },
    //点击某个一级分类,触发change,显示对应二级分类
    subjectLevelOneChanged(value) {
    
    
      //value就是一级分类id值
      //遍历所有的分类,包含一级和二级
      for (var i = 0; i < this.subjectOneList.length; i++) {
    
    
        //每个一级分类
        var oneSubject = this.subjectOneList[i];
        //判断:所有一级分类id 和 点击一级分类id是否一样
        if (value === oneSubject.id) {
    
    
          //从一级分类获取里面所有的二级分类
          this.subjectTwoList = oneSubject.children;
          //把二级分类id值清空
          this.courseInfo.subjectId = "";
        }
      }
    },
    //查询所有的一级分类
    getOneSubject() {
    
    
      subject.getSubjectList().then((response) => {
    
    
        this.subjectOneList = response.data.list;
      });
    },
    //查询所有的讲师
    getListTeacher() {
    
    
      course.getListTeacher().then((response) => {
    
    
        this.teacherList = response.data.items;
      });
    },
    //添加课程
    addCourse() {
    
    
      course.addCourseInfo(this.courseInfo).then((response) => {
    
    
        //提示
        this.$message({
    
    
          type: "success",
          message: "添加课程信息成功!",
        });
        //跳转到第二步
        this.$router.push({
    
    
          path: "/course/chapter/" + response.data.courseId,
        });
      });
    },
    //修改课程
    updateCourse() {
    
    
      course.updateCourseInfo(this.courseInfo).then((response) => {
    
    
        //提示
        this.$message({
    
    
          type: "success",
          message: "修改课程信息成功!",
        });
        //跳转到第二步
        this.$router.push({
    
     path: "/course/chapter/" + this.courseId });
      });
    },
    saveOrUpdate() {
    
    
      //判断添加还是修改
      if (!this.courseInfo.id) {
    
    
        //添加
        this.addCourse();
      } else {
    
    
        this.updateCourse();
      }
    },
  },
};
</script>
<style scoped>
.tinymce-container {
    
    
  line-height: 29px;
}
</style>

章节管理

后端

controller

新增章节
//添加章节
    @ApiOperation(value = "新增章节")
    @PostMapping("addChapter")
    @ApiParam(name = "eduChapter", value = "章节对象", required = true)
    public ResultVo addChapter(@RequestBody EduChapter eduChapter){
    
    
        chapterService.save(eduChapter);
        return ResultVo.ok();

    }
根据id查询
//根据章节id查询
    @ApiOperation(value = "根据ID查询章节")
    @ApiParam(name = "chapterId", value = "章节ID", required = true)
    @GetMapping("getChapterInfo/{chapterId}")
    public ResultVo getChapterInfo(@PathVariable String chapterId){
    
    
        EduChapter eduChapter= chapterService.getById(chapterId);
        return ResultVo.ok().data("chapter",eduChapter);
    }
更新
//修改章节

    @ApiOperation(value = "根据ID修改章节")
    @ApiParam(name = "eduChapter", value = "章节对象", required = true)
    @GetMapping("updateChapter")

    public ResultVo updateChapter(@RequestBody EduChapter eduChapter){
    
    
        chapterService.updateById(eduChapter);
        return ResultVo.ok();
    }
删除
//删除章节

    @ApiOperation(value = "根据ID删除章节")
    @ApiParam(name = "chapterId", value = "章节ID", required = true)
    @DeleteMapping("{chapterId}")
    public ResultVo deleteChapter(@PathVariable String chapterId){
    
    
        boolean flag=chapterService.deleteChapter(chapterId);
        if(flag){
    
    
            return ResultVo.ok();
        }
        else{
    
    
            return ResultVo.error();
        }

    }

Service

ChapterService层:接口
boolean deleteChapter(String chapterId);
实现类
//删除章节
    @Override
    public boolean deleteChapter(String chapterId) {
    
    
        //根据chapterId章节id查询小节表,如果查询数据,不进行删除
        QueryWrapper<EduVideo> QueryWrapper = new QueryWrapper<>();
        QueryWrapper.eq("chapter_id",chapterId);
        int count = videoService.count(QueryWrapper);
        //判断
        if(count>0){
    
    //能查询出来小节,不进行删除
            throw new GuliException(20001,"不能删除");
        }else {
    
     //没有查询到数据,进行删除
            //删除章节
            int result=baseMapper.deleteById(chapterId);
            return result>0;
        }

    }

前端

定义api

//添加章节
    addChapter(chapter) {
    
    
        return request({
    
    
            url: '/eduservice/chapter/addChapter',
            method: 'post',
            data: chapter
          })
    },
    //根据id查询章节
    getChapter(chapterId) {
    
    
        return request({
    
    
            url: '/eduservice/chapter/getChapterInfo/'+chapterId,
            method: 'get'
          })
    },
    //修改章节
    updateChapter(chapter) {
    
    
        return request({
    
    
            url: '/eduservice/chapter/updateChapter',
            method: 'post',
            data: chapter
          })
    },
    //删除章节
    deleteChapter(chapterId) {
    
    
        return request({
    
    
            url: '/eduservice/chapter/'+chapterId,
            method: 'delete'
          })
    },

定义data数据

chapter: {
    
    
        //封装章节数据
        title: "",
        sort: 0,
      },
dialogChapterFormVisible: false, //章节弹框      

章节表单dialog

<!-- 添加和修改章节表单 -->
    <el-dialog :visible.sync="dialogChapterFormVisible" title="添加章节">
      <el-form :model="chapter" label-width="120px">
        <el-form-item label="章节标题">
          <el-input v-model="chapter.title" />
        </el-form-item>
        <el-form-item label="章节排序">
          <el-input-number
            v-model="chapter.sort"
            :min="0"
            controls-position="right"
          />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogChapterFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="saveOrUpdate">确 定</el-button>
      </div>
    </el-dialog>

添加章节按钮

 <el-button type="text" @click="openChapterDialog()">添加章节</el-button>

添加章节methods

//添加章节
    addChapter() {
    
    
      //设置课程id到chapter对象里面
      this.chapter.courseId = this.courseId;
      chapter.addChapter(this.chapter).then((response) => {
    
    
        //关闭弹框
        this.dialogChapterFormVisible = false;
        //提示
        this.$message({
    
    
          type: "success",
          message: "添加章节成功!",
        });
        //刷新页面
        this.getChapterVideo();
      });
    },
修改章节信息
<el-button style="" type="text" @click="openEditChatper(chapter.id)">编辑</el-button>

定义编辑方法


//修改章节弹框数据回显
    openEditChatper(chapterId) {
    
    
      //弹框
      this.dialogChapterFormVisible = true;
      //调用接口
      chapter.getChapter(chapterId).then((response) => {
    
    
        this.chapter = response.data.chapter;
      });
    },
    

定义更新方法

//修改章节的方法
    updateChapter() {
    
    
      chapter.updateChapter(this.chapter).then((response) => {
    
    
        //关闭弹框
        this.dialogChapterFormVisible = false;
        //提示
        this.$message({
    
    
          type: "success",
          message: "修改章节成功!",
        });
        //刷新页面
        this.getChapterVideo();
      });
    },

删除章节按钮

<el-button type="text" @click="removeChapter(chapter.id)">删除</el-button>

定义删除方法

//删除章节
    removeChapter(chapterId) {
    
    
      this.$confirm("此操作将删除章节, 是否继续?", "提示", {
    
    
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
    
    
        //点击确定,执行then方法
        //调用删除的方法
        chapter.deleteChapter(chapterId).then((response) => {
    
    
          //删除成功
          //提示信息
          this.$message({
    
    
            type: "success",
            message: "删除成功!",
          });
          //刷新页面
          this.getChapterVideo();
        });
      }); //点击取消,执行catch方法
    },

弹出添加章节页面清空

//弹出添加章节页面
    openChapterDialog() {
    
    
      //弹框
      this.dialogChapterFormVisible = true;
      //表单数据清空
      this.chapter.title = "";
      this.chapter.sort = 0;
    },

小节管理

后端

定义VideoInfoVo对象

但是我偷懒了没有使用这个对象,直接用的EduVideo

package com.atguigu.eduservice.entity.video;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@ApiModel(value = "课时基本信息", description = "编辑课时基本信息的表单对象")

@Data
public class VideoInfoVo {
    
    
    @ApiModelProperty(value = "视频ID")
    private String id;

    @ApiModelProperty(value = "节点名称")
    private String title;

    @ApiModelProperty(value = "课程ID")
    private String courseId;

    @ApiModelProperty(value = "章节ID")
    private String chapterId;

    @ApiModelProperty(value = "视频资源")
    private String videoSourceId;

    @ApiModelProperty(value = "显示排序")
    private Integer sort;

    @ApiModelProperty(value = "是否可以试听:0默认 1免费")
    private Boolean free;
}

controller

新增课时
 //添加小节
     @ApiOperation(value = "新增课时")
   @PostMapping("/addVideo")
    public ResultVo addVideo(@RequestBody EduVideo eduVideo){
    
    
       boolean save = videoService.save(eduVideo);
       if(save){
    
    
           return ResultVo.ok();
       }
       else{
    
    
           return ResultVo.error();
       }
   }

课时的修改

//根据ID查询课时
    @ApiParam(name = "id", value = "课时ID", required = true)
    @ApiOperation(value = "根据ID查询课时")
    @GetMapping("getVideoInfo/{id}")
    public ResultVo getVideoInfo(@PathVariable String id){
    
    
        EduVideo video = videoService.getById(id);
        return ResultVo.ok().data("video",video);
    }
    //修改小节
    @ApiOperation(value = "更新课时")
    @PostMapping("updateVideo")
    public ResultVo updateCourseInfo(@RequestBody EduVideo eduVideo){
    
    
        boolean update = videoService.updateById(eduVideo);
        if(update){
    
    
            return ResultVo.ok();
        }
        else{
    
    
            return ResultVo.error();
        }
    }

课时的删除

//删除小节

    @PostMapping("{id}")
    @ApiOperation(value = "根据ID删除课时")
    @ApiParam(name = "id", value = "课时ID", required = true)
    public ResultVo deleteVideo(@PathVariable String id){
    
    
        boolean b = videoService.removeById(id);
        if(b){
    
    
            return ResultVo.ok();
        }
        else{
    
    
            return ResultVo.error();
        }
    }

前端

定义api

import request from '@/utils/request'
export default {
    
    

  //添加小节
  addVideo(video) {
    
    
    return request({
    
    
      url: '/eduservice/video/addVideo',
      method: 'post',
      data: video
    })
  },

  //获取小节信息
  getVideoInfo(id) {
    
    
    return request({
    
    
      url: '/eduservice/video/getVideoInfo/' + id,
      method: 'get',

    })
  },

  updateVideoInfo(video) {
    
    
    return request({
    
    
      url: '/eduservice/video/updateVideo',
      method: 'post',
      data: video
    })
  },

  //删除小节
  deleteVideo(id) {
    
    
    return request({
    
    
      url: '/eduservice/video/' + id,
      method: 'post'
    })
  },
}

定义data数据

video: {
    
    
        title: "",
        sort: 0,
        free: 0,
        videoSourceId: "",
      },
      dialogChapterFormVisible: false, //章节弹框
      dialogVideoFormVisible: false, //小节弹框

添加课时按钮

<el-button style="" type="text" @click="openVideo(chapter.id)" >添加小节</el-button>

课时表单dialog

<!-- 添加和修改课时表单 -->
    <el-dialog :visible.sync="dialogVideoFormVisible" title="添加课时">
      <el-form :model="video" label-width="120px">
        <el-form-item label="课时标题">
          <el-input v-model="video.title" />
        </el-form-item>
        <el-form-item label="课时排序">
          <el-input-number
            v-model="video.sort"
            :min="0"
            controls-position="right"
          />
        </el-form-item>
        <el-form-item label="是否免费">
          <el-radio-group v-model="video.free">
            <el-radio :label="true">免费</el-radio>
            <el-radio :label="false">默认</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="上传视频">
          <!-- TODO -->
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVideoFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="saveOrUpdateVideo">确 定</el-button>
      </div>
    </el-dialog>

引入video模块

import video1 from "@/api/edu/video";
//添加小节弹框的方法
    openVideo(chapterId) {
    
    
      //弹框
      this.dialogVideoFormVisible = true;
      //设置章节id
      this.video.chapterId = chapterId;
    },
    //添加小节
    addVideo() {
    
    
      //设置课程id
      this.video.courseId = this.courseId;
      video1.addVideo(this.video).then((response) => {
    
    
        //关闭弹框
        this.dialogVideoFormVisible = false;
        //提示
        this.$message({
    
    
          type: "success",
          message: "添加小节成功!",
        });
        //刷新页面
        this.getChapterVideo();
      });
    },
    openEditVideo(videoId) {
    
    
      //弹框
      this.dialogVideoFormVisible = true;
      //调用接口
      video1.getVideoInfo(videoId).then((response) => {
    
    
        this.video = response.data.video;
      });
    },
    updateDataVideo() {
    
    
      video1.updateVideoInfo(this.video).then((response) => {
    
    
        //关闭弹框
        this.dialogChapterFormVisible = false;
        //提示
        this.$message({
    
    
          type: "success",
          message: "修改小节成功!",
        });
        //刷新页面
        this.getChapterVideo();
      });
    },
    saveOrUpdateVideo() {
    
    
      // this.saveVideoBtnDisabled = true;

      if (!this.video.id) {
    
    
        this.addVideo();
      } else {
    
    
        this.updateDataVideo();
      }
    },

编辑课时按钮

<el-button style="" type="text" @click="openEditVideo(video.id)">编辑</el-button>

定义编辑方法

openEditVideo(videoId) {
    
    
      //弹框
      this.dialogVideoFormVisible = true;
      //调用接口
      video1.getVideoInfo(videoId).then((response) => {
    
    
        this.video = response.data.video;
      });
    },
    updateDataVideo() {
    
    
      video1.updateVideoInfo(this.video).then((response) => {
    
    
        //关闭弹框
        this.dialogChapterFormVisible = false;
        //提示
        this.$message({
    
    
          type: "success",
          message: "修改小节成功!",
        });
        //刷新页面
        this.getChapterVideo();
      });
    },

删除按钮

<el-button type="text" @click="removeChapter(chapter.id)">删除</el-button>

定义删除方法

//删除小节
    removeVideo(id) {
    
    
      this.$confirm("此操作将删除小节, 是否继续?", "提示", {
    
    
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
    
    
        //点击确定,执行then方法
        //调用删除的方法
        video1.deleteVideo(id).then((response) => {
    
    
          //删除成功
          //提示信息
          this.$message({
    
    
            type: "success",
            message: "删除小节成功!",
          });
          //刷新页面
          this.getChapterVideo();
        });
      }); //点击取消,执行catch方法
    },

注意

加了nginx后,put和delete请求都会出现跨域问题,需要修改nginx配置,由于我没有解决,所以我把put和delete改成了post请求,也有可能是我用的最新版的nginx的原因

课程最终发布

后端

定义vo

@ApiModel(value = "课程发布信息")
@Data
public class CoursePublishVo {
    
    

    private String id;
    private String title;
    private String cover;
    private Integer lessonNum;
    private String subjectLevelOne;
    private String subjectLevelTwo;
    private String teacherName;
    private String price;//只用于显示
}

CourseMapper.java

public interface EduCourseMapper extends BaseMapper<EduCourse> {
    
    


    public CoursePublishVo getPublishCourseInfo(String courseId);
}

CourseMapper.xml

 <select id="getPublishCourseInfo" resultType="com.atguigu.eduservice.entity.vo.CoursePublishVo">
        SELECT ec.id,ec.`title`,ec.`price`,ec.`lesson_num` AS lessonNum,ec.`cover`,
               et.`name` AS teacherName,
               es1.`title` AS subjectLevelOne,
               es2.title AS subjectLevelTwo

        FROM edu_course ec LEFT JOIN edu_course_description ecd ON ec.id=ecd.id
                           LEFT JOIN edu_teacher et ON ec.`teacher_id`=et.`id`
                           LEFT JOIN edu_subject es1 ON ec.`subject_parent_id`=es1.id
                           LEFT JOIN edu_subject es2 ON ec.subject_id=es2.`id`
        WHERE ec.`id`=#{courseId}
    </select>

CourseService.java

 @Override
    public CoursePublishVo publishCourseInfo(String id) {
    
    
        CoursePublishVo publishCourseInfo = baseMapper.getPublishCourseInfo(id);

        return publishCourseInfo;
    }

EduCourseController

 //根据课程id查询课程确认信息
    @GetMapping("/getPublishCourseInfo/{id}")
    public ResultVo getPublishCourseInfo(@PathVariable String id){
    
    
        CoursePublishVo coursePublishVo= courseService.publishCourseInfo(id);
        return ResultVo.ok().data("publishCourse",coursePublishVo);
    }

测试:报告异常

AbstractHandlerExceptionResolver.java:194 |org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver |Resolved exception caused by handler execution: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.guli.edu.mapper.CourseMapper.getCoursePublishVoById

问题分析:

dao层编译后只有class文件,没有mapper.xml,因为maven工程在默认情况下src/main/java目录下的所有资源文件是不发布到target目录下的,

在这里插入图片描述

解决方案

在service的pom.xml中

<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

重新打包项目会发现target目录下出现了xml文件夹
在这里插入图片描述
如果target目录里没有生成需要把target目录删除掉重新编译

在Spring Boot配置文件中添加配置

在eduservice的配置文件中添加

mybatis-plus:
  mapper-locations: classpath:com/atguigu/eduservice/mapper/xml/*.xml

修改课程状态

//课程最终发布
    //修改课程状态
    @PostMapping("publishCourse/{id}")
    public ResultVo publishCourse(@PathVariable String id){
    
    
        EduCourse eduCourse = new EduCourse();
        eduCourse.setId(id);
        eduCourse.setStatus("Normal");//设置课程发布状态
        boolean b = courseService.updateById(eduCourse);
        if (b){
    
    
            return ResultVo.ok();
        }
        else {
    
    
            return ResultVo.error();
        }



    }

前端

定义api

分析这个页面一共有两个远程方法:一个是根基课程id获取课程基本预览信息,第二个是发布课程

//课程确认信息显示
  getPublihCourseInfo(id) {
    
    
    return request({
    
    
      url: '/eduservice/course/getPublishCourseInfo/' + id,
      method: 'get'
    })
  },
  //课程最终发布
  publihCourse(id) {
    
    
    return request({
    
    
      url: '/eduservice/course/publishCourse/' + id,
      method: 'post'
    })
  },

定义数据模型

data() {
    
    
    return {
    
    
      saveBtnDisabled: false, // 保存按钮是否禁用
      courseId: "",
      coursePublish: {
    
    },
    };
  },

完善步骤导航

previous() {
    
    
      //console.log("previous");
      this.$router.push({
    
     path: "/course/chapter/" + this.courseId });
    },

发布函数

publish() {
    
    
      course.publihCourse(this.courseId).then((response) => {
    
    
        //提示
        this.$message({
    
    
          type: "success",
          message: "课程发布成功!",
        });
        //跳转课程列表页面
        this.$router.push({
    
     path: "/course/list" });
      });
    },

created

created() {
    
    
    //获取路由课程id值
    if (this.$route.params && this.$route.params.id) {
    
    
      this.courseId = this.$route.params.id;
      //调用接口方法根据课程id查询
      this.getCoursePublishId();
    }
  },

获取数据的方法

//根据课程id查询
    getCoursePublishId() {
    
    
      course.getPublihCourseInfo(this.courseId).then((response) => {
    
    
        this.coursePublish = response.data.publishCourse;
      });
    },

组件模板

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

    <div class="ccInfo">
      <img :src="coursePublish.cover" />
      <div class="main">
        <h2>{
   
   { coursePublish.title }}</h2>
        <p class="gray">
          <span>共{
   
   { coursePublish.lessonNum }}课时</span>
        </p>
        <p>
          <span
            >所属分类:{
   
   { coursePublish.subjectLevelOne }} —
            {
   
   { coursePublish.subjectLevelTwo }}</span
          >
        </p>
        <p>课程讲师:{
   
   { coursePublish.teacherName }}</p>
        <h3 class="red">¥{
   
   { coursePublish.price }}</h3>
      </div>
    </div>

    <div>
      <el-button @click="previous">返回修改</el-button>
      <el-button :disabled="saveBtnDisabled" type="primary" @click="publish"
        >发布课程</el-button
      >
    </div>
  </div>
</template>

css样式

<style scoped>
.ccInfo {
    
    
  background: #f5f5f5;
  padding: 20px;
  overflow: hidden;
  border: 1px dashed #ddd;
  margin-bottom: 40px;
  position: relative;
}
.ccInfo img {
    
    
  background: #d6d6d6;
  width: 500px;
  height: 278px;
  display: block;
  float: left;
  border: none;
}
.ccInfo .main {
    
    
  margin-left: 520px;
}

.ccInfo .main h2 {
    
    
  font-size: 28px;
  margin-bottom: 30px;
  line-height: 1;
  font-weight: normal;
}
.ccInfo .main p {
    
    
  margin-bottom: 10px;
  word-wrap: break-word;
  line-height: 24px;
  max-height: 48px;
  overflow: hidden;
}

.ccInfo .main p {
    
    
  margin-bottom: 10px;
  word-wrap: break-word;
  line-height: 24px;
  max-height: 48px;
  overflow: hidden;
}
.ccInfo .main h3 {
    
    
  left: 540px;
  bottom: 20px;
  line-height: 1;
  font-size: 28px;
  color: #d32f24;
  font-weight: normal;
  position: absolute;
}
</style>

课程列表显示

后端

定义搜索对象

CourseQuery

@ApiModel(value = "Course查询对象", description = "课程查询对象封装")
@Data
public class CourseQueryVo {
    
    
    @ApiModelProperty(value = "课程名称")
    private String title;

    @ApiModelProperty(value = "课程状态 Draft未发布  Normal已发布")
    private String status;

    @ApiModelProperty(value = "讲师id")
    private String teacherId;

    @ApiModelProperty(value = "一级类别id")
    private String subjectParentId;

    @ApiModelProperty(value = "二级类别id")
    private String subjectId;
}

定义service方法

void pageQuery(Page<EduCourse> eduCoursePage, CourseQueryVo courseQuery);
@Override
    public void pageQuery(Page<EduCourse> eduCoursePage, CourseQueryVo courseQuery) {
    
    
        QueryWrapper<EduCourse> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("gmt_create");
        if(courseQuery==null){
    
    
            this.page(eduCoursePage,queryWrapper);
            return;
        }
        String title = courseQuery.getTitle();
        String teacherId = courseQuery.getTeacherId();
        String subjectId = courseQuery.getSubjectId();
        String subjectParentId = courseQuery.getSubjectParentId();
        String status = courseQuery.getStatus();

        if(!StringUtils.isEmpty(title)){
    
    
            queryWrapper.like("title",title);
        }
        if(!StringUtils.isEmpty(teacherId)){
    
    
            queryWrapper.eq("teacher_id",teacherId);
        }
        if(!StringUtils.isEmpty(status)){
    
    
            queryWrapper.eq("status",status);
        }
        if(!StringUtils.isEmpty(subjectId)){
    
    
            queryWrapper.eq("subject_id", subjectId);
        }
        if(!StringUtils.isEmpty(subjectParentId)){
    
    
            queryWrapper.eq("subject_parent_id", subjectParentId);
        }
        this.page(eduCoursePage,queryWrapper);

    }

controller

public ResultVo getCourseList(@PathVariable long page,@PathVariable long limit,@RequestBody(required = false) CourseQueryVo courseQuery){
    
    
        Page<EduCourse> eduCoursePage = new Page<>(page, limit);
        courseService.pageQuery(eduCoursePage,courseQuery);
        List<EduCourse> list = eduCoursePage.getRecords();
        long total = eduCoursePage.getTotal();
        return ResultVo.ok().data("list",list).data("rows",total);
    }

前端

<template>
  <div class="app-container">
    课程列表

    <!--查询表单-->
    <el-form :inline="true" class="demo-form-inline">
      <el-form-item>
        <el-input v-model="courseQuery.title" placeholder="课程名称" />
      </el-form-item>

      <el-form-item>
        <el-select
          v-model="courseQuery.status"
          clearable
          placeholder="课程状态"
        >
          <el-option value="Normal" label="已发布" />
          <el-option value="Draft" label="未发布" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-input v-model="courseQuery.teacherId" placeholder="教师id" />
      </el-form-item>

      <el-button type="primary" icon="el-icon-search" @click="getList()"
        >查询</el-button
      >
      <el-button type="default" @click="resetData()">清空</el-button>
    </el-form>

    <!-- 表格 -->
    <el-table :data="list" border fit highlight-current-row>
      <el-table-column label="序号" width="70" align="center">
        <template slot-scope="scope">
          {
   
   { (page - 1) * limit + scope.$index + 1 }}
        </template>
      </el-table-column>

      <el-table-column prop="title" label="课程名称" align="center" />

      <el-table-column label="课程状态" width="80" align="center">
        <template slot-scope="scope">
          {
   
   { scope.row.status === "Normal" ? "已发布" : "未发布" }}
        </template>
      </el-table-column>

      <el-table-column
        prop="lessonNum"
        label="课时数"
        width="60"
        align="center"
      />

      <el-table-column prop="gmtCreate" label="添加时间" width="160" />

      <el-table-column
        prop="viewCount"
        label="浏览数量"
        width="60"
        align="center"
      />

      <el-table-column label="操作" width="150" align="center">
        <template slot-scope="scope">
          <router-link :to="'/course/info/' + scope.row.id">
            <el-button type="primary" size="mini" icon="el-icon-edit"
              >编辑课程基本信息</el-button
            >
          </router-link>
          <router-link :to="'/course/chapter/' + scope.row.id">
            <el-button type="primary" size="mini" icon="el-icon-edit"
              >编辑课程大纲息</el-button
            >
          </router-link>
          <el-button
            type="danger"
            size="mini"
            icon="el-icon-delete"
            @click="removeDataById(scope.row.id)"
            >删除课程信息</el-button
          >
        </template>
      </el-table-column>
    </el-table>

    <!-- 分页 -->
    <el-pagination
      :current-page="page"
      :page-size="limit"
      :total="total"
      style="padding: 30px 0; text-align: center"
      layout="total, prev, pager, next, jumper"
      @current-change="getList"
    />
  </div>
</template>
<script>
//引入调用teacher.js文件
import course from "@/api/edu/course";

export default {
      
      
  //写核心代码位置
  // data:{
      
      
  // },
  data() {
      
      
    //定义变量和初始值
    return {
      
      
      list: null, //查询之后接口返回集合
      page: 1, //当前页
      limit: 10, //每页记录数
      total: 0, //总记录数
      courseQuery: {
      
      }, //条件封装对象
    };
  },
  created() {
      
      
    //页面渲染之前执行,一般调用methods定义的方法
    //调用
    this.getList();
  },
  methods: {
      
      
    //创建具体的方法,调用teacher.js定义的方法
    //讲师列表的方法
    getList(page = 1) {
      
      
      this.page = page;
      course
        .getListCourse(this.page, this.limit, this.courseQuery)
        .then((response) => {
      
      
          //请求成功
          //response接口返回的数据
          this.list = response.data.list;
          this.total = response.data.rows;
        });
    },
    resetData() {
      
      
      //清空的方法
      //表单输入项数据清空
      this.courseQuery = {
      
      };
      //查询所有讲师数据
      this.getList();
    },
    removeDataById(id) {
      
      
      this.$confirm(
        "此操作将永久删除该课程,以及该课程下的章节和视频,是否继续?",
        "提示",
        {
      
      
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }
      ).then(() => {
      
      
        //点击确定,执行then方法
        //调用删除的方法
        course.deleteCourseById(id).then((response) => {
      
      
          //删除成功
          //提示信息
          this.$message({
      
      
            type: "success",
            message: "删除成功!",
          });
          //回到列表页面
          this.getList(this.page);
        });
      }); //点击取消,执行catch方法
    },
  },
};
</script>

删除课程

后端

controller

//删除课程
    @ApiOperation(value = "根据ID删除课程")
    @PostMapping("deleteCourse/{courseId}")
    public ResultVo deleteCourse(@PathVariable String courseId){
    
    
        boolean remove = courseService.removeCourse(courseId);
        if(remove){
    
    
            return ResultVo.ok();
        }
        else{
    
    
            return ResultVo.error();
        }
    }

service

如果用户确定删除,则首先删除video记录,然后删除chapter记录,最后删除Course记录

在VideoService中定义根据courseId删除video业务方法

接口

void removeVideoByCourseId(String courseId);

实现

@Override
    public void removeVideoByCourseId(String courseId) {
    
    
        QueryWrapper<EduVideo> QueryWrapper = new QueryWrapper<>();
        QueryWrapper.eq("course_id",courseId);
        baseMapper.delete(QueryWrapper);
    }
在ChapterService中定义根据courseId删除chapter业务方法

接口

void removeChapterByCourseId(String courseId);

实现

 @Override
    public void removeChapterByCourseId(String courseId) {
    
    
        QueryWrapper<EduChapter> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("course_id",courseId);
        baseMapper.delete(queryWrapper);
    }
删除当前course记录

注意这里传递的是课程id所有删除小节和章节时不能直接通过removeById的方式删除

@Override
    public boolean removeCourse(String courseId) {
    
    
        //根据课程id删除小节
        eduVideoService.removeVideoByCourseId(courseId);
        //根据课程id删除章节
        chapterService.removeChapterByCourseId(courseId);
        //根据课程id删除描述
        courseDescriptionService.removeById(courseId);
        //根据课程id删除课程本身
        int i = baseMapper.deleteById(courseId);
        if(i==0){
    
    
            throw new GuliException(20001,"删除失败");
        }
        return true;
    }

前端

定义api

course.js中添加删除方法

deleteCourseById(id) {
    
    
    return request({
    
    
      url: '/eduservice/course/deleteCourse/' + id,
      method: 'post'
    })
  }

修改删除按钮

<el-button type="text" size="mini" icon="el-icon-delete" @click="removeDataById(scope.row.id)">删除</el-button>

编写删除方法

removeDataById(id) {
    
    
      this.$confirm("此操作将永久删除该课程,以及该课程下的章节和视频,是否继续?", "提示", {
    
    
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
    
    
        //点击确定,执行then方法
        //调用删除的方法
        course.deleteCourseById(id).then((response) => {
    
    
          //删除成功
          //提示信息
          this.$message({
    
    
            type: "success",
            message: "删除成功!",
          });
          //回到列表页面
          this.getList(this.page);
        });
      }); //点击取消,执行catch方法
    },

猜你喜欢

转载自blog.csdn.net/qq_44866153/article/details/120606122