15. 尚融宝后台实现数据字典、导入、导出

后端实现Excel批量导入数据库

service-core模块

1、pom

<dependencies>		
		<dependency>
			<groupId>com.alibabagroupId>
			<artifactId>easyexcelartifactId>
		dependency>

		<dependency>
			<groupId>org.apache.xmlbeansgroupId>
			<artifactId>xmlbeansartifactId>
		dependency>
dependencies>

<build>
    
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
    resources>
build>

2、pojo

创建dto

ExcelDictDTO.java

package com.indi.srb.core.pojo.dto;

@Data
public class ExcelDictDTO {
     
    // ExcelProperty注解
    // 写入的时候,会写到Excel的表头,
    // 读取的时候,会根据表头的名称,自动的读到对应的属性上
    @ExcelProperty("id")
    private Long id;

    @ExcelProperty("上级id")
    private Long parentId;

    @ExcelProperty("名称")
    private String name;

    @ExcelProperty("值")
    private Integer value;

    @ExcelProperty("编码")
    private String dictCode;
}

3、mapper

DictMapper.java

    void insertBatch(List<ExcelDictDTO> list);

DictMapper.xml

	<insert id="insertBatch">
		insert into dict(
			id,
			parent_id,
			name,
			value,
			dict_code
		) values
		<foreach collection="list" item="item" index="index" separator=",">
			(
			#{item.id},
			#{item.parentId},
			#{item.name},
			#{item.value},
			#{item.dictCode}
			)
		foreach>
	insert>

3、监听器

com.indi.srb.core包下面创建listener

ExcelDictDTOListener.java

package com.indi.srb.core.listener;

@Slf4j
@NoArgsConstructor
public class ExcelDictDTOListener extends AnalysisEventListener<ExcelDictDTO> {
     
    private DictMapper dictMapper;
    List<ExcelDictDTO> list = new ArrayList<>();    // 数据列表
    // 每5条记录存储一次数据
    private static final int BATCH_COUNT = 5;

    public ExcelDictDTOListener(DictMapper dictMapper) {
     
        this.dictMapper = dictMapper;
    }

    @Override
    public void invoke(ExcelDictDTO excelDictDTO, AnalysisContext analysisContext) {
     
        log.info("解析一条数据:{}", excelDictDTO);

        // 将数据存入数据列表
        list.add(excelDictDTO);
        if (list.size() >= BATCH_COUNT) {
     
            saveData();
            list.clear();
        }
    }

    private void saveData() {
     
        log.info("{}条数据被存储到数据库", list.size());
        // 调用mapper层的save方法:save list对象
        dictMapper.insertBatch(list);
        log.info("{}条数据存储到数据库成功", list.size());
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
     
        // 当最后剩余的记录数不足BATCH_COUNT时,最终一次性存入
        saveData();
        log.info("全部数据解析完成!");
    }
}

4、service

DictService.java

    void importData(InputStream inputStream);

DictServiceImpl.java

    // 一旦中途导入失败,直接回滚数据
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importData(InputStream inputStream) {
     
        EasyExcel.read(inputStream, ExcelDictDTO.class, new ExcelDictDTOListener(baseMapper)).sheet().doRead();
        log.info("Excel导入成功");
    }

6、controller

admin包下面创建AdminDictController.java

@Api(tags = "数据字典管理")
@RestController
@RequestMapping("/admin/core/dict")
@Slf4j
@CrossOrigin
public class AdminDictController {
     
    @Resource
    DictService dictService;

    @ApiOperation("Excel数据的批量导入")
    @PostMapping("/import")
    public R batchImport(
            @ApiParam(value = "Excel数据字典文件", required = true)
            @RequestParam("file") MultipartFile file){
     
        try {
     
            InputStream inputStream = file.getInputStream();
            dictService.importData(inputStream);
            return R.ok().setMessage("数据字典批量导入成功");
        } catch (IOException e) {
     
            throw new BusinessException(ResponseEnum.UPLOAD_ERROR, e);
        }
    }
}

前端实现Excel批量导入

1、添加路由

src/router/index.js

15. 尚融宝后台实现数据字典、导入、导出_第1张图片

  {
     
    path: '/core',
    component: Layout,
    redirect: '/core/dict/list',
    name: 'coreDict',
    meta: {
      title: '系统设置', icon: 'el-icon-setting' },
    alwaysShow: true,
    children: [
      {
     
        path: 'dict/list',
        name: '数据字典',
        component: () => import('@/views/core/dict/list'),
        meta: {
      title: '数据字典' }
      }
    ]
  },

2、创建页面

src/views/core/dict/list.vue

<template>
  <div class="app-container">
    <div style="margin-bottom: 10px;">
      <el-button
        @click="dialogVisible = true"
        type="primary"
        size="mini"
        icon="el-icon-download"
      >
        导入Excel
      el-button>
    div>

    <el-dialog title="数据字典导入" :visible.sync="dialogVisible" width="30%">
      <el-form>
        <el-form-item label="请选择Excel文件">
          <el-upload
            :auto-upload="false"
            :multiple="false"
            :limit="1"
            :on-exceed="fileUploadExceed"
            :on-success="fileUploadSuccess"
            :on-error="fileUploadError"
            :action="BASE_API + '/admin/core/dict/import'"
            name="file"
            accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
          >
            <el-button size="small" type="primary">点击上传el-button>
          el-upload>
        el-form-item>
      el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">
          取消
        el-button>
      div>
    el-dialog>
  div>
template>

<script>
export default {
      
  // 定义数据
  data() {
      
    return {
      
      dialogVisible: false, //文件上传对话框是否显示
      BASE_API: process.env.VUE_APP_BASE_API //获取后端接口地址
    }
  },

  methods: {
      
    // 上传多于一个文件时
    fileUploadExceed() {
      
      this.$message.warning('只能选取一个文件')
    },

    // 与服务器通信成功的回调
    fileUploadSuccess(response) {
      
      if (response.code === 0) {
      
        // 业务成功
        this.$message.success('数据导入成功')
        this.dialogVisible = false
      } else {
      
        // 业务失败
        this.$message.error(response.message)
      }
    },

    // 与服务器通信失败的回调
    fileUploadError(error) {
      
      this.$message.error('数据导入失败')
    }
  }
}
script>

3、测试

15. 尚融宝后台实现数据字典、导入、导出_第2张图片

后端实现Excel批量导出

1、service

DictService.java

    List<ExcelDictDTO> dictListData();

DictServiceImpl.java

    @Override
    public List<ExcelDictDTO> dictListData() {
     
        List<Dict> dictList = baseMapper.selectList(null);
        ArrayList<ExcelDictDTO> excelDictDTOList = new ArrayList<>(dictList.size());
        dictList.forEach(dict -> {
     
            ExcelDictDTO excelDictDTO = new ExcelDictDTO();
            BeanUtils.copyProperties(dict,excelDictDTO);
            excelDictDTOList.add((excelDictDTO));
        });
        return excelDictDTOList;
    }

2、controller

AdminDictController.java

    @ApiOperation("Excel数据的批量导出")
    @GetMapping("/export")
    public void export(HttpServletResponse response) {
     
        try {
     
            // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("mydict", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=mydict.xlsx");
            // 主要是这个地方
            EasyExcel.write(response.getOutputStream(), ExcelDictDTO.class).sheet().doWrite(dictService.dictListData());
        } catch (IOException e) {
     
            //EXPORT_DATA_ERROR(104, "数据导出失败"),
            throw new BusinessException(ResponseEnum.EXPORT_DATA_ERROR, e);
        }
    }

前端实现Excel批量导出

1、页面添加导出按钮

      <el-button
        @click="exportData"
        type="primary"
        size="mini"
        icon="el-icon-download"
      >
        导出Excel
      el-button>  

15. 尚融宝后台实现数据字典、导入、导出_第3张图片

2、添加导出方法

在methods中添加Excel数据导出方法

    // Excel数据导出
    exportData() {
     
      // 因为后端的导出接口是无刷新的,我们需要要让浏览器主动刷新,
      // 所以就不能是使用axios发送普通请求,需要使用下面这种方式发送请求
      window.location.href = this.BASE_API + '/admin/core/dict/export'
    }

3、测试

15. 尚融宝后台实现数据字典、导入、导出_第4张图片

后端获取字典列表

1、pojo

Dict.java中添加属性

    @ApiModelProperty(value = "是否包含子节点")
    @TableField(exist = false)	// 在数据库表中忽略此列
    private boolean hasChildren;

2、Service

DictService.java

	List<Dict> listByParentId(Long parentId);

DictServiceImpl.java

    @Override
    public List<Dict> listByParentId(Long parentId) {
     
        QueryWrapper<Dict> queryWrapper = new QueryWrapper<Dict>().eq("parent_id",parentId);
        List<Dict> dictList = baseMapper.selectList(queryWrapper);
        // 填充hasChilderen字段
        dictList.forEach(dict -> {
     
            boolean hasChildren = this.hasChildren(dict.getId());
            dict.setHasChildren(hasChildren);
        });
        return dictList;
    }
    
    /**
     * 判断当前id所在的节点是否有子节点
     */
    private boolean hasChildren(Long id) {
     
        QueryWrapper<Dict> queryWrapper = new QueryWrapper<Dict>().eq("parent_id",id);
        Integer count = baseMapper.selectCount(queryWrapper);
        if(count.intValue() > 0) {
     
            return true;
        }
        return false;
    }

3、Controller

AdminDictController.java

    /**
     * 方案二:延迟加载
     * 不需要后端返回数据中包含嵌套数据,但是要定义布尔属性hasChildren,表示当前节点是否包含子数据
     * 如果hasChildren为true,就表示当前节点包含子数据
     * 如果hasChildren为false,就表示当前节点不包含子数据
     * 如果当前节点包含子数据,那么点击当前节点的时候,就需要通过load方法加载子数据
     */    
	@ApiOperation("根据上级id获取子节点数据列表")
    @GetMapping("/listByParentId/{parentId}")
    public R listByParentId(
        @ApiParam(value = "上级节点id", required = true)
        @PathVariable Long parentId) {
     
        List<Dict> dictList = dictService.listByParentId(parentId);
        return R.ok().data("list", dictList);
    }

前端显示字典列表

1、api

创建 src/api/core/dict.js

import request from '@/utils/request'
export default {
     
  listByParentId(parentId) {
     
    return request({
     
      url: `/admin/core/dict/listByParentId/${
       parentId}`,
      method: 'get'
    })
  }
}

3、组件模板

src/views/core/dict/list.vue

放到之前写的闭合标签下面

    <el-table :data="list" border row-key="id" lazy :load="load">
      <el-table-column label="名称" align="left" prop="name" />
      <el-table-column label="编码" prop="dictCode" />
      <el-table-column label="" align="left" prop="value" />
    el-table>

2、组件脚本

先导入dictApi

import dictApi from '@/api/core/dict'

data中定义数据字典列表

      list: [] //数据字典列表

methods中添加新方法

    // 调用api层获取1级菜单数据
    fetchData() {
     
      dictApi.listByParentId(1).then(response => {
     
        this.list = response.data.list
      })
    },

    // 延迟加载子节点的方法
    load(tree, treeNode, resolve) {
     
      dictApi.listByParentId(tree.id).then(response => {
     
        // 负责将子节点数据放到展开的列表中  
        resolve(response.data.list)
      })
    }

初始化调用

  created() {
     
    this.fetchData()
  },

4、流程优化

数据导入后刷新页面的数据列表

15. 尚融宝后台实现数据字典、导入、导出_第5张图片

5、测试

15. 尚融宝后台实现数据字典、导入、导出_第6张图片

你可能感兴趣的:(#,尚融宝后台,excel,java)