背景:涉及表格需要导入/导出表格(.csv、.excel)
实现:

  • 后端之间提供可供下载的url
  • 前端自主实现导入导出

    表格的几种格式文件

CSV文件的优势

CSV兼容性差异

CSV相关的坑

问题:导出csv出现多余换行

背景:
后端接口获取数据 -> 过滤 + 定义列名称/字段及顺序 -> 借助a标签的download属性进行.csv格式导出

  1. exportCSV(titles, data);

image.png
image.png
image.png
纠正:清除换行符

  1. press_info_name: item.press_info.name.trim() || ' ',

问题: window导出csv一列分割成两列

初步分析: window在对空格(二进制编码为20)解析为分隔符。

尝试:window office开发表格正常

截屏2022-01-13 下午4.07.15.png

❌ 错误操作步骤

  1. 下载csv,自动在记事本中打开, 选择保存。纠正:浏览器设置禁止下载后自动打开。计算机中修改csv默认打开应用为office/wps
  2. office excel中新建数据表, 操作栏:数据 -> 自定义/导入数据 -> 选择csv文件 -> 保存为xlsx文件。

截屏2022-01-13 下午5.00.54的副本.png
纠正:分割符禁止选择【空格】
截屏2022-01-13 下午5.00.46.png

附件源码

借助a标签导出.csv文件

  1. // export.js
  2. /**
  3. * 导出 csv 文件,并直接下载
  4. * 多用于导出当前表格的数据
  5. * 注:导出的对象的字段以 titleOptions 中的字段为准,即不在 titleOptions 中的字段不会导出,便于直接使用表格绑定的对象
  6. * @param titleOptions {Object} 标题行配置,即要输出的属性名显示的标题. e.g { id: 'ID', title: '标题', sku: 'sku' }
  7. * @param data {Array} 导出的具体数据,通常为表格绑定的对象。
  8. * @param fileName {String} 下载文件的名字,默认使用 js 时间戳
  9. */
  10. export function exportCSV(titleOptions = {}, data = [], fileName) {
  11. const fields = Object.keys(titleOptions);
  12. // 校验是否有空数据
  13. if (fields.length === 0 || data.length === 0) {
  14. console.error('参数不能为空'); // for debug
  15. return;
  16. }
  17. // 校验 titleOptions 对象中是否有空
  18. const hasEmptyTitle = !Object.values(titleOptions).every(Boolean);
  19. if (hasEmptyTitle) {
  20. console.error('titleOptions 中不能有空值'); // for debug
  21. return;
  22. }
  23. // 拼接输出内容
  24. const csvTitles = `${fields.map((field) => titleOptions[field]).toString()}\n`;
  25. let csvData = '';
  26. data.forEach((item) => {
  27. csvData += `${fields
  28. .map((field) => {
  29. // 将输出内容中英文逗号的部分替换掉,否则会导致 csv 识别错误
  30. return item[field] ? item[field].toString().replace(/,/g, ';') : '';
  31. })
  32. .toString()}\n`;
  33. });
  34. const universalBOM = '\uFEFF';
  35. const content = universalBOM + csvTitles + csvData;
  36. // 下载文件
  37. const downloadLink = window.document.createElement('a');
  38. downloadLink.setAttribute('href', 'data:text/csv; charset=utf-8,' + encodeURIComponent(content));
  39. downloadLink.setAttribute('download', `${fileName || new Date().getTime()}.csv`);
  40. window.document.body.appendChild(downloadLink);
  41. downloadLink.click();
  42. }

上传csv文件 ExcelUpload.vue

依赖 element 、xlsx
原理
专题|导入导出CSV - 图7

实现

  1. <template>
  2. <!-- 解析 excel 文件的通用组件 -->
  3. <!-- <input type="file" ref="upload" accept=".xls,.xlsx" class="outputlist_upload"> -->
  4. <el-upload
  5. action=""
  6. ref="upload"
  7. :show-file-list="false"
  8. :http-request="readExcel"
  9. :auto-upload="true"
  10. accept=".xlsx"
  11. >
  12. <el-button v-bind="btnConf" :style="btnStyle" >{{ btnText }}</el-button>
  13. </el-upload>
  14. </template>
  15. <script>
  16. import xlsx from 'xlsx';
  17. import { setTimeout } from 'timers';
  18. export default {
  19. components: {},
  20. props: {
  21. btnText: {
  22. required: false,
  23. type: String,
  24. default: '上传 Excel',
  25. },
  26. btnConf: {
  27. required: false,
  28. type: Object,
  29. default: () => ({}),
  30. },
  31. btnStyle:{
  32. required: false,
  33. type: String,
  34. default: '',
  35. },
  36. setTime:{
  37. required: false,
  38. type: Number,
  39. default: 100,
  40. },
  41. handleData: {
  42. required: false,
  43. type: Function,
  44. default: () => {},
  45. }
  46. },
  47. watch: {},
  48. data() {
  49. return {};
  50. },
  51. methods: {
  52. /**
  53. * 异步读取 excel 文件的第一个 sheet 内容,并转换成 json 数组传递给回调函数
  54. * @param {file} file excel 文件
  55. */
  56. readExcel({file}) {
  57. const fileType = file.name.split('.').reverse()[0]
  58. if(fileType!=='xlsx'){
  59. this.$message.error('上传文件格式错误');
  60. return ;
  61. }
  62. this.$emit('uploadStatus');
  63. //保证loding出现,延后解析excel
  64. setTimeout(() => {
  65. let reader = new FileReader();
  66. reader.onload = (e) => {
  67. const wb = xlsx.read(e.target.result, { type: 'binary' });
  68. const jsonContent = xlsx.utils.sheet_to_json(
  69. wb.Sheets[wb.SheetNames[0]]
  70. );
  71. //传递excel文件名
  72. this.$emit('filename',file.name);
  73. this.handleData(jsonContent);
  74. };
  75. reader.readAsBinaryString(file);
  76. },this.setTime)
  77. },
  78. },
  79. mounted() {},
  80. };
  81. </script>

引用

  1. <excel-upload
  2. class="excel-upload"
  3. :btn-conf="excelBtnConf"
  4. :handle-data="uploadExcel"
  5. :set-time="500"
  6. @uploadStatus="uploadStatus"
  7. ></excel-upload>
  8. <el-data :data="tableData" v-loading="tableLoading"></el-data>
  9. uploadExcel(content) {
  10. const fieldsMap = {
  11. 书名: 'book_name',
  12. isbn: 'isbn',
  13. 原价: 'pricing',
  14. };
  15. let ans = [];
  16. for (let i = 0; i < content.length; i++) {
  17. // ans.push()
  18. }
  19. this.tableLoading = false;
  20. if (content.length > 1) {
  21. this.toast(`成功匹配 ${matchedCount} 项任务`, 'success');
  22. } else {
  23. this.toast('匹配失败,请重新确认 excel 中的电子书名');
  24. }
  25. return ans;
  26. }

测试文件1642067998784.csv