vue-element-admin项目导入和导出的实现


Posted in Vue.js onMay 21, 2021

vue-element-admin导入组件封装

模板和样式

vue-element-admin项目导入和导出的实现

首先封装一个类似的组件,首先需要注意的是,类似功能,vue-element-admin已经提供了,我们只需要改造即可
excel导入功能需要使用npm包xlsx,所以需要安装xlsx插件

npm i xlsx

将vue-element-admin提供的导入功能新建一个组件,位置: src/components/UploadExcel

import CommonTools from './CommonTools'
import UploadExcel from './UploadExcel'
export default {
  install(Vue) {
    Vue.component('CommonTools', CommonTools) // 注册工具栏组件
    Vue.component('UploadExcel', UploadExcel) // 注册导入excel组件
  }
}

修改样式和布局

<template>
  <div class="upload-excel">
    <div class="btn-upload">
      <el-button :loading="loading" size="mini" type="primary" @click="handleUpload">
        点击上传
      </el-button>
    </div>

    <input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
    <div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
      <i class="el-icon-upload" />
      <span>将文件拖到此处</span>
    </div>
  </div>
</template>

<script>
import XLSX from 'xlsx'

export default {
  props: {
    beforeUpload: Function, // eslint-disable-line
    onSuccess: Function// eslint-disable-line
  },
  data() {
    return {
      loading: false,
      excelData: {
        header: null,
        results: null
      }
    }
  },
  methods: {
    generateData({ header, results }) {
      this.excelData.header = header
      this.excelData.results = results
      this.onSuccess && this.onSuccess(this.excelData)
    },
    handleDrop(e) {
      e.stopPropagation()
      e.preventDefault()
      if (this.loading) return
      const files = e.dataTransfer.files
      if (files.length !== 1) {
        this.$message.error('Only support uploading one file!')
        return
      }
      const rawFile = files[0] // only use files[0]

      if (!this.isExcel(rawFile)) {
        this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files')
        return false
      }
      this.upload(rawFile)
      e.stopPropagation()
      e.preventDefault()
    },
    handleDragover(e) {
      e.stopPropagation()
      e.preventDefault()
      e.dataTransfer.dropEffect = 'copy'
    },
    handleUpload() {
      this.$refs['excel-upload-input'].click()
    },
    handleClick(e) {
      const files = e.target.files
      const rawFile = files[0] // only use files[0]
      if (!rawFile) return
      this.upload(rawFile)
    },
    upload(rawFile) {
      this.$refs['excel-upload-input'].value = null // fix can't select the same excel

      if (!this.beforeUpload) {
        this.readerData(rawFile)
        return
      }
      const before = this.beforeUpload(rawFile)
      if (before) {
        this.readerData(rawFile)
      }
    },
    readerData(rawFile) {
      this.loading = true
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = e => {
          const data = e.target.result
          const workbook = XLSX.read(data, { type: 'array' })
          const firstSheetName = workbook.SheetNames[0]
          const worksheet = workbook.Sheets[firstSheetName]
          const header = this.getHeaderRow(worksheet)
          const results = XLSX.utils.sheet_to_json(worksheet)
          this.generateData({ header, results })
          this.loading = false
          resolve()
        }
        reader.readAsArrayBuffer(rawFile)
      })
    },
    getHeaderRow(sheet) {
      const headers = []
      const range = XLSX.utils.decode_range(sheet['!ref'])
      let C
      const R = range.s.r
      /* start in the first row */
      for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
        const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
        /* find the cell in the first row */
        let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
        if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
        headers.push(hdr)
      }
      return headers
    },
    isExcel(file) {
      return /\.(xlsx|xls|csv)$/.test(file.name)
    }
  }
}
</script>

<style scoped lang="scss">
.upload-excel {
  display: flex;
  justify-content: center;
   margin-top: 100px;
   .excel-upload-input{
       display: none;
        z-index: -9999;
     }
   .btn-upload , .drop{
      border: 1px dashed #bbb;
      width: 350px;
      height: 160px;
      text-align: center;
      line-height: 160px;
   }
   .drop{
       line-height: 80px;
       color: #bbb;
      i {
        font-size: 60px;
        display: block;
      }
   }
}
</style>

创建路由和组件

建立公共导入的页面路由,新建一个公共的导入页面,挂载路由 src/router/index.js

path: '/import',
    component: Layout,
    hidden: true, // 隐藏在左侧菜单中
    children: [{
      path: '', // 二级路由path什么都不写 表示二级默认路由
      component: () => import('@/views/import')
    }]
  },

创建import路由组件 src/views/import/index.vue

<template>
  <!-- 公共导入组件 --> 
  <upload-excel :on-success="success" />
</template>

实现导入

封装导入用户的api接口

export function importUser(data) {
  return request({
    url: 'user/batch',
    method: 'post',
    data
  })
}

获取导入的excel数据, 导入excel接口

async  success({ header, results }) {
      // 如果是导入用户
        const userRelations = {
          '入职日期': 'create_time',
          '手机号': 'mobile',
          '用户名': 'username',
          '密码': 'password',
          '邮箱': 'email',
          '部门':'部门'
        }
        const arr = []
       results.forEach(item => {
          const userInfo = {}
          Object.keys(item).forEach(key => {
            userInfo[userRelations[key]] = item[key]
          })
         arr.push(userInfo) 
        })
        await importUser(arr) // 调用导入接口
        this.$router.back()
    }

为了让这个页面可以服务更多的导入功能,我们可以在页面中用参数来判断,是否是导入用户

data() {
    return {
      type: this.$route.query.type
    }
  },

当excel中有日期格式的时候,实际转化的值为一个数字,我们需要一个方法进行转化

formatDate(numb, format) {
      const time = new Date((numb - 1) * 24 * 3600000 + 1)
      time.setYear(time.getFullYear() - 70)
      const year = time.getFullYear() + ''
      const month = time.getMonth() + 1 + ''
      const date = time.getDate() - 1 + ''
      if (format && format.length === 1) {
        return year + format + month + format + date
      }
      return year + (month < 10 ? '0' + month : month) + (date < 10 ? '0' + date : date)
    }

导入的手机号不能和之前的存在的手机号重复

逻辑判断

async  success({ header, results }) {
      if (this.type === 'user') {
        const userRelations = {
          '入职日期': 'create_time',
          '手机号': 'mobile',
          '用户名': 'username',
          '密码': 'password',
          '邮箱': 'email',
          '部门':'部门'
        }
        const arr = []
        // 遍历所有的数组
        results.forEach(item => {
        // 需要将每一个条数据里面的中文都换成英文
          const userInfo = {}
          Object.keys(item).forEach(key => {
          // key是当前的中文名 找到对应的英文名
            if (userRelations[key] === 'timeOfEntry' || userRelations[key] === 'correctionTime') {
              userInfo[userRelations[key]] = new Date(this.formatDate(item[key], '/')) // 只有这样, 才能入库
              return
            }
            userInfo[userRelations[key]] = item[key]
          })
          // 最终userInfo变成了全是英文
          arr.push(userInfo)
        })
        await importUser(arr)
        this.$message.success('导入成功')
      }
      this.$router.back() // 回到上一页
    },
    formatDate(numb, format) {
      const time = new Date((numb - 1) * 24 * 3600000 + 1)
      time.setYear(time.getFullYear() - 70)
      const year = time.getFullYear() + ''
      const month = time.getMonth() + 1 + ''
      const date = time.getDate() - 1 + ''
      if (format && format.length === 1) {
        return year + format + month + format + date
      }
      return year + (month < 10 ? '0' + month : month) + (date < 10 ? '0' + date : date)
    }

用户页面跳转

<el-button type="warning" size="small" @click="$router.push('/import?type=user')">导入</el-button>

用户导出**

日常业务中,我们经常遇到excel导出功能, 怎么使用呢
Excel 的导入导出都是依赖于js-xlsx来实现的。
在 js-xlsx的基础上又封装了Export2Excel.js来方便导出数据。

安装所需依赖

npm install xlsx file-saver -S
npm install script-loader -S -D

由于js-xlsx体积还是很大的,导出功能也不是一个非常常用的功能,所以使用的时候建议使用懒加载。使用方法如下:

import('@/vendor/Export2Excel').then(excel => {
  excel.export_json_to_excel({
    header: tHeader, //表头 必填
    data, //具体数据 必填
    filename: 'excel-list', //非必填
    autoWidth: true, //非必填
    bookType: 'xlsx' //非必填
  })
})

excel导出参数的介绍

vue-element-admin提供了导出的功能模块,在课程资源/excel导出目录下,放置到src目录下

vue-element-admin项目导入和导出的实现

excel导出基本的结构

下面代码会用到 Export2Excel.js 模块,所以首先在 src 目录下新建 vendor 目录,其中新建 Export2Excel.js ,输入如下代码

/* eslint-disable */
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'

function generateArray(table) {
  var out = [];
  var rows = table.querySelectorAll('tr');
  var ranges = [];
  for (var R = 0; R < rows.length; ++R) {
    var outRow = [];
    var row = rows[R];
    var columns = row.querySelectorAll('td');
    for (var C = 0; C < columns.length; ++C) {
      var cell = columns[C];
      var colspan = cell.getAttribute('colspan');
      var rowspan = cell.getAttribute('rowspan');
      var cellValue = cell.innerText;
      if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;

      //Skip ranges
      ranges.forEach(function (range) {
        if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
          for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
        }
      });

      //Handle Row Span
      if (rowspan || colspan) {
        rowspan = rowspan || 1;
        colspan = colspan || 1;
        ranges.push({
          s: {
            r: R,
            c: outRow.length
          },
          e: {
            r: R + rowspan - 1,
            c: outRow.length + colspan - 1
          }
        });
      };

      //Handle Value
      outRow.push(cellValue !== "" ? cellValue : null);

      //Handle Colspan
      if (colspan)
        for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
    }
    out.push(outRow);
  }
  return [out, ranges];
};

function datenum(v, date1904) {
  if (date1904) v += 1462;
  var epoch = Date.parse(v);
  return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}

function sheet_from_array_of_arrays(data, opts) {
  var ws = {};
  var range = {
    s: {
      c: 10000000,
      r: 10000000
    },
    e: {
      c: 0,
      r: 0
    }
  };
  for (var R = 0; R != data.length; ++R) {
    for (var C = 0; C != data[R].length; ++C) {
      if (range.s.r > R) range.s.r = R;
      if (range.s.c > C) range.s.c = C;
      if (range.e.r < R) range.e.r = R;
      if (range.e.c < C) range.e.c = C;
      var cell = {
        v: data[R][C]
      };
      if (cell.v == null) continue;
      var cell_ref = XLSX.utils.encode_cell({
        c: C,
        r: R
      });

      if (typeof cell.v === 'number') cell.t = 'n';
      else if (typeof cell.v === 'boolean') cell.t = 'b';
      else if (cell.v instanceof Date) {
        cell.t = 'n';
        cell.z = XLSX.SSF._table[14];
        cell.v = datenum(cell.v);
      } else cell.t = 's';

      ws[cell_ref] = cell;
    }
  }
  if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
  return ws;
}

function Workbook() {
  if (!(this instanceof Workbook)) return new Workbook();
  this.SheetNames = [];
  this.Sheets = {};
}

function s2ab(s) {
  var buf = new ArrayBuffer(s.length);
  var view = new Uint8Array(buf);
  for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
  return buf;
}

export function export_table_to_excel(id) {
  var theTable = document.getElementById(id);
  var oo = generateArray(theTable);
  var ranges = oo[1];

  /* original data */
  var data = oo[0];
  var ws_name = "SheetJS";

  var wb = new Workbook(),
    ws = sheet_from_array_of_arrays(data);

  /* add ranges to worksheet */
  // ws['!cols'] = ['apple', 'banan'];
  ws['!merges'] = ranges;

  /* add worksheet to workbook */
  wb.SheetNames.push(ws_name);
  wb.Sheets[ws_name] = ws;

  var wbout = XLSX.write(wb, {
    bookType: 'xlsx',
    bookSST: false,
    type: 'binary'
  });

  saveAs(new Blob([s2ab(wbout)], {
    type: "application/octet-stream"
  }), "test.xlsx")
}

export function export_json_to_excel({
  multiHeader = [],
  header,
  data,
  filename,
  merges = [],
  autoWidth = true,
  bookType = 'xlsx'
} = {}) {
  /* original data */
  filename = filename || 'excel-list'
  data = [...data]
  data.unshift(header);

  for (let i = multiHeader.length - 1; i > -1; i--) {
    data.unshift(multiHeader[i])
  }

  var ws_name = "SheetJS";
  var wb = new Workbook(),
    ws = sheet_from_array_of_arrays(data);

  if (merges.length > 0) {
    if (!ws['!merges']) ws['!merges'] = [];
    merges.forEach(item => {
      ws['!merges'].push(XLSX.utils.decode_range(item))
    })
  }

  if (autoWidth) {
    /*设置worksheet每列的最大宽度*/
    const colWidth = data.map(row => row.map(val => {
      /*先判断是否为null/undefined*/
      if (val == null) {
        return {
          'wch': 10
        };
      }
      /*再判断是否为中文*/
      else if (val.toString().charCodeAt(0) > 255) {
        return {
          'wch': val.toString().length * 2
        };
      } else {
        return {
          'wch': val.toString().length
        };
      }
    }))
    /*以第一行为初始值*/
    let result = colWidth[0];
    for (let i = 1; i < colWidth.length; i++) {
      for (let j = 0; j < colWidth[i].length; j++) {
        if (result[j]['wch'] < colWidth[i][j]['wch']) {
          result[j]['wch'] = colWidth[i][j]['wch'];
        }
      }
    }
    ws['!cols'] = result;
  }

  /* add worksheet to workbook */
  wb.SheetNames.push(ws_name);
  wb.Sheets[ws_name] = ws;

  var wbout = XLSX.write(wb, {
    bookType: bookType,
    bookSST: false,
    type: 'binary'
  });
  saveAs(new Blob([s2ab(wbout)], {
    type: "application/octet-stream"
  }), `${filename}.${bookType}`);
}

因为数据中的key是英文,想要导出的表头是中文的话,需要将中文和英文做对应

const headers = {
        '入职日期': 'create_time',
        '手机号': 'mobile',
        '用户名': 'username',
        '角色': 'role_name',
        '邮箱': 'email',
        '部门': 'department_name'
      }

完成导出代码

import { formatDate } from '@/filters'
// 导出数据
    exportData() {
      const headers = {
        '入职日期': 'create_time',
        '手机号': 'mobile',
        '用户名': 'username',
        '角色': 'role_name',
        '邮箱': 'email',
        '部门': 'department_name'
      }
      import('@/vendor/Export2Excel').then(async excel => {
        const res = await getUserList({ query: '', pagenum: 1, pagesize: this.page.total })
        // console.log(res)
        const data = this.formatJson(headers, res.users)
        console.log(data)
        excel.export_json_to_excel({
          header: Object.keys(headers),
          data,
          filename: '用户信息表',
          autoWidth: true,
          bookType: 'xlsx'

        })
      })
    }    
    }

导出时间格式的处理

// 该方法负责将数组转化成二维数组
    formatJson(headers, rows) {
      return rows.map(item => {
        return Object.keys(headers).map(key => {
          if (headers[key] === 'create_time') {
            return formatDate(item[headers[key]]) // formatDate 函数是定义好的一个过滤器
          }
          return item[headers[key]]
        })
      })

过滤器 formatDate

import moment from 'moment'
export function formatTime(value) {
  return moment(value * 1000).format('YYYY-MM-DD HH:mm:ss')
}
export function formatDate(value) {
  return moment(value * 1000).format('YYYY-MM-DD')
}

到此这篇关于vue-element-admin项目导入和导出的实现的文章就介绍到这了,更多相关vue-element-admin项目导入导出内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Vue.js 相关文章推荐
Vue项目利用axios请求接口下载excel
Nov 17 Vue.js
深入了解Vue3模板编译原理
Nov 19 Vue.js
Vue解决移动端弹窗滚动穿透问题
Dec 15 Vue.js
vue中activated的用法
Jan 03 Vue.js
vue使用lodop打印控件实现浏览器兼容打印的方法
Feb 07 Vue.js
详解vue3中组件的非兼容变更
Mar 03 Vue.js
详细聊聊vue中组件的props属性
Nov 02 Vue.js
如何用vue实现网页截图你知道吗
Nov 17 Vue.js
vue中使用mockjs配置和使用方式
Apr 06 Vue.js
使用vuex-persistedstate本地存储vuex
Apr 29 Vue.js
vue css 相对路径导入问题级踩坑记录
Jun 05 Vue.js
vue2实现provide inject传递响应式
May 21 #Vue.js
vue使用节流函数的踩坑实例指南
vue实现同时设置多个倒计时
May 20 #Vue.js
Vue和Flask通信的实现
Vue Element UI自定义描述列表组件
使用这 6个Vue加载动画库来减少我们网站的跳出率
一文带你理解vue创建一个后台管理系统流程(Vue+Element)
You might like
网站当前的在线人数
2006/10/09 PHP
php后台程序与Javascript的两种交互方式
2009/10/25 PHP
sphinx增量索引的一个问题
2011/06/14 PHP
PHP动态分页函数,PHP开发分页必备啦
2011/11/07 PHP
PHP贪婪算法解决0-1背包问题实例分析
2015/03/23 PHP
Laravel 5+ .env环境配置文件详解
2020/04/06 PHP
one.php 多项目、函数库、类库 统一为一个版本的方法
2020/08/24 PHP
jquery学习笔记 用jquery实现无刷新登录
2011/08/08 Javascript
js校验表单后提交表单的三种方法总结
2014/02/28 Javascript
node.js中的fs.open方法使用说明
2014/12/17 Javascript
Javascript节点关系实例分析
2015/05/15 Javascript
Angular Js文件上传之form-data
2015/08/28 Javascript
深入浅析JavaScript中prototype和proto的关系
2015/11/15 Javascript
JS提示:Uncaught SyntaxError:Unexpected token ) 错误的解决方法
2016/08/19 Javascript
详解Vue.use自定义自己的全局组件
2017/06/14 Javascript
vue2.0 常用的 UI 库实例讲解
2017/12/12 Javascript
详解使用Nuxt.js快速搭建服务端渲染(SSR)应用
2019/03/13 Javascript
判断JavaScript中的两个变量是否相等的操作符
2019/12/21 Javascript
原生js实现瀑布流效果
2020/03/09 Javascript
将Vue组件库更换为按需加载的方法步骤
2020/05/06 Javascript
在vant 中使用cell组件 定义图标该图片和位置操作
2020/11/02 Javascript
[01:30:15]DOTA2-DPC中国联赛 正赛 Ehome vs Aster BO3 第二场 2月2日
2021/03/11 DOTA
Python单元测试框架unittest使用方法讲解
2015/04/13 Python
在SAE上部署Python的Django框架的一些问题汇总
2015/05/30 Python
Django 限制访问频率的思路详解
2019/12/24 Python
解决Python3.8用pip安装turtle-0.0.2出现错误问题
2020/02/11 Python
python3连接MySQL8.0的两种方式
2020/02/17 Python
纯css3实现图片翻牌特效
2015/03/10 HTML / CSS
世界上最大的隐形眼镜商店:1-800 Contacts
2018/11/03 全球购物
房地产营销策划方案
2014/02/08 职场文书
工商企业管理应届生求职信
2014/05/04 职场文书
网上祭英烈活动总结
2015/02/04 职场文书
告知书格式
2015/07/01 职场文书
Python实现照片卡通化
2021/12/06 Python
基于Python编写简易版的天天跑酷游戏的示例代码
2022/03/23 Python
Sql Server 行数据的某列值想作为字段列显示的方法
2022/04/20 SQL Server