谷粒学院第七天

谷粒学院第七天

在这里插入图片描述


一、添加课程分类前端实现

1、配置路由

(1)src/router/index.js

//课程分类
{
    path: 'subject',
    component: Layout,
    redirect: '/edu/subject/list',
    name: 'Subject',
    meta: { title: '课程分类管理', icon: 'nested' },
    children: [
      {
        path: '/list',
        name: '课程分类列表',
        component: () => import('@/views/edu/subject/list.vue'),
        meta: { title: '课程分类列表', icon: 'table' }
      },
      {
        path: 'import',
        name: '课程分类导入',
        component: () => import('@/views/edu/subject/import.vue'),
        meta: { title: '导入课程分类', icon: 'nested' }
      }
    ]
}

(2)添加组件

在这里插入图片描述


2、编写 import.vue

(1)js 定义数据

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

(2)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/1.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/edu-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>

(3)上传方法

  methods: {
    //点击按钮上传文件到接口
    submitUpload() {
      this.importBtnDisabled = true;
      this.loading = true;
      this.$refs.upload.submit();
    },
    //上传成功后
    fileUploadSuccess(resp) {
      if (resp.success === true) {
        this.loading = false;
        this.$message({
          type: "success",
          message: resp.message,
        });
      }
    },
    //上传失败后
    fileUploadError(resp) {
      this.loading = false;
      this.$message({
        type: "error",
        message: "导入失败",
      });
    },
  }



二、课程分类列表显示(树形)

1、后端实现(重要)

在这里插入图片描述


(1)编写实体类

/**
 * 一级分类
 * @ClassName OneSubject
 * @Author YH
 * @Date 2021/8/26
 * @Version 1.0
 */
@Data
public class OneSubject {
    
    private String id;

    private String title;

    /**
     * 一个一级分类中可能有多个二级分类
     */
    private List<TwoSubject> children = new ArrayList<>();
}
/**
 * 二级分类
 * @ClassName TwoSubject
 * @Author YH
 * @Date 2021/8/26
 * @Version 1.0
 */
@Data
public class TwoSubject {

    private String id;

    private String title;
}

(2)编写 Controller 方法

/**
 * 课程分类列表显示(树形)
 */
@PostMapping("/getAllSubject")
public Result getAllSubject() {
    /**
     * 因为一级分类中包含二级分类
     */
    List<OneSubject> subjectList = eduSubjectService.getAllOneTwoSubject();
    Map<String, Object> map = new HashMap<>();
    map.put("subjectList", subjectList);

    return Result.ok().data(map);
}

(3)编写 Servie 接口方法

/**
 * 课程分类列表显示(树形)
 * @return
 */
List<OneSubject> getAllOneTwoSubject();

(4)编写 ServiceImpl

@Autowired
private EduSubjectMapper eduSubjectMapper;

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

    // 查询所有二级分类
    QueryWrapper<EduSubject> twoWrapper = new QueryWrapper<>();
    twoWrapper.ne("parent_id", "0");
    List<EduSubject> twoSubjectList = eduSubjectMapper.selectList(twoWrapper);

    // 存储最终结果
    List<OneSubject> result = new ArrayList<>();

    // 封装一级分类,利用工具类
    for (EduSubject eduSubject : oneSubjectList) {
        OneSubject oneSubject = new OneSubject();
        BeanUtils.copyProperties(eduSubject, oneSubject);
        result.add(oneSubject);

        // 封装二级分类
        List<TwoSubject> children = new ArrayList<>();
        for (EduSubject twoSubject : twoSubjectList) {
            // 判断其父级 id 是否等于
            if (twoSubject.getParentId().equals(eduSubject.getId()) == true) {
                TwoSubject tempTwoSubject = new TwoSubject();
                BeanUtils.copyProperties(twoSubject, tempTwoSubject);
                children.add(tempTwoSubject);
            }
        }

        oneSubject.setChildren(children);
    }

    return result;
}

(4)测试结果

在这里插入图片描述


2、前端实现

(1)创建 api

api/teacher/subject.js

import request from '@/utils/request' //引入已经封装好的axios 和 拦截器

export default{
    //课程分类
    getSubjectList(){
        return request({
            url:"/eduservice/edu-subject/getAllSubject",
            method: 'get'
        })
    }
    
}

(2)list.vue

<template>
  <div class="app-container">
    <el-input
      v-model="filterText"
      placeholder="Filter keyword"
      style="margin-bottom: 30px"
    />

    <el-tree
      ref="tree2"
      :data="data"
      :props="defaultProps"
      :filter-node-method="filterNode"
      class="filter-tree"
      default-expand-all
    />
  </div>
</template>

<script>
import subject from '@/api/teacher/subject.js'
export default {
  data() {
    return {
      filterText: "",
      data: [], //返回所有分类的数据
      defaultProps: {
        children: "children",
        label: "title",
      },
    };
  },
  watch: {
    filterText(val) {
      this.$refs.tree2.filter(val);
    },
  },

  methods: {
    getAllSubjectList(){
      subject.getSubjectList()
        .then(resp=>{
          this.data = resp.data.list
        })
    },
    // 优化前端过滤功能
    filterNode(value, data) {
      if (!value) return true;
      return data.title.indexOf(value) !== -1;
    },
  },
  created() {
    this.getAllSubjectList()
  },
};
</script>

(3)优化前端过滤功能

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

(4)给之前的添加课程分类添加路由跳转
在这里插入图片描述




三、课程发布需求分析

1、课程发布流程说明

第一步

在这里插入图片描述


第二步

在这里插入图片描述


第三步

在这里插入图片描述



2、课程相关表关系(重要)

在这里插入图片描述


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NqAg73MO-1629975089935)(C:\Users\YH\AppData\Roaming\Typora\typora-user-images\image-20210826114630093.png)]



3、添加课程分析

(1)使用代码生成器生成相关代码

strategy.setInclude("edu_course", "edu_course_description", "edu_video", "edu_chapter");//根据数据库哪张表生成,有多张表就加逗号继续填写

(2)细节问题

在这里插入图片描述


(3)编写 vo 实体类

@ApiModel(value = "课程基本信息", description = "编辑课程基本信息的表单对象")
@Data
public class CourseInfoVo implements Serializable {

    private static final long serialVersionUID = 1L;

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

}

(4)编写 Controller

@CrossOrigin
@RestController
@RequestMapping("/eduservice/edu-course")
public class EduCourseController {

    @Autowired
    private EduCourseService eduCourseService;


    /**
     * 添加课程
     * @param courseInfoVo
     * @return
     */
    @PostMapping("/addCourseInfo")
    public Result addCourseInfo(@RequestBody CourseInfoVo courseInfoVo) {

        eduCourseService.addCourseInfo(courseInfoVo);
        return Result.ok();
    }

}

(5)编写 Service、ServiceImpl

public interface EduCourseService extends IService<EduCourse> {

    void addCourseInfo(CourseInfoVo courseInfoVo);
}
@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {

    @Autowired
    private EduCourseMapper eduCourseMapper;

    @Autowired
    private EduCourseDescriptionMapper eduCourseDescriptionMapper;

    @Override
    public void addCourseInfo(CourseInfoVo courseInfoVo) {
        // 向课程表添加信息
        EduCourse eduCourse = new EduCourse();
        BeanUtils.copyProperties(courseInfoVo, eduCourse);
        int insert = eduCourseMapper.insert(eduCourse);

        String courseId = eduCourse.getId();
        if (insert != 1) {
            throw new GuliException(200, "添加课程失败");
        }

        // 向课程简介表中添加信息
        EduCourseDescription eduCourseDescription = new EduCourseDescription();
        eduCourseDescription.setDescription(courseInfoVo.getDescription());
        eduCourseDescription.setId(courseId);
        eduCourseDescriptionMapper.insert(eduCourseDescription);
    }
}

(6)添加 MP 自动生成时间和主键生成策略

在这里插入图片描述


在这里插入图片描述



4、添加课程信息前端

(1)配置路由

//课程信息管理
  {
    path: '/course',
    component: Layout,
    redirect: '/course/list',
    name: '课程管理',
    meta: { title: '课程管理', icon: 'nested' },
    children: [
      {
        path: 'list',
        name: '课程列表',
        component: () => import('@/views/edu/course/list.vue'),
        meta: { title: '课程列表', icon: 'table' }
      },
      {
        path: 'info',
        name: '添加课程',
        component: () => import('@/views/edu/course/info.vue'),
        meta: { title: '添加课程', icon: 'nested' }
      },
      {
        path: 'info/:id',
        name: 'EduCourseInfoEdit',
        component: () => import('@/views/edu/course/info.vue'),
        meta: { title: '编辑课程基本信息', noCache: true },
        hidden: true
        },
        {
        path: 'chapter/:id',
        name: 'EduCourseChapterEdit',
        component: () => import('@/views/edu/course/chapter.vue'),
        meta: { title: '编辑课程大纲', noCache: true },
        hidden: true
        },
        {
        path: 'publish/:id',
        name: 'EduCoursePublishEdit',
        component: () => import('@/views/edu/course/publish.vue'),
        meta: { title: '发布课程', noCache: true },
        hidden: true
        }
    ]
  },

(2)添加组件
在这里插入图片描述


(3)课程信息页面

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,
    };
  },
  methods: {
    next() {
        //跳转到第二步
      this.$router.push({path:'/course/chapter/1'})
    },
  },
  created(){

  }
};
</script>

<style></style>

(4)课程大纲页面

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,
    };
  },
  methods: {
    //跳转到上一步
    previous() {
      this.$router.push({ path: "/course/info/1" });
    },
    next() {
      //跳转到第三步
      this.$router.push({ path: "/course/publish/1" });
    },
  },
  created() {},
};
</script>

<style>
</style>

(5)课程大纲页面

.<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,
    };
  },
  methods: {
    //跳转到上一步
    previous() {    
      this.$router.push({ path: "/course/chapter/1" });
    },
    publish(){
      this.$router.push({ path: "/course/list" });
    }

  },
};
</script>

<style>
</style>

(6)定义 API

import request from '@/utils/request' //引入已经封装好的axios 和 拦截器

export default{
    //添加课程信息功能
    addCourseInfo(courseInfo){
        return request({
            url:"/eduservice/edu-course/addCourseInfo",
            method: 'post',
            data: courseInfo,
        })
    }
    
}

(7)组件模板

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

      <!-- 课程讲师 TODO -->

      <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="课程简介">
        <el-input v-model="courseInfo.description" placeholder="" />
      </el-form-item>

      <!-- 课程封面 TODO -->

      <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/teacher/course.js";

export default {
  data() {
    return {
      saveBtnDisabled: false,
      courseInfo: 
        {
          title: "",
          subjectId: "",
          teacherId: "",
          lessonNum: 0,
          description: "",
          cover: "",
          price: 0,
        },
    };
  },
  methods: {
    saveOrUpdate() {
      course.addCourseInfo(this.courseInfo).then(resp => {
        this.$message({
          message: "添加课程信息成功",
          type: "success",
        })
        //跳转到第二步,并带着这个课程生成的id
        this.$router.push({ path: "/course/chapter/"+resp.data.courseId });
      });
    },
  },
  created() {},
};
</script>

<style>
</style>



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

(1)编写组件

<!--课程讲师-->
<el-form-item label="课程讲师">
    <el-select v-model="courseInfo.teacherId" placeholder="请选择">
        <el-option
                   v-for="teacher in teacherLists"
                   :key="teacher.id"
                   :label="teacher.name"
                   :value="teacher.id"
                   ></el-option>
    </el-select>
</el-form-item>

(2)定义 API

//查询所有讲师
getAllTeacher(){
    return request({
        url:"/eduservice/edu-teacher/findAll",
        method: 'get',
    })
}

(3)引入 api

import course from "@/api/teacher/course.js";

(4)组件 js

export default {
  data() {
    return {
	  ...
      teacherLists: [], //封装所有讲师数据
    };
  },
  methods: {
    //查询所有讲师
    getListTeacher() {
      course.getAllTeacher().then((resp) => {
        this.teacherLists = resp.data.items;
      });
    },
    ....
  created() {
    //初始化所有讲师
    this.getListTeacher();
  },
};
</script>


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

(1)组件数据定义

subjectOneLists: [], //封装所以一级分类数据
subjectTwoLists: [], //封装二级分类数据	

(2)组件内容

<el-select
           v-model="courseInfo.subjectParentId"
           placeholder="一级分类"
           @change="subjectLevelOneChanged"
           >
    <el-option
               v-for="subject in subjectOneLists"
               :key="subject.id"
               :label="subject.title"
               :value="subject.id"
               ></el-option>
</el-select>

(3)引入组件

import subject from "@/api/teacher/subject.js";

(4)定义方法

//课程分类
getSubjectList(){
    return request({
        url:"/eduservice/edu-subject/getAllSubject",
        method: 'get'
    })
}

(5)组件模板

<el-select v-model="courseInfo.subjectId" placeholder="二级分类">
    <el-option
               v-for="subject in subjectTwoLists"
               :key="subject.id"
               :label="subject.title"
               :value="subject.id"
               ></el-option>
</el-select>

(6)注册 change 事件

(7)定义 Change 方法

//点击某个一级分类,触发change事件,显示对应的二级分类
subjectLevelOneChanged(value) {
    //value就是一级分类的id值
    for (let i = 0; i < this.subjectOneLists.length; i++) {
        if (this.subjectOneLists[i].id === value) {
            this.subjectTwoLists = this.subjectOneLists[i].children;
            this.courseInfo.subjectId = "";
        }
    }
}

(8)在 Created 中调用

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


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

(1)定义 data 数据

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

(2)编写组件

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

(3)结果回调

//上传封面成功调用的方法
handleAvatarSuccess(resp,file) {
    this.courseInfo.cover = resp.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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值