谷粒学院——第七章、课程分类管理

news2024/12/27 13:11:49

EasyExcel 介绍

简介

Excel导入导出的应用场景

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

EasyExcel的特点

  • Java 领域解析、生成 Excel 比较有名的框架有 Apache poi、jxl 等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会 OOM 或者 JVM 频繁的 full gc。
  • EasyExcel 是阿里巴巴开源的一个 excel 处理框架,以使用简单、节省内存著称。EasyExcel 能大大减少占用内存的主要原因是在解析 Excel 时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
  • EasyExcel 采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理 (AnalysisEventListener)。

EasyExcel 读写操作案例

1、创建项目引入依赖

在service模块下创建 easy_excel_demo 模块,并在其 pom 文件中引入依赖:

<dependencies>
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.1.1</version>
  </dependency>
</dependencies>

2、创建实体类

@Data
public class DemoData {
    // 设置表头名称
    @ExcelProperty(value = "学生编号",index = 0)
    private Integer sno;
    @ExcelProperty(value = "学生姓名",index = 1)
    private String sname;
}

3、实现写操作

创建类 TestEasyExcel,创建方法循环设置要添加到 Excel 的数据

public class TestEasyExcel {
    public static void main(String[] args) {
        // 实现Excel写的操作
        // 1、设置写入文件夹地址和excel文件名
        String filename = "/gulixy/write.xlsx";
        // 2、调用easyexcel里面的方法实现写操作
        // write方法两个参数,第一个参数文件路径名称,第二个参数实体类class
        EasyExcel.write(filename, DemoData.class).sheet("学生列表").doWrite(getData());
    }

    // 创建方法返回list集合
    private static List<DemoData> getData() {
        List<DemoData> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setSno(i);
            data.setSname("lucy"+i);
            list.add(data);
        }
        return list;
    }
}

运行main方法,结果:
image.png

4、实现读操作

(1)创建监听器

public class ExcelListener extends AnalysisEventListener<DemoData> {
    // 一行一行读取Excel内容
    @Override
    public void invoke(DemoData data, AnalysisContext analysisContext) {
        System.out.println("****" + data);
    }
    // 读取表头内容
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("表头:" + headMap);
    }
    // 读取完成之后
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {}
}

(2)读取操作

//main方法里面添加:
EasyExcel.read(filename, DemoData.class, new ExcelListener()).sheet().doRead();

课程分类功能

后端实现

1、引入依赖与代码生成器

在 service_edu 模块中引入依赖

<dependencies>
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.1.1</version>
  </dependency>
</dependencies>

将之前用过的代码生成器换一下表名为edu_subject即可。

注意添加自动创建时间的注解:
image.png

2、创建实体类

entity下建立excel包,然后创建类:

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

3、controller层

@Api(description = "课程分类管理")
@RestController
@RequestMapping("/eduservice/subject")
@CrossOrigin
public class EduSubjectController {
    @Autowired
    private EduSubjectService subjectService;

    // 添加课程分类
    // 获取上传过来的文件,把文件内容读取出来
    @ApiOperation(value = "Excel导入")
    @PostMapping("addSubject")
    public R addSubject(MultipartFile file) {
        // 上传过来Excel文件
        subjectService.saveSubject(file, subjectService);
        return R.ok();
    }
}

4、service层

(1)接口

public interface EduSubjectService extends IService<EduSubject> {
	// 添加课程分类文件
    void saveSubject(MultipartFile file, EduSubjectService subjectService);
}

(2)实现类

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

    @Override
    public void saveSubject(MultipartFile file, EduSubjectService subjectService) {
        try {
            // 获取文件输入流
            InputStream is = file.getInputStream();
            // 调用方法进行读取
            EasyExcel.read(is, SubjectData.class, new SubjectExcelListener(subjectService)).sheet().doRead();
        } catch (Exception e) {
            e.printStackTrace();
            throw new GuliException(20001, "添加课程分类失败");
        }
    }
}

5、创建监听器

创建包 listener,然后创建类:SubjectExcelListener

public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {

    // SubjectExcelListener不能交给spring管理,需要手动new
    // 提供有参和无参的构造器
    private EduSubjectService subjectService;
    
    public SubjectExcelListener() {
    }

    public SubjectExcelListener(EduSubjectService subjectService) {
        this.subjectService = subjectService;
    }

    // 读取Excel文件内容,一行一行的读取
    @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) {}
}

6、Swagger 测试

先准备一张表:
image.png
课程分类.xlsx

启动 EduApplication,访问 http://localhost:8001/swagger-ui.html,点击选择文件,添加刚刚制作的表格:
image.png
查看数据库:
image.png

前端实现

1、添加路由

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

2、添加组件

在 views/edu 目录下新建 subject 目录,然后创建 list.vue 和 save.vue,然后在 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/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="xls,xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,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() {
      // 提示信息
      this.loading = false
      this.$message({
        type: 'success',
        message: '添加课程分类成功'
      })
      // 跳转课程分类列表
      // 路由跳转
      this.$router.push({ path: '/subject/list' })
    },
    // 上传失败
    fileUploadError() {
      // 提示信息
      this.loading = false
      this.$message({
        type: 'error',
        message: '添加课程分类失败'
      })
    }
  }
}
</script>

3、测试

先清掉数据表 edu_subject 里面的数据,然后启动前后端项目,先点击选取文件,再点击上传到服务器:
image.png
成功:
image.png

课程列表功能

后端实现

image.png

1、创建实体类

在 entity 包下建立 subject 包,然后创建两个类:

// 一级分类
@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;
}

2、controller 层

在 EduSubjectController 类中添加方法:

// 课程分类列表(树形)
@ApiOperation(value = "获取课程分类列表")
@GetMapping("getAllSubject")
public R getAllSubject() {
    // list集合泛型是一级分类
    List<OneSubject> list = subjectService.getAllOneTwoSubject();
    return R.ok().data("list", list);
}

3、service 层

在EduSubjectService接口中增加方法:

// 课程分类列表(树形)
List<OneSubject> getAllOneTwoSubject();

在EduSubjectServiceImpl类中增加方法:

// 课程分类列表(树形)
@Override
public List<OneSubject> getAllOneTwoSubject() {
    // 1、查询所有一级分类
    QueryWrapper<EduSubject> wrapperOne = new QueryWrapper<>();
    wrapperOne.eq("parent_id", "0");
    List<EduSubject> oneSubjectList = baseMapper.selectList(wrapperOne);

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

    // 3、创建list集合,用于存储最终封装数据
    List<OneSubject> finalSubjectList = new ArrayList<>();

    // 4、封装一级分类
    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());
        // 快速封装属性,相当于上面两行的内容
        BeanUtils.copyProperties(eduSubject, oneSubject);//注意这里是Spring包下的BeanUtils工具类
        finalSubjectList.add(oneSubject);

        // 5、在一级分类循环遍历查询所有的二级分类
        List<TwoSubject> twoFinalSubjectList = new ArrayList<>();
        for (int j = 0; j < twoSubjectList.size(); j++) {
            EduSubject subject = twoSubjectList.get(j);
            if (subject.getParentId().equals(eduSubject.getId())) {
                TwoSubject twoSubject = new TwoSubject();
                BeanUtils.copyProperties(subject, twoSubject);
                twoFinalSubjectList.add(twoSubject);
            }
        }
        // 把一级下面所有二级分类放到一级分类里面
        oneSubject.setChildren(twoFinalSubjectList);
    }
    return finalSubjectList;
}

4、Swagger 测试

启动项目,访问:http://localhost:8001/swagger-ui.html
image.png
点击 Try it out!,成功:
image.png

前端实现

1、js

在src/api/edu目录下建立subject.js:

import request from '@/utils/request'

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

2、vue

views/edu/subject/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'
      }
    }
  },
  watch: {
    filterText(val) {
      this.$refs.tree2.filter(val)
    }
  },
  created() {
    this.getAllSubjectList()
  },
  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>

效果:
image.png
输入数据还可以自动检索:
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/140314.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python数据分析三剑客之Pandas

写在前面的话&#xff1a; 开始之前请确保已经配置好python环境&#xff0c;并安装好第三方库pandas和numpy。 1. pandas库介绍 什么是pandas&#xff1f;pandas是提供高性能易用数据类型和数据分析工具的第三方库。简单讲&#xff0c;pandas主要作用有两个&#xff1a;提供了…

电子学会2020年6月青少年软件编程(图形化)等级考试试卷(二级)答案解析

目录 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 二、判断题&#xff08;共10题&#xff0c;每题2分&#xff0c;共20分&#xff09; 三、编程题&#xff08;共3题&#xff0c;共30分&#xff09; 青少年软件编程&#xff08;Scratch&…

谷粒学院——第八章、课程管理

一、课程添加功能 概览 课程添加的步骤 课程相关表的关系 后端实现 1、代码生成器 只修改表名即可&#xff0c;依次填入&#xff1a;“edu_course”, “edu_course_description”, “edu_chapter”, “edu_video” 生成完成后&#xff0c; 删除EduCourseDescriptionContr…

力扣 1801. 积压订单中的订单总数

题目 给你一个二维整数数组 orders &#xff0c;其中每个 orders[i] [pricei, amounti, orderTypei] 表示有 amounti 笔类型为 orderTypei 、价格为 pricei 的订单。 订单类型 orderTypei 可以分为两种&#xff1a; 0 表示这是一批采购订单 buy 1 表示这是一批销售订单 sel…

学习疑惑:用什么方法进行产品原型设计

对于在互联网行业的各位来讲&#xff0c;应该很清楚原型设计在应用开发中的重要性。它所起到的不仅是沟通的作用&#xff0c;更有体现之效。通过内容和结构展示&#xff0c;以及粗略布局&#xff0c;能够说明用户将如何与产品进行交互&#xff0c;体现开发者及UI设计师的idea&a…

pytorch拼接函数:torch.stack()和torch.cat()详解

在pytorch中&#xff0c;常见的拼接函数主要是两个&#xff0c;分别是&#xff1a;stack()和cat()。 torch.stack()函数的意义&#xff1a;使用stack可以保留两个信息&#xff1a;[1. 序列] 和 [2. 张量矩阵] 信息&#xff0c;属于【扩张再拼接】的函数。 形象的理解&#xff…

谷粒学院——第十二章、Banner轮播图

Banner微服务 配置 Nginx 修改文件&#xff1a;nginx.conf 修改完后&#xff0c;重启 nginx nginx -s reload创建项目和初始化 1、新建模块 service_cms 2、配置文件和启动类 创建配置文件&#xff1a;application.properties # 服务端口 server.port8004# 服务名 spr…

电子招标采购系统源码之从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。

统一供应商门户 便捷动态、呈现丰富 供应商门户具备内外协同的能力&#xff0c;为外部供应商集中推送展示与其相关的所有采购业务信息&#xff08;历史合作、考察整改&#xff0c;绩效评价等&#xff09;&#xff0c;支持供应商信息的自助维护&#xff0c;实时风险自动提示。…

互联网还会回暖吗?蛮三刀酱的2022年终技术总结

靠近年底的月份&#xff0c;内心深处总会不断提醒你&#xff0c;该写年终总结了。无论是工作上&#xff0c;生活上&#xff0c;亦或是互联网、APP上&#xff0c;无数的年终报告提醒我&#xff0c;一年了也该总结一下自己了&#xff0c;不然这之前的1年是不是白过了呢&#xff1…

Vue组件之间的通信(二)

一、兄弟组件之间的通信 &#xff08;1&#xff09;使用Vue的状态管理器Vue&#xff1a;建议在大型项目中使用 &#xff08;2&#xff09;使用第三方的模块&#xff1a;mitt(中央数据总线方式)&#xff0c;创建一个事件中心&#xff0c;由它来进行事件的监听、触发&#xff0…

第一章算法简介

二分查找引入大O表示法 仅知道算法需要多长时间运行完毕还不够&#xff0c;还需要知道运行时间如何随列表长度增加而增加&#xff0c;这正是大O表示法的用武之地。 大O表示法 大O表示法指出了算法有多块&#xff0c;之所以称作大O&#xff0c;单纯因为前面有个O(funny!)大O表…

Allegro174版本新功能介绍之导入导出菜单栏设置

Allegro174版本新功能介绍之导入导出菜单栏设置 Allegro174版本除了支持锁定菜单栏设置之外,同样还支持导出和导入菜单栏的设置 具体操作如下 首先设置并且排列好菜单栏,类似下图选

基于链表的通信录管理

意义&#xff1a;对于一个通信录来说&#xff0c;要管理联系人的信息&#xff0c;包括编号&#xff0c;姓名&#xff0c;性别&#xff0c;电话。开发其系统主要为了帮助用户提高通讯录有管理效率&#xff0c;节约资源&#xff0c;提高信息的精确度模块&#xff1a;一级菜单内容…

【Go基础】数据类型

文章目录1. 数据类型1.1 基本数据类型1.2 复合数据类型1.3 自定义数据类型2. 数组3. 切片4. 字符串5. 数据类型转换6. Map7. Channel1. 数据类型 1.1 基本数据类型 类型长度(字节)默认值说明bool1falsebyte10uint8&#xff0c;取值范围[0,255]rune40Unicode Code Point, int3…

电脑解压文件丢失怎么找回来?四种恢复方法

您在使用电脑过程中&#xff0c;有没有解压过文件呢&#xff1f;如果有的话&#xff0c;是否出现过解压文件丢失的情况呢&#xff1f;解压文件通俗易懂地讲就是把压缩好了的文件解开。而有些小伙伴在解压文件以后发现丢失了&#xff0c;这些丢失的文件或许是您重要的img解压文件…

数学建模学习笔记-算法(求解整数规划-2.割平面算法)

基本思想 松弛问题无解&#xff0c;则整数规划无解 松弛问题的最优解是整数解&#xff0c;则他也是整数规划的最优解 如果松弛问题的解是非整数解&#xff0c;则对其增加割平面条件。 割平面条件&#xff1a;增加一个线性条件&#xff0c;通过不等式将可行区域割掉一部分&a…

如何使用ebpf统计某个端口的流量

前言 上篇文章我们已经初步使用kprobe来探测内核函数了, 这篇文章就在上篇文章的基础上做个修改, 通过kprobe探测内核函数tcp_sendmsg来统计tcp服务端的发送流量. 废话不多说, 直接上正文. 环境 tcp服务端运行在ubuntu22, 监听端口为6230, 其内核为5.19.0-26-generic, ebpf程序…

LSA、pLSA、LDA、NMF、BERTopic、Top2Vec进行主题建模

在自然语言处理(NLP)中,主题建模是一种技术,用于从文本数据中发现隐藏的语义主题(或主题)。这是一个无监督机器学习问题,即在没有标签或标签的情况下学习模式。主题建模的应用非常广泛,可用于搜索引擎、情感分析、新闻聚类和摘要生成等许多任务。 在这里将探讨主题建模…

一屏统管 智慧交管Web3D可视化大屏云控系统

交通是城市发展的基础&#xff0c;体现着社会文明程度&#xff0c;彰显着城市治理水平。今天给大家分享一个基于 数维图 的 Sovit3D编辑器 构建轻量化 3D 可视化场景的案例——智慧交管三维可视化系统。多维度呈现城市交通情况&#xff0c;赋能“安全管控、缓堵保畅、出行服务”…

硬件系统工程师宝典(2)-----硬件电路的概要设计启动

今天我们继续来读这本书&#xff0c;硬件系统工程师宝典。作者提到&#xff0c;产品需求分析之后就进入概要设计阶段。在这个阶段&#xff0c;ID&#xff08;Industrial Design&#xff09;工业设计及结构工程师、软件系统开发工程师和硬件系统开发工程师等开始分头工作。 工业…