欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Vue+EasyExcel实现Excel的导入导出功能

程序员文章站 2022-04-13 15:43:05
...

需要技术EasyExcel支持:https://www.yuque.com/easyexcel/doc/easyexcel

导出Excel

效果预览
Vue+EasyExcel实现Excel的导入导出功能
Vue+EasyExcel实现Excel的导入导出功能
代码奉上

Vue页面
带选中框的表格是直接从element-ui官网复制过来的
选中数据->选中的数据存放在data中的json_data变量中
点击导出Excel->将data中json_data的数据处理成后端想要的格式使用JSON.stringify()转化成Json字符串发送给后端接口

带选中框的表格

<template>
  <el-table
    ref="multipleTable"
    :data="tableData"
    tooltip-effect="dark"
    style="width: 100%"
    @selection-change="handleSelectionChange">
    <el-table-column
      type="selection"
      width="55">
    </el-table-column>
    <el-table-column
      label="日期"
      width="120">
      <template slot-scope="scope">{{ scope.row.date }}</template>
    </el-table-column>
    <el-table-column
      prop="name"
      label="姓名"
      width="120">
    </el-table-column>
    <el-table-column
      prop="address"
      label="地址"
      show-overflow-tooltip>
    </el-table-column>
  </el-table>
  <div style="margin-top: 20px">
    <el-button @click="toggleSelection([tableData[1], tableData[2]])">切换第二、第三行的选中状态</el-button>
    <el-button @click="toggleSelection()">取消选择</el-button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        tableData: [{
          date: '2016-05-03',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        }, {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        }, {
          date: '2016-05-04',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        }, {
          date: '2016-05-01',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        }, {
          date: '2016-05-08',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        }, {
          date: '2016-05-06',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        }, {
          date: '2016-05-07',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        }],
        multipleSelection: []
      }
    },

    methods: {
      toggleSelection(rows) {
        if (rows) {
          rows.forEach(row => {
            this.$refs.multipleTable.toggleRowSelection(row);
          });
        } else {
          this.$refs.multipleTable.clearSelection();
        }
      },
      handleSelectionChange(val) {
        this.multipleSelection = val;
      }
    }
  }
</script>

导出Excel按钮

<el-button type="primary" icon="el-icon-edit" @click="export_excel()" style="margin-top: 25px;">导出选中订单.Excel</el-button>

按钮绑定的点击事件方法

		//导出选中Excel
            export_excel(){
                const h = this.$createElement;
                if (this.json_data.length==0){
                    //如果没有选中任何数据
                    this.$notify({
                        type: 'error',
                        title: '导出失败',
                        message: h('i', { style: 'color: red'}, "您还没有选中任何订单数据")
                    });
                    return "sda";
                }
                let params=new URLSearchParams();
                //处理封装orderData对象
                let  orderDatas=[];
                for (let i = 0; i < this.json_data.length; i++) {
                    let order_data=this.json_data[i];
                    let orderData={};
                    let costStatus="";
                    switch (order_data.costStatus) {
                        case 0:costStatus="未支付";break;
                        case -1:costStatus="乘客取消";break;
                        case -2:costStatus="管理员取消";break;
                        case -3:costStatus="系统取消";break;
                        case -4:costStatus="失败";break;
                        case 1:costStatus="已支付";break;
                        case 2:costStatus="派车中";break;
                        case 3:costStatus="进行中";break;
                        case 4:costStatus="订单完成";break;
                    }
                    if(order_data.client==null){
                        orderData={origin:order_data.origin,destination:order_data.destination,beginTime:order_data.beginTime,endTime:order_data.endTime,node:"暂时没有备注",clname:"已删除",clphone:"已删除",clidCard:"已删除",sname:order_data.seller.ename,cost:order_data.cost,costStatus:costStatus};
                    }else {
                        orderData={origin:order_data.origin,destination:order_data.destination,beginTime:order_data.beginTime,endTime:order_data.endTime,node:"暂时没有备注",clname:order_data.client.clname,clphone:order_data.client.clphone,clidCard:order_data.client.clidcard,sname:order_data.seller.ename,cost:order_data.cost,costStatus:costStatus};
                    }
                    orderDatas[i]=orderData;
                }
                params.append("orderDatas",JSON.stringify(orderDatas));
                let _this=this;
                this.axios.post("/file/download",params)
                    .then(res=>{
                        this.$notify({
                            type: 'success',
                            title: '导出成功',
                            message: h('p', null, [
                                h('i', { style: 'color: red' }, 'Go+订单.xlsx'),
                                h('span', null, ' 成功导出到'),
                                h('i', { style: 'color: teal' }, res.data)
                            ])
                        });
                    }).catch(function(error) {
                        _this.$notify({
                            type: 'error',
                            title: '下载失败',
                            message: h('i', { style: 'color: red'}, "请检查文件是不是已经存在或打开")
                        });
                    });
            },

Controller层接口
将传递过来的JSON字符串转换成OrderData类型的List集合使用在Excel doWrite()导出中

需要先导入EasyExcel依赖

 <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>easyexcel</artifactId>
      <version>2.2.6</version>
    </dependency>

接口

    String PATH="D:\\谷歌下载\\";

    /**
     * 将传递过来的JSON字符串转换成OrderData类型的List集合使用在Excel doWrite()导出中
     * @param orderDatas
     * @return
     */
    @RequestMapping("/download")
    public String downloadExcel(String orderDatas) {
        // 写法1
        String fileName = PATH+ ExcelDateUtils.getDateString("go+订单模板") +".xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // write(fileName, 格式类)
        //sheet (表名)
        //doWrite(数据)
        List orderList = JSON.parseArray(orderDatas,OrderData.class);
        EasyExcel.write(fileName, OrderData.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("模板").doWrite(orderList);
        return PATH;
    }

Excel 批量导入数据

效果预览
Vue+EasyExcel实现Excel的导入导出功能
Vue+EasyExcel实现Excel的导入导出功能
Vue+EasyExcel实现Excel的导入导出功能
代码奉上

Vue导入按钮
element-ui官网的按钮action绑定事件
点击按钮->选择文件->调用后台接口->调用上传成功方法->异步刷新表格https://blog.csdn.net/liudachu/article/details/109207192

 <el-upload
                        class="upload-demo"
                        action="/api/file/upload"
                        :on-success="upload_success"
                        :on-preview="handlePreview"
                        :on-remove="handleRemove"
                        :before-remove="beforeRemove"
                        multiple
                        :limit="3"
                        :on-exceed="handleExceed"
                        :file-list="fileList">
                    <el-button  type="primary">点击导入Excel文件</el-button>
                </el-upload>

按钮绑定的方法

/*Excel上传成功!*/
            upload_success(file, fileList){
                const h = this.$createElement;
                this.$notify({
                    type: 'success',
                    title: '导入成功',
                    message: h('p', null, [
                        h('i', { style: 'color: teal' }, file),
                        h('span', null, ' 成功导入!'),
                    ])
                });
                //异步刷新
                this.reload();
            },
            handleRemove(file, fileList) {
                console.log(file, fileList);
            },
            handlePreview(file) {
                console.log(file);
            },
            handleExceed(files, fileList) {
                this.$message.warning(`当前限制选择 3 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
            },
            beforeRemove(file, fileList) {
                return this.$confirm(`确定移除 ${ file.name }?`);
            },

Controller接口
1.获取传递过来的CommonsMultipartFile文件
2.保存文件到(上传路径)服务器
3.使用EasyExcel读取Excel文件,并调用Listenter进行数据库数据的导入

 @Autowired
    private FileMapper fileMapper;

    /**
     * 1.获取传递过来的CommonsMultipartFile文件
     * 2.保存文件到(上传路径)服务器
     * 3.使用EasyExcel读取Excel文件,并调用Listenter进行数据库数据的导入
     * @param file
     * @return
     * @throws IOException
     */
    @RequestMapping("/upload")
    public String uploadExcel(@RequestParam("file") CommonsMultipartFile file) throws IOException {
        //获取文件名 : file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();
        /**
         *   使用springmvc中的CommonsMultipartFile进行文件的保存
         */
        //上传路径保存设置
        String path = "D:\\谷歌下载\\GO+订单\\";
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        //上传文件地址
       // System.out.println("上传文件保存地址:"+realPath);
        //通过CommonsMultipartFile的方法直接写文件(注意这个时候)
        file.transferTo(new File(realPath +"/"+ uploadFileName));

        /**
         * 使用EasyExcel读取Excel文件 进行数据的导入
         */
        EasyExcel.read(path+uploadFileName, OrderData.class, new OrderDataListener(fileMapper)).sheet().doRead();

        //返回文件名
        return uploadFileName;
    }

Listener

package com.chif.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.chif.dao.FileMapper;
import com.chif.pojo.OrderData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

// 有个很重要的点 OrderDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class OrderDataListener extends AnalysisEventListener<OrderData> {
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderDataListener.class);
    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<OrderData> list = new ArrayList<OrderData>();
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
     //private DemoDAO demoDAO;
     private FileMapper fileMapper;
    /* public OrderDataListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoDAO = new DemoDAO();
    }*/
    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param fileMapper
     */
    public OrderDataListener(FileMapper fileMapper) {
        this.fileMapper = fileMapper;
    }
    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data
     *            one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(OrderData data, AnalysisContext context) {
        //System.out.println(JSON.toJSONString(data));
        data.setCreateTime(new Date());
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }
    /**
     * 所有数据解析完成了 都会来调用
     *
     * DemoData 类型
     * AnalysisContext 分析上下文
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        LOGGER.info("所有数据解析完成!");
    }
    /**
     * 加上存储数据库
     */
    private void saveData() {
        LOGGER.info("{}条数据,开始存储数据库!", list.size());
        fileMapper.addOrdersByList(list);
        LOGGER.info("存储数据库成功!");
    }
}

调用的持久层mapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chif.dao.FileMapper">
    
    <insert id="addOrdersByList" parameterType="java.util.List">
        insert into db_go.t_order
        (origin, destination, begin_time, end_time, node, client_id, bus_id, seller_id, cost, cost_status, create_time)
        values
        <foreach collection="list" item="item" index="index" separator="," >
            (#{item.origin},#{item.destination},#{item.beginTime},#{item.endTime},#{item.node},
            (select cl_id from db_go.t_client where cl_name=#{item.clname}),
            1,
            (select e_id from db_go.t_emp where e_name=#{item.sname}),
            #{item.cost},
            (select id from t_status where status=#{item.costStatus}),
            #{item.createTime})
        </foreach>
    </insert>

</mapper>

EasyExcel需要封装的实体类

package com.chif.pojo;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.HeadFontStyle;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;

import java.util.Date;

@Data
@HeadRowHeight(20)
// 头字体设置成20
@HeadFontStyle(fontHeightInPoints = 10)
public class OrderData {
    @ExcelProperty("出发地点")
    private String origin;//出发地点
    @ExcelProperty("目的地")
    private String destination;//目的地
    @ExcelProperty("出发时间")
    private Date beginTime;//出发时间
    @ExcelProperty("预计结束时间")
    private Date endTime;//预计结束时间
    @ExcelProperty("备注")
    private String node;//备注
    @ExcelProperty("乘客姓名")
    private String clname;//乘客姓名
    @ExcelProperty("乘客电话")
    private String clphone;//乘客电话
    @ExcelProperty("乘客身份证号")
    private String clidCard;//乘客身份证号
    @ExcelProperty("售票员姓名")
    private String sname;//售票员姓名
    @ExcelProperty("费用")
    private String cost;//费用
    @ExcelProperty("缴费状态")
    private String costStatus;

    @ExcelIgnore
    private Date createTime;

}

完结