优学在线教育之课程分类功能模块

举报
tea_year 发表于 2025/06/30 21:23:07 2025/06/30
【摘要】 课程目标1)课程分类Excel导入实现2)课程分类列表展示1、 分类上传前端实现1. Excel模板1.1. 编辑Excel模板1.2. 将文件上传至阿里云OSS2. 配置路由2.1. 添加路由位置:src/router/index.js// 课程分类管理{path: '/ebs/subject',component: Layout,redirect: '/ebs/subject/list'...

课程目标

1)课程分类Excel导入实现

2)课程分类列表展示

1、 分类上传前端实现

1. Excel模板

1.1. 编辑Excel模板

1.2. 将文件上传至阿里云OSS


2. 配置路由

2.1. 添加路由

位置:src/router/index.js

// 课程分类管理

{

path: '/ebs/subject',

component: Layout,

redirect: '/ebs/subject/list',

name: 'Subject',

meta: { title: '课程分类管理', icon: 'nested' },

children: [

{

path: 'list',

name: 'EbsSubjectList',

component: () => import('@/views/ebs/subject/list'),

meta: { title: '课程分类列表' }

},

{

path: 'import',

name: 'EbsSubjectImport',

component: () => import('@/views/ebs/subject/import'),

meta: { title: '导入课程分类' }

}

]

},


2.2. 添加vue组件

3. 表单组件import.vue

3.1. js定义数据 

<script>

export default {

data() {

return {

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

OSS_PATH: process.env.OSS_PATH, // 阿里云OSS地址

fileUploadBtnText: '上传到服务器', // 按钮文字

importBtnDisabled: false, // 按钮是否禁用,

loading: false

}

}

}

</script>


3.2. 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="OSS_PATH + '/excel/%E8%AF%BE%E7%A8%8B%E5%88%86%E7%B1%BB%E5%88%97%E8%A1%A8%E6%A8%A1%E6%9D%BF.xls'">点击下载模版</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+'/admin/edu/subject/import'"

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>

3.3. js上传方法

 

methods: {

submitUpload() {

this.fileUploadBtnText = '正在上传'

this.importBtnDisabled = true

this.loading = true

//图片开始上传

this.$refs.upload.submit()

},

fileUploadSuccess(response) {

},

fileUploadError(response) {

}

}

3.4. 回调函数 

fileUploadSuccess(response) {

if (response.success === true) {

this.fileUploadBtnText = '导入成功'

this.loading = false

this.$message({

type: 'success',

message: response.message

})

} else {

this.fileUploadBtnText = '导入失败'

this.loading = false

const messages = response.data.messageList

let msgString = '<ul>'

messages.forEach(msg => {

msgString += `<li>${msg}</li>`

})

msgString += '</ul>'

this.$alert(msgString, response.message, {

dangerouslyUseHTMLString: true

})

}

},

fileUploadError(response) {

this.fileUploadBtnText = '导入失败'

this.loading = false

this.$message({

type: 'error',

message: '导入失败'

})

}



2、 分类管理接口

1. 依赖

yxzx-ebs中添加依赖

1.1. parent锁定依赖版本 

<poi.version>3.9</poi.version>

<commons-fileupload.version>1.3.1</commons-fileupload.version>

 <!--xls-->

<dependency>

<groupId>org.apache.poi</groupId>

<artifactId>poi</artifactId>

<version>${poi.version}</version>

</dependency>

<!--xlsx-->

<dependency>

<groupId>org.apache.poi</groupId>

<artifactId>poi-ooxml</artifactId>

<version>${poi.version}</version>

</dependency>

<!--文件上传-->

<dependency>

<groupId>commons-fileupload</groupId>

<artifactId>commons-fileupload</artifactId>

<version>${commons-fileupload.version}</version>

</dependency>


1.2. yxzx-common配置依赖

子项目可选<optional>true</optional>

<!--xls-->

<dependency>

<groupId>org.apache.poi</groupId>

<artifactId>poi</artifactId>

<optional>true</optional>

</dependency>

<!--xlsx-->

<dependency>

<groupId>org.apache.poi</groupId>

<artifactId>poi-ooxml</artifactId>

<optional>true</optional>

</dependency>

 

1.3. yxzx-ebs配置依赖 

<!--xls-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>

<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>


2. 业务处理

成功导入:

问题导入:


2.1. EbsSubjectController 

package com.yxzx.ebs.controller;

import com.baomidou.mybatisplus.extension.api.R;
import com.yxzx.ebs.common.Result;
import com.yxzx.ebs.teacher.service.EbsSubjectService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

/**
* <p>
* 课程分类
* </p>
*
*/
@Api(description="课程分类管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/subject")
public class EbsSubjectController {
@Autowired
private EbsSubjectService subjectService;

@ApiOperation(value = "Excel批量导入")
@PostMapping("import")
public Result addUser(
@ApiParam(name = "file", value = "Excel文件", required = true)
@RequestParam("file") MultipartFile file) throws Exception {

List<String> msg = subjectService.batchImport(file);
if(msg.size() == 0){
return Result.ok().message("批量导入成功");
}else{
return Result.error().message("部分数据导入失败").data("messageList", msg);
}
}
}

2.2. EbsSubjectService

接口

List<String> batchImport(MultipartFile file);

 实现V1:获取Excel记录并逐条导入 

/**
* 获取Excel记录并逐条导入
*
* @param file
* @return
*/
@Override
public List<String> batchImport(MultipartFile file) {

try {
//1 获取文件输入流
InputStream in = file.getInputStream();
//2 创建workbook
Workbook workbook = new HSSFWorkbook(in);
//3 workbook获取sheet
Sheet sheet = workbook.getSheetAt(0);

//为了存储错误信息
List<String> msg = new ArrayList<>();
//4 sheet获取row
//循环遍历获取行,从第二行开始获取数据
int lastRowNum = sheet.getLastRowNum();
// sheet.getRow(0)
for (int i = 1; i <= lastRowNum; i++) {
//得到excel每一行
Row row = sheet.getRow(i);
//如果行为空,提示错误信息
if(row == null) {
String str = "表格数据为空,请输入数据";
msg.add(str);
continue;
}
//行数据不为空
//5 row获取第一列
Cell cellOne = row.getCell(0);
//判断列是否为空
if(cellOne == null) {
String str = "第"+i+"行数据为空";
// 跳出这一行,往下继续执行
msg.add(str);
continue;
}
//列不为空获取数据,第一列值
//一级分类值
String cellOneValue = cellOne.getStringCellValue();
//添加一级分类
//因为excel里面有很多重复的一级分类
//在添加一级分类之前判断:判断要添加的一级分类在数据库表是否存在,如果不存在添加。
EbsSubject eduSubjectExist = this.existOneSubject(cellOneValue);
//存储一级分类id
String id_parent = null;
if(eduSubjectExist == null) {//如果不存在添加
//添加
EbsSubject eduSubject = new EbsSubject();
eduSubject.setTitle(cellOneValue);
eduSubject.setParentId("0");
eduSubject.setSort(0);
baseMapper.insert(eduSubject);
//把新添加的一级分类id获取到进行赋值
id_parent = eduSubject.getId();
} else {//存在,不添加
//把一级分类id赋值id_parent
id_parent = eduSubjectExist.getId();
}

//5 row获取第二列
Cell cellTwo = row.getCell(1);
if(cellTwo == null) {
String str = "第"+i+"行数据为空";
// 跳出这一行,往下继续执行
msg.add(str);
continue;
}
//不为空,获取第二列值
String cellTwoValue = cellTwo.getStringCellValue();
//添加二级分类
//判断数据库表是否存储二级分类,如果不存在进行添加
EbsSubject twoSubjectExist = this.existTwoSubject(cellTwoValue, id_parent);
if(twoSubjectExist == null) {
EbsSubject twoSubject = new EbsSubject();
twoSubject.setTitle(cellTwoValue);
twoSubject.setParentId(id_parent);
twoSubject.setSort(0);
baseMapper.insert(twoSubject);
}
}
return msg;
}catch(Exception e) {
e.printStackTrace();
throw new EbsException(20001,"导入失败出现异常");
}

}



实现Final

service层辅助方法

//判断数据库表是否存在二级分类


private EbsSubject existTwoSubject(String name,String parentid) {
QueryWrapper<EbsSubject> wrapper = new QueryWrapper<>();
//拼接条件
wrapper.eq("title",name);
wrapper.eq("parent_id",parentid);
EbsSubject ebsSubject = baseMapper.selectOne(wrapper);
return ebsSubject;
}



// 判断数据库表是否存在一级分类

private EbsSubject existOneSubject(String name) {
QueryWrapper<EbsSubject> wrapper = new QueryWrapper<>();
//拼接条件
wrapper.eq("title",name);
wrapper.eq("parent_id","0");
//调用方法
EbsSubject ebsSubject = baseMapper.selectOne(wrapper);
return ebsSubject;
}


 


3、 分类列表展示

1. 前端实现

1.1. 参考 views/tree/index.vue


1.2. 创建api

api/ebs/subject.js


import request from '@/utils/request'

const api_name = '/ebs/subject'

export default {

getNestedTreeList() {

return request({

url: `${api_name}`,

method: 'get'

})

}

}


 1.3. list.vue 

<template>

<div class="app-container">

<el-input v-model="filterText" placeholder="Filter keyword" style="margin-bottom:30px;" />

<el-tree

ref="subjectTree"

:data="subjectList"

:props="defaultProps"

:filter-node-method="filterNode"

class="filter-tree"

default-expand-all

/>

</div>

</template>

<script>

import subject from '@/api/ebs/subject'

export default {

data() {

return {

filterText: '',

subjectList: [],

defaultProps: {

children: 'children',

label: 'title'

}

}

},

watch: {

filterText(val) {

this.$refs.subjectTree.filter(val)

}

},

created() {

this.fetchNodeList()

},

methods: {

fetchNodeList() {

subject.getNestedTreeList().then(response => {

if (response.success === true) {

this.subjectList = response.data.items

}

})

},

filterNode(value, data) {

if (!value) return true

return data.title.indexOf(value) !== -1

}

}

}

</script>



2. 后端实现

后端接口数据:

2.1. 创建vo 

package com.yxzx.ebs.entity.vo;

@Data
public class EbsSubjectVo {

private String id;
private String title;
}


@Data
public class EbsSubjectNestedVo {

private String id;
private String title;
private List<EbsSubjectVo> children = new ArrayList<>();
}


2.2. 创建controller 

@ApiOperation(value = "嵌套数据列表")
@GetMapping
public Result nestedList() {

List<EbsSubjectNestedVo> subjectNestedVoList = subjectService.nestedList();
return Result.ok().data("items", subjectNestedVoList);
}


2.3. 创建service

接口 EbsSubjectService

/**
* 嵌套树形结构列表数据
* @return
*/
List<EbsSubjectNestedVo> nestedList();



实现v1:获取一级分类列表

 

/**
* 嵌套树形结构列表数据
*
* @return
*/
@Override
public List<EbsSubjectNestedVo> nestedList() {

//最终要的到的数据列表
ArrayList<EbsSubjectNestedVo> subjectNestedVoArrayList = new ArrayList<>();

//获取一级分类数据记录
QueryWrapper<EbsSubject> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id", 0);
queryWrapper.orderByAsc("sort", "id");
List<EbsSubject> subjects = baseMapper.selectList(queryWrapper);

//获取二级分类数据记录
//TODO

//填充一级分类vo数据
int count = subjects.size();
for (int i = 0; i < count; i++) {
EbsSubject ebsSubject = subjects.get(i);

//创建一级类别vo对象
EbsSubjectNestedVo subjectNestedVo = new EbsSubjectNestedVo();
BeanUtils.copyProperties(ebsSubject, subjectNestedVo);
subjectNestedVoArrayList.add(subjectNestedVo);

//填充二级分类vo数据
//@TODO
return subjectNestedVoArrayList;
}


实现v2:完善二级分类记录获取和填充 


//获取二级分类数据记录
QueryWrapper<EbsSubject> queryWrapper2 = new QueryWrapper<>();
queryWrapper2.ne("parent_id", 0);
queryWrapper2.orderByAsc("sort", "id");
List<EbsSubject> subSubjects = baseMapper.selectList(queryWrapper2);

//填充二级分类vo数据
ArrayList<EbsSubjectVo> subjectVoArrayList = new ArrayList<>();
int count2 = subSubjects.size();
for (int j = 0; j < count2; j++) {

EbsSubject subSubject = subSubjects.get(j);
if(ebsSubject.getId().equals(subSubject.getParentId())){

//创建二级类别vo对象
EbsSubjectVo subjectVo = new EbsSubjectVo();
BeanUtils.copyProperties(subSubject, subjectVo);
subjectVoArrayList.add(subjectVo);
}
}
subjectNestedVo.setChildren(subjectVoArrayList);
}

3. 优化前端过滤功能

 

filterNode(value, data) {

if (!value) return true

return data.title.toLowerCase().indexOf(value.toLowerCase()) !== -1

}

filter-node-method

对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏

Function(value, data, node)

搜索实现步骤:

第一步:定义组件

<el-input placeholder="输入关键字进行过滤" v-model="filterText">

data() {

return {

filterText: ''//记录搜索关键字

}

第二步:设置监听

watch: { filterText(val) { this.$refs.tree.filter(val); } },

第三步:实现过滤函数

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


【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。