一、后端实现

1、定义vo

ChapterVo

  1. package com.guli.edu.vo;
  2. @ApiModel(value = "章节信息")
  3. @Data
  4. public class ChapterVo implements Serializable {
  5. private static final long serialVersionUID = 1L;
  6. private String id;
  7. private String title;
  8. private List<VideoVo> children = new ArrayList<>();
  9. }

VideoVo

  1. package com.guli.edu.vo;
  2. @ApiModel(value = "课时信息")
  3. @Data
  4. public class VideoVo implements Serializable {
  5. private static final long serialVersionUID = 1L;
  6. private String id;
  7. private String title;
  8. private Boolean free;
  9. }

2、服务层

接口

  1. package com.guli.edu.service;
  2. public interface ChapterService extends IService<Chapter> {
  3. List<ChapterVo> nestedList(String courseId);
  4. }

实现

  1. package com.guli.edu.service.impl;
  2. @Service
  3. public class ChapterServiceImpl extends ServiceImpl<ChapterMapper, Chapter> implements ChapterService {
  4. @Autowired
  5. private VideoService videoService;
  6. @Override
  7. public List<ChapterVo> nestedList(String courseId) {
  8. //最终要的到的数据列表
  9. ArrayList<ChapterVo> chapterVoArrayList = new ArrayList<>();
  10. //获取章节信息
  11. QueryWrapper<Chapter> queryWrapper1 = new QueryWrapper<>();
  12. queryWrapper1.eq("course_id", courseId);
  13. queryWrapper1.orderByAsc("sort", "id");
  14. List<Chapter> chapters = baseMapper.selectList(queryWrapper1);
  15. //获取课时信息
  16. QueryWrapper<Video> queryWrapper2 = new QueryWrapper<>();
  17. queryWrapper2.eq("course_id", courseId);
  18. queryWrapper2.orderByAsc("sort", "id");
  19. List<Video> videos = videoService.list(queryWrapper2);
  20. //填充章节vo数据
  21. int count1 = chapters.size();
  22. for (int i = 0; i < count1; i++) {
  23. Chapter chapter = chapters.get(i);
  24. //创建章节vo对象
  25. ChapterVo chapterVo = new ChapterVo();
  26. BeanUtils.copyProperties(chapter, chapterVo);
  27. chapterVoArrayList.add(chapterVo);
  28. //填充课时vo数据
  29. ArrayList<VideoVo> videoVoArrayList = new ArrayList<>();
  30. int count2 = videos.size();
  31. for (int j = 0; j < count2; j++) {
  32. Video video = videos.get(j);
  33. if(chapter.getId().equals(video.getChapterId())){
  34. //创建课时vo对象
  35. VideoVo videoVo = new VideoVo();
  36. BeanUtils.copyProperties(video, videoVo);
  37. videoVoArrayList.add(videoVo);
  38. }
  39. }
  40. chapterVo.setChildren(videoVoArrayList);
  41. }
  42. return chapterVoArrayList;
  43. }
  44. }

3、web层

package com.guli.edu.controller.admin;

@Api(description="课程章节管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/admin/edu/chapter")
public class ChapterAdminController {


  @Autowired
  private ChapterService chapterService;

  @ApiOperation(value = "嵌套章节数据列表")
  @GetMapping("nested-list/{courseId}")
  public R nestedListByCourseId(
          @ApiParam(name = "courseId", value = "课程ID", required = true)
          @PathVariable String courseId){


      List<ChapterVo> chapterVoList = chapterService.nestedList(courseId);
      return R.ok().data("items", chapterVoList);
  }

}

4、Swagger测试

二、前端实现

1、定义api

chapter.js


import request from '@/utils/request'

const api_name = '/admin/edu/chapter'

export default {


  getNestedTreeList(courseId) {
    return request({
      url: `${api_name}/nested-list/${courseId}`,
      method: 'get'
    })
  }

}

2、定义组件脚本

定义data


courseId: '', // 所属课程

chapterNestedList: [] // 章节嵌套课时列表

created中调用init方法

created() {
  console.log('chapter created')
  this.init()
},

定义相关methods获取章节和课时列表

init() {
 if (this.$route.params && this.$route.params.id) {
   this.courseId = this.$route.params.id
   // 根据id获取课程基本信息
   this.fetchChapterNestedListByCourseId()
 }

},

fetchChapterNestedListByCourseId() {
  chapter.getNestedTreeList(this.courseId).then(response => {
    this.chapterNestedList = response.data.items
  })

},

3、定义组件模板

<el-button type="text">添加章节</el-button>
<!-- 章节 -->
<ul class="chanpterList">
  <li
      v-for="chapter in chapterNestedList"
      :key="chapter.id">
      <p>
          {{ chapter.title }}


          <span class="acts">
              <el-button type="text">添加课时</el-button>
              <el-button style="" type="text">编辑</el-button>
              <el-button type="text">删除</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 type="text">编辑</el-button>
                      <el-button type="text">删除</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>

4、定义样式

将样式的定义放在页面的最后
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>