【项目】小帽学堂(七)

小帽学堂

2. 添加分类前端

【项目】小帽学堂(七)_第1张图片

  • 添加路由
// src\router\index.js
{
    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' }
      },
    ]
  },
  • 添加vue组件
// src\views\edu\subject\save.vue
<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/data.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">{{ fileUploadBtnText }}el-button>
        el-upload>
      el-form-item>
    el-form>
  div>
template>
<script>
export default {
    data() {
        return {
            BASE_API: process.env.BASE_API, // 接口API地址
            fileUploadBtnText: '上传到服务器', // 按钮文字
            importBtnDisabled: false, // 按钮是否禁用,
            loading: false
        }
    },
    created() {

    },
    methods: {
        // 点击按钮上传文件到接口里面
        submitUpload() {
            this.importBtnDisabled = true
            this.loading = true
            this.$refs.upload.submit()
        },
        // 上传成功
        fileUploadSuccess() {
            // 提示信息
            this.loading = false
            this.$message({
                type: 'success',
                message: '添加课程分类成功'
            })
        },
        // 上传失败
        fileUploadError() {
            // 提示信息
            this.loading = false
            this.$message({
                type: 'error',
                message: '添加课程分类失败'
            })
        }
    }
}
script>

3. 课程分类显示接口

【项目】小帽学堂(七)_第2张图片

  • 后端实现

    • 创建实体类
    // 一级分类
    @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
    @RestController
    @RequestMapping("/eduservice/subject")
    @CrossOrigin
    public class EduSubjectController {
        @Autowired
        private EduSubjectService subjectService;
    
        // 添加课程分类
        // 获取上传过来的文件,把文件内容读取出来
        @PostMapping("addSubject")
        public R addSubject(MultipartFile file) {
            // 上传过来excel文件
            subjectService.saveSubject(file, subjectService);
            return R.ok();
        }
    
        // 课程分类列表(树形)
        @GetMapping("getAllSubject")
        public R getAllSubject() {
            // list集合中的泛型是一级分类
            List<OneSubject> list = subjectService.getAllOneTwoSubject();
            return R.ok().data("list", list);
        }
    }
    
    • 创建service
    // 接口
    public interface EduSubjectService extends IService<EduSubject> {
    
        // 添加课程分类
        void saveSubject(MultipartFile file, EduSubjectService subjectService);
    
        // 课程分类列表
        List<OneSubject> getAllOneTwoSubject();
    }
    
    // 实现类
    @Service    // 把对象创建交给Spring管理
    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) {
                e.printStackTrace();
            }
        }
    
        @Override
        public List<OneSubject> getAllOneTwoSubject() {
            // 1. 查询所有一级分类 parentid = 0
            QueryWrapper<EduSubject> wrapperOne = new QueryWrapper<>();
            wrapperOne.eq("parent_id", "0");
            List<EduSubject> oneSubjectList = baseMapper.selectList(wrapperOne);
            // this.list(wrapperOne);
    
            // 2. 查询所有二级分类 parentid != 0
            QueryWrapper<EduSubject> wrapperTwo = new QueryWrapper<>();
            wrapperTwo.ne("parent_id", "0");
            List<EduSubject> twoSubjectList = baseMapper.selectList(wrapperTwo);
    
            // 创建List集合,用于存储最终封装数据
            List<OneSubject> finalSubjectList = new ArrayList<>();
    
            // 3. 封装一级分类
            // 查询出来所有的一级分类List集合遍历,得到每个一级分类对象,获取每个一级分类对象值
            // 封装到要求的list集合里面 List finalSubjectList
            for(int i = 0; i < oneSubjectList.size(); i++) {
                // 得到oneSubjectList 每个 eduSubject 对象
                EduSubject eduSubject = oneSubjectList.get(i);
    
                // 把eduSubject里面值获取出来,放到OneSubject对象里面
                OneSubject oneSubject = new OneSubject();
    //            oneSubject.setId(eduSubject.getId());
    //            oneSubject.setTitle(eduSubject.getTitle());
                // eduSubject的值复制到对应oneSubject对象里面
                BeanUtils.copyProperties(eduSubject, oneSubject);
                // 多个OneSubject放到finalSubjectList里面
                finalSubjectList.add(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())) {
                        // 把tSubject值复制到TwoSubject里面,放到twoFinalSubjectList里面
                        TwoSubject twoSubject = new TwoSubject();
                        BeanUtils.copyProperties(tSubject, twoSubject);
                        twoFinalSubjectList.add(twoSubject);
                    }
                }
    
                // 把一级下面所有二级分类放到一级分类里面
                oneSubject.setChildren(twoFinalSubjectList);
            }
            // 4. 封装二级分类
            return finalSubjectList;
        }
    }
    

4. 课程分类显示前端

  • 前端实现
    • 参考 views/tree/index.vue
    • 创建api
    // src\api\edu\subject.js
    import request from '@/utils/request'
    
    export default {
        // 1. 课程分类列表(条件查询分页)
        getSubjectList() {
          return request({
            url: '/eduservice/subject/getAllSubject',
            method: 'get'
          })
        }
    }
    
    <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>
    

十五、课程管理

1. 课程发布流程说明

【项目】小帽学堂(七)_第3张图片

2. 课程相关表关系

【项目】小帽学堂(七)_第4张图片

3. 添加课程分析

【项目】小帽学堂(七)_第5张图片

4. 添加课程信息接口

  • 后台api

    • 定义form表单对象
    @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则可免费观看")
        private BigDecimal price;
        @ApiModelProperty(value = "总课时")
        private Integer lessonNum;
        @ApiModelProperty(value = "课程封面图片路径")
        private String cover;
        @ApiModelProperty(value = "课程简介")
        private String description;
    }
    
    • 修改CourseDescription主键生成策略
    @ApiModelProperty(value = "课程ID")
    @TableId(value = "id", type = IdType.INPUT)
    private String id;
    
    • 定义控制层接口
    @RestController
    @RequestMapping("/eduservice/course")
    @CrossOrigin
    public class EduCourseController {
    
        @Autowired
        private EduCourseService courseService;
    
        // 添加课程基本信息的方法
        @PostMapping("addCourseInfo")
        public R addCourseInfo(@RequestBody CourseInfoVo courseInfoVo) {
            courseService.saveCourseInfo(courseInfoVo);
            return R.ok();
        }
    }
    
    • 定义业务层方法
    // 接口
    public interface EduCourseService extends IService<EduCourse> {
        void saveCourseInfo(CourseInfoVo courseInfoVo);
    }
    
    @Service
    public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {
    
        @Autowired
        private EduCourseDescriptionService courseDescriptionService;
    
        // 添加课程基本信息的方法
        @Override
        public void saveCourseInfo(CourseInfoVo courseInfoVo) {
            // 1.向课程表添加课程基本信息
            // courseInfoVo 对象转换为 eduCourse 对象
            EduCourse eduCourse = new EduCourse();
            BeanUtils.copyProperties(courseInfoVo, eduCourse);
            int insert = baseMapper.insert(eduCourse);
            if(insert == 0) {
                // 添加失败
                throw new LemonException(20001,"添加课程信息失败");
            }
    
            // 获取添加之后课程id
            String cid = eduCourse.getId();
            // 2.向课程简介表添加课程简介
            EduCourseDescription courseDescription = new EduCourseDescription();
            courseDescription.setDescription(courseInfoVo.getDescription());
            // 设置描述id就是课程id
            courseDescription.setId(cid);
            courseDescriptionService.save(courseDescription);
        }
    }
    
    • Swagger测试

5. 添加课程信息前端

【项目】小帽学堂(七)_第6张图片

  • 配置路由
    • 添加路由
    {
        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
          }
        ]
    },
    
    • 添加vue组件
  • 整合步骤条组件
    • 课程信息页面
// 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/1' })
    }
  }
}
script>
  • 课程大纲页面
<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('info created')
  },
  methods: {
    previous(){
        this.$router.push({ path: '/course/info/1' })
    },
    next() {
        this.$router.push({ path: '/course/publish/1' })
    }
  }
}
script>
  • 课程发布页面
<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: '/course/chapter/1' })
    },

    publish() {
      console.log('publish')
      this.$router.push({ path: '/course/list' })
    }
  }
}
script>
  • 编辑课程基本信息

    • 定义api
    // src\api\edu\course.js
    import request from '@/utils/request'
    
    export default {
        // 1. 添加课程信息
        addCourseInfo(courseInfo) {
          return request({
            url: '/eduservice/course/addCourseInfo',
            method: 'post',
            data: courseInfo
          })
        }
    }
    
    • 组件模板
    <el-form label-width="120px">
      <el-form-item label="课程标题">
        <el-input v-model="courseInfo.title" placeholder=" 示例:机器学习项目课:从基础到搭建项目视频课程。专业名称注意大小写"/>
      el-form-item>
      
      
      <el-form-item label="总课时">
        <el-input-number :min="0" v-model="courseInfo.lessonNum" controls-position="right" placeholder="请填写课程的总课时数"/>
      el-form-item>
      
      <el-form-item label="课程简介">
        <el-input v-model="courseInfo.description"  placeholder=""/>
      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>
    
    • 组件js
    <script>
    import course from '@/api/edu/course'
    export default {
      data() {
        return {
          saveBtnDisabled: false, // 保存按钮是否禁用
          courseInfo: {
            title: '',
            subjectId: '',
            teacherId: '',
            lessonNum: 0,
            description: '',
            cover: '',
            price: 0
          }
        }
      },
      created() {
        console.log('info created')
      },
      methods: {
        saveOrUpdate() {
          course.addCourseInfo(this.courseInfo)
          .then(response => {
            // 提示
            this.$message({
              type: 'success',
              message: '添加课程信息成功!'
            });
            // 跳转到第二步
            this.$router.push({ path: '/course/chapter/' + response.data.courseId })
          })
        }
      }
    }
    </script>
    
    • 后台接口
    @RestController
    @RequestMapping("/eduservice/course")
    @CrossOrigin
    public class EduCourseController {
    
        @Autowired
        private EduCourseService courseService;
    
        // 添加课程基本信息的方法
        @PostMapping("addCourseInfo")
        public R addCourseInfo(@RequestBody CourseInfoVo courseInfoVo) {
            // 返回添加之后课程id,为了后面添加大纲使用
            String id = courseService.saveCourseInfo(courseInfoVo);
            return R.ok().data("courseId", id);
        }
    }
    
    public interface EduCourseService extends IService<EduCourse> {
        String saveCourseInfo(CourseInfoVo courseInfoVo);
    }
    
    @Service
    public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {
    
        @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 LemonException(20001,"添加课程信息失败");
            }
    
            // 获取添加之后课程id
            String cid = eduCourse.getId();
            // 2.向课程简介表添加课程简介
            EduCourseDescription courseDescription = new EduCourseDescription();
            courseDescription.setDescription(courseInfoVo.getDescription());
            // 设置描述id就是课程id
            courseDescription.setId(cid);
            courseDescriptionService.save(courseDescription);
            return cid;
        }
    }
    

6. 添加课程信息前端完善(显示讲师)

【项目】小帽学堂(七)_第7张图片

// src\api\edu\course.js
export default {
    // 2. 查询所有讲师
    getListTeacher() {
      return request({
        url: '/eduservice/teacher/findAll',
        method: 'get'
      })
    }
}
// src\views\edu\course\info.vue
<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>

// 定义data
teacherList: [] // 封装所有的讲师

created() {
    // 初始化所有讲师
    this.getListTeacher()
},
methods: {
  // 查询所有的讲师
  getListTeacher() {
    course.getListTeacher()
      .then(response => {
        this.teacherList = response.data.items
      })
  },
}

7. 添加课程信息前端完善(显示分类)

【项目】小帽学堂(七)_第8张图片

  • 获取一、二级分类
    • 组件数据定义
      • 定义在data中
subjectOneList: [], // 一级分类
subjectTwoList: []  // 二级分类
  • 组件模板
<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>

<script>
import subject from '@/api/edu/subject'
methods: {
    // 点击某个一级分类,触发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
        })
    },
}
script>

8. 添加课程信息前端完善(封面上传)


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

<script>
import course from '@/api/edu/course'
import subject from '@/api/edu/subject'
export default {
  data() {
    return {
      BASE_API: process.env.BASE_API, // 接口API地址
    }
  },
  methods: {
    // 上传封面成功调用的方法
    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
    }
  }
}
script>

你可能感兴趣的:(项目,vue.js,javascript)