uiku

78 阅读6分钟

echarts 图表无数据/空数据 展示“暂无数据”

<div id="test_chart" style="width: 600px;height:400px;"></div>
 
   //图表
    echartsData() {
      if (this.chart != null && this.chart != "" && this.chart != undefined) {
        this.chart.clear();
        this.chart.dispose();
      }
      // if (this.chart) {
      //   this.chart.clear();
      //   this.chart.dispose();
      // }
      const myChartSteam = this.$refs.myChartSteam;
      if (myChartSteam) {
        let ydata = this.eyList;
        let xdata = this.exList;
        this.chart = this.$echarts.init(myChartSteam);
        let option;
        if (xdata.length == 0) {
          option = {
            title: {
              text: "暂无数据",
              x: "center",
              y: "center",
              textStyle: {
                fontSize: 14,
                fontWeight: "normal",
              },
            },
          };
        } else {
          option = {
            tooltip: {
              trigger: "axis",
              axisPointer: {
                type: "shadow",
              },
            },
            xAxis: {
              type: "category",
              data: xdata,
              axisTick: {
                show: false,
              },
              axisLabel: {
                showMinLabel: true,
                showMaxLabel: true,
                width: 100,
                overflow: "break",
              },
            },
            yAxis: {
              name: this.active == 1 ? "(t)" : this.active == 2 ? "(元)" : "",
              type: "value",
              axisLabel: {
                formatter: (value) => {
                  if (value >= 10000) {
                    value = value / 10000 + "W";
                  }
                  return value;
                },
              },
            },
            grid: {
              left: 45,
              right: 45,
            },
            series: [
              {
                name:
                  this.active == 1
                    ? "蒸汽用量"
                    : this.active == 2
                    ? "蒸汽费用"
                    : "瞬时流量",
                data: ydata,
                type: "line",
                color: this.active == 0 ? "#51C41D" : "#FFD289",
              },
            ],
          };
        }

        this.chart.setOption(option);
        window.addEventListener("resize", function () {
          if (this.chart) {
            this.chart.resize();
          }
        });
      }
    },

charts统计图Y轴(或X轴)文字过长问题解决

axisLabel: {//超出指定数字省略号
  color: "#000",
  interval: 0,
  formatter: function(value) {
    if (value.length > 12) {
      return value.substring(0, 12) + "...";
    } else {
      return value;
    }
  }
},
     yAxis: {
        type: 'value',
        axisLabel:{
            formatter:(value) => {
                if(value >= 10000){
                    value = (value / 10000) + 'W';
                }
                if(value >= 1000){
                    value = (value / 1000) + 'K';
                }
                return value;
            }
        }

formatter属性可供用户自定义一些属性,很方便使用。

Echarts折线图配置项

      chartsOption: {
        tooltip: {
          trigger: "axis",
          backgroundColor: "rgba(22,42,209,1)",
          borderColor: "#28a6d8",
          textStyle: {
            color: "#fff",
            align: "left"
          },
          formatter: params => {
            let { value, axisValue } = params[0];
            let html = `<span>数值:${value.toFixed(2)}</span><br/>
            <span>时间:${axisValue}</span>`;
            return html;
          }
        },
        grid: {
          top: 10,
          left: 50,
          right: 40,
          bottom: 10,
          containLabel: true
        },
        xAxis: {
          type: "category",
          boundaryGap: false,
          data: [],
          //x轴
          axisLine: {
            show: true,
            lineStyle: {
              color: "#3d5bff"
            }
          },
          //x轴上刻度文字
          axisLabel: {
            textStyle: {
              color: "#8ea0ff",
              fontSize: 16
            },
            //显示间隔
            interval: (index, value) => {
              return this.intervals[index].count;
            },
            formatter: function(value, index) {
              return value.split(" ")[0];
            }
            //rotate: 30
          }
        },
        yAxis: {
          type: "value",
          //y轴
          axisLine: {
            show: true,
            lineStyle: {
              color: "#3d5bff"
            }
          },
          //y轴上刻度文字
          axisLabel: {
            textStyle: {
              color: "#8ea0ff",
              fontSize: 16
            }
          },
          //横向网格线(诶嘿,想不到吧,横向网格线样式的设置居然是在yAxis中呢O(∩_∩)O)
          splitLine: {
            lineStyle: {
              color: ["#0a2395"]
            }
          }
        },
        series: [
          {
            type: "line",
            smooth: true,
            //折线图拐点使用自定义图标
            symbol: () => {
              let circleIcon = require("@/assets/images/peiqi.png");
              return `image://${circleIcon}`;
            },
            symbolSize: 20, //折线图拐点大小
            itemStyle: {
              normal: {
                lineStyle: {
                  width: 2,
                  type: "dashed"
                }
              }
            },
            data: []
          }
        ],
        color: [], //折线颜色
        //缩放及滚动条
        dataZoom: [
          {
            type: "inside",
            xAxisIndex: [0, 1],
            start: 0,
            end: 100
          },
          {
            show: false,
            xAxisIndex: [0, 1],
            type: "slider",
            top: "75%",
            start: 0,
            end: 100
          }
        ]
      }

Vue-Awesome1-Swiper基本能解决你所有的轮播需求

在我们使用的很多ui库(vant、antiUi、elementUi等)中,都有轮播组件,对于普通的轮播效果足够了。但是,某些时候,我们的轮播效果可能比较炫,这时候ui库中的轮播可能就有些力不从心了。当然,如果技术和时间上都还可以的话,可以自己造个比较炫的轮子。

这里我说一下vue-awesome-swiper这个轮播组件,真的非常强大,基本可以满足我们的轮播需求。swiper相信很多人都用过,很好用,也很方便我们二次开发,定制我们需要的轮播效果。vue-awesome-swiper组件实质上基于swiper的,或者说就是能在vue中跑的swiper。下面说下怎么使用:

  • 安装 cnpm install vue-awesome-swiper --save
  • 在组件中使用的方法,全局使用意义不大:
// 引入组件
import 'swiper/dist/css/swiper.css' 
import { swiper, swiperSlide } from 'vue-awesome-swiper'
​
// 在components中注册组件
components: {
    swiper,
    swiperSlide
}
​
// template中使用轮播
// ref是当前轮播
// callback是回调
// 更多参数用法,请参考文档
<swiper :options="swiperOption" ref="mySwiper" @someSwiperEvent="callback">            
    <!-- slides -->            
    <swiper-slide><div class="item">1</div></swiper-slide>            
    <swiper-slide><div class="item">2</div></swiper-slide>            
    <swiper-slide><div class="item">3</div></swiper-slide>            
          
    <!-- Optional controls -->            
    <div class="swiper-pagination"  slot="pagination"></div>            
    <div class="swiper-button-prev" slot="button-prev"></div>            
    <div class="swiper-button-next" slot="button-next"></div>            
    <div class="swiper-scrollbar"   slot="scrollbar"></div>
</swiper>
复制代码
// 参数要写在data中
data() {            
    return {     
        // swiper轮播的参数           
        swiperOption: { 
            // 滚动条                   
            scrollbar: {                        
                el: '.swiper-scrollbar',                    
            }, 
            // 上一张,下一张                   
            navigation: {                        
                nextEl: '.swiper-button-next',                        
                prevEl: '.swiper-button-prev',                    
            },
            // 其他参数…………   
        }            
    }                    
},复制代码

swiper需要配置哪些功能需求,自己根据文档进行增加或者删减。附上文档:npm文档swiper3.0/4.0文档,更多用法,请参考文档说明。

ant design vue 表格自动滚动

//a-table标签添加class="tableRect"
<a-table class="tableRect"/>

<script>
  data() {
    return {
      scrollTimer: null, // 滚动定时器
      scrollDirection: 'down', // 滚动方向 up向上 down向下
  },
  created() {
    //this.scrollFun();
  },
  methods: {
	scrollFun() {
      // 如果定时器存在
      if (this.scrollTimer) {
        // 则先清除
        clearInterval(this.scrollTimer)
        this.scrollTimer = null
      }
      this.scrollTimer = setInterval(() => {
        const scrollHeight = document.querySelectorAll(`.tableRect .ant-table-body`)[0].scrollHeight
        const clientHeight = document.querySelectorAll(`.tableRect .ant-table-body`)[0].clientHeight
        const scroll = scrollHeight - clientHeight
        // 获取当前滚动条距离顶部高度		tableRect是a-table标签名
        const scrollTop = document.querySelectorAll(`.tableRect .ant-table-body`)[0].scrollTop
        console.log('scrollTop',scrollTop);
        // 向下滚动
        if (this.scrollDirection === 'down') {
          // 滚动速度
          const temp = scrollTop + 10
          document.querySelectorAll(`.tableRect .ant-table-body`)[0].scrollTop = temp // 滚动
          // 距离顶部高度  大于等于 滚动长度
          if (scroll <= temp) {
            // 滚动到底部 停止定时器
            clearInterval(this.scrollTimer)
            this.scrollTimer = null
          }
        }
      }, 150)
    },
   }
</script>

//在a-table标签外添加一个div标签
<div v-on:mouseover="scrollFun"
     v-on:mouseout="pauseScroll">
     <a-table/>
</div>

<script>
  methods: {
	pauseScroll() {
      // 定时器不为空
      if (this.scrollTimer) {
        // 清除定时器
        clearInterval(this.scrollTimer)
        this.scrollTimer = null
      }
    }
  }
</script>

elementui

element固定列+固定高度后,滚动到底部错位

项目中自定义了滚动条样式,设置的宽高不一样。
解决方法:将宽高设置成一样

::-webkit-scrollbar{
  width: 13px;
  height: 13px;
  background-color: #01064b;
}

参考文档:blog.csdn.net/a1983029606…

同一组件上存在多个table进行tabs和v-if/v-show切换时,多表格的数据会相互混淆,串在一起,引发bug

为每个table指定对应且唯一的key属性。

其他一些类似的问题也可以尝试为其添加key属性来解决

vue element 多个 Form 表单同时验证

<template>
<el-form   ref="form1"></el-form>
<el-form   ref="form2"></el-form>
<el-form   ref="form3"></el-form>
</template>
<script>
export default{
    methods: {
      onValidate() { // 保存操作
      const formArr =['form1', 'form2','form3']//三个form表单的ref
      const resultArr = [] //用来接受返回结果的数组
      let _self = this
      function checkForm(formName) { //封装验证表单的函数
        let result = new Promise(function (resolve, reject) {
          _self.$refs[formName].validate((valid) => {
            if (valid) {
              resolve();
            } else { reject() }
          })
        })
        resultArr.push(result) // 得到promise的结果
      }
      formArr.forEach(item => { // 根据表单的ref校验
        checkForm(item)
      })
      Promise.all(resultArr).then(values => {
         // 此时必填完成,做保存后的业务操作
        //  ...
        console.log('success');
      }).catch(_ => {
        console.log('err')
      })
    },
    }
}
</script>

巧用$options

$options是一个记录当前Vue组件的初始化属性选项,当我们想把 data 里的某个值重置为初始值时,非常有用

例如:

this.value = this.$options.data().value;

dialog 里重置表单

利用上面介绍的 $options 特性

我们经常的业务场景是这样:一个el-dialog中有一个el-form,而且我们通常是新增和编辑复用同一个组件,现在我们要求每次打开el-dialog时都要重置el-form里的数据,并且清除校验状态。

  // 弹框打开时
initForm(){
  this.$refs['form'] && this.$refs['form'].resetFields()
  this.form = this.$options.data.call(this).form;

Element UI 下拉框和级联选择器位置错乱问题

  • 对于el-select来说添加 :popper-append-to-body = “false” 即可
  • 对于el-cascader来说添加 :append-to-body=“false” 即可

v-loading与html2canvas同时使用时导致loading效果总是不能及时出现

问题描述:众所周知,html2canvas可以实现对页面内容进行截图,但是遇到稍微复杂一点的页面结构时会比较耗时,为了提升用户体验,我们希望在html2canvas执行前显示Loading,执行结束之后Loading消失。但是,在实际使用时,发现html2canvas会阻塞进程,导致loading无法在我们希望的时机出现和消失。
解决问题:先让loading出现,然后将html2canvas的逻辑处理放在setTimeout中执行,然后在html2canvas的成功回调中让loading消失,这样可以保证loading在我们需要时正确显隐。

el-form的resetField()方法无法清空表单内容

解决方法:保证表单项el-form-item中有prop属性且prop值与表单数据源的key一致

image.png

el-dialog嵌套导致无渲染

问题描述:项目中利用el-dialog封装了自定义的进度条组件,当时的业务场景是,某个地方的对话框加载时需要复杂且耗时的逻辑处理,所以需要在对话框出现的同时显示进度条,我就在对话框组件内引用了进度条组件,这样就就无形中产生了el-dialog嵌套的问题,导致进度条组件始终无法渲染。
解决问题:在对话框组件和自定义的进度条组件需要同时使用时,使其并列而非嵌套

el-form的表单验证方法validate()总是不进回调

解决方法:如果有自定义校验规则,必须保证确保所有出口(即每种条件)都有callback

image.png

el-tree主题样式复写之后通过小三角展开节点并离开后背景色变白色问题

.el-tree .el-tree-node:focus > .el-tree-node__content{ background-color:blue; }

el-input 限制输入框只能输入数字

    <el-input v-model.number="num"  onkeyup="this.value = this.value.replace(/[^\d.]/g,'');"></el-input>

el-input 过滤特殊字符或身份证脱敏

v-model拆分为:value和@input

<el-input :value="input" @input='e => input = idCardValid (e)' placeholder="请输入内容"></el-input>

  methods:{
    idCardValid(val){
      const idCard= val.replace(/^(\d{6})\d+(\d{4})$/, "$1******$2")
      console.log(idCard)
      return idCard
    } 
},

修改走马灯指示器样式

::v-deep .el-carousel__indicators--horizontal {
        position: absolute;
        left: auto;
        // right: 10px;
        // bottom: 10px;
        // overflow: hidden;
        left: 50%;
        top: 96%;
        text-align: right;
 
        .el-carousel__indicator--horizontal button {
            width: 8px;
            height: 8px;
            // background: #962a2a;
            background: #ffffff;
​
            border-radius: 50%;
            opacity: 0.5;
        }
 
        .el-carousel__indicator--horizontal.is-active button {
            width: 24px;
            height: 8px;
            // background: #38429b;
            background: #ffffff;
            opacity: 0.5;
            border-radius: 10px;
        }
    }

el-carousel走马灯 修改指示器样式、高度随着图片大小而更改

@load="imgLoad"`  `第一次加载页面跟踪图片高度
<el-carousel :height="imgHeight">
    <el-carousel-item v-for="item in imgList" :key="item">
        <img ref="imgHeight" :src="item.src" @load="imgLoad">
    </el-carousel-item>
</el-carousel>
data: {
    imgHeight: '',
},
mounted() {
    this.imgLoad()
    window.onresize = () => {
        this.imgLoad()
    }
},
methods:{
  imgLoad(){
    this.$nextTick(()=>{
      this.imgHeight = this.$refs.imgHeight['0'].height +'px'
    })
   }
 
}    
destroyed() {
     window.onresize = null;
}

微信截图_20220706160144.png

设置样式的时候,一定要添加scoped
<style lang="less" scoped>
    /deep/.el-carousel__indicators--horizontal {
        position: absolute;
        left: auto;
        right: 10px;
        bottom: 10px;
        text-align: right;
 
        .el-carousel__indicator--horizontal button {
            width: 8px;
            height: 8px;
            background: #ffffff;
            border-radius: 50%;
            opacity: 0.5;
        }
 
        .el-carousel__indicator--horizontal.is-active button {
            width: 24px;
            height: 8px;
            background: #ffffff;
            opacity: 0.5;
            border-radius: 10px;
        }
    }
</style>

走马灯轮播图,el-carousel左右箭头位置调整

  <template>
  <section class="excellentCases">
    <p class="title">箭头轮播图</p>
    <div class="baseContent"> 
      <div class="prev">
        <img src="~/assets/images/solution/wisdomGov/prev.png"  @click="arrowClick('prev')" />
      </div>
      <div class="carousel">
        <el-carousel  :autoplay="false" arrow="never" indicator-position="outside" ref="cardShow" >
          <el-carousel-item v-for="(item, index) in 4" :key="index">
            <h3>{
   { item }}</h3>
          </el-carousel-item>
        </el-carousel>
      </div>
      <div class="next">
        <img src="~/assets/images/solution/wisdomGov/next.png" @click="arrowClick('next')"/>
      </div>
    </div>
  </section>
</template>
<script>
export default {
  data() {
    return {};
  },
  created() {},
  mounted() {},
  methods: {
    arrowClick(val) {
      if(val === 'next') {
        this.$refs.cardShow.next()
      } else {
        this.$refs.cardShow.prev()
      }
    },
  },
};
</script>
<style scoped lang="scss">
.excellentCases {
    max-width: 1426px;
    height: 530px;
    margin: 0 auto;
    background: #323233;
  .title{
    font-size: 34px;
    font-weight: 500;
    line-height: 34px;
    color: #FFFFFF;
    text-align: center;
    margin: 56px 0 27px 0;
  }
  .baseContent{
    position: relative;
    .prev{
      position: absolute;
      left: 46px;
      top:34%;
      img{
        width: 75px;
        height: 74px;
      }
    }
    .next{
      position: absolute;
      right: 46px;
      top:34%;
      img{
        width: 75px;
        height: 74px;
      }
    }
    .carousel{
      width: 1110px;
      // height: 347px;
      margin: 0 auto;
      background-color: #cccc;
      /deep/.el-carousel__container{
        height: 347px;
      }
      /deep/ .el-carousel__indicators--outside{
        background-color: #323233;;
      }
    }
  }
}
@media screen and (max-width: 992px) {
}
</style>

搜索框重置,没有清除问题

```
      <el-form-item label="签约人" prop="signatories">
            <el-input v-model="queryParams.signatories" placeholder="签约人" clearable size="small"
              @keyup.enter.native="handleQuery" style="width:240px" />
          </el-form-item>
复制代码
```

是因为 prop="signatories" 没有设置或者设置与对应的输入框不一样导致

弹窗必填提示不显示

```
<el-form-item label="撤回原因" prop="reason">
                    <el-input v-model="withdrawalForm.reason"  type="textarea"  rows="5"  maxlength="200"
                      show-word-limit/>
                  </el-form-item>
复制代码
```

一可能是没有设置 prop="reason",二是设置与输入框不一致,三对应的如rules规则没有设置对应的值

弹窗数据没有重新加载问题

```
<!-- 添加或修改区域政策对话框 :fullscreen='true' -->
    <el-dialog
      :title="title"
      :visible.sync="open"
      v-if="open"
      width="1250px"
      append-to-body
      custom-class="chy-dialog"
    >
复制代码
```

对话框增加一个 v-if="open" 即可
  1. 数据深度拷贝方法

    参考链接:www.cnblogs.com/juneling/p/…

el-table篇

1.el-table表格固定列之后下方边缘出现白线

解决方案:

.el-table__fixed-right::before, .el-table__fixed::before {
  background-color: transparent;
}
复制代码

参考文档:blog.csdn.net/DDD4V/artic…

2.el-table表格XY方向都滚动时右下角出现的白色小方块

::-webkit-scrollbar-corner{
  background: transparent;
}
复制代码

vue+elementUI在输入框中按回车键会刷新页面

当一个 form 元素中只有一个输入框时,在该输入框中按下回车应提交该表单。如果希望阻止这一默认行为,可以在 <el-form> 标签上添加 @submit.native.prevent。

<templat>
<el-form   @submit.native.prevent >< /el-form >
</templat>

el-select 下拉框样式修改

使用样式穿透修改下拉框样式,你会发现打死都不生效,那是因为下拉框是默认挂载在 body 下面。解决办法:设置 :popper-append-to-body="false",然后再用样式穿透

element-ui select组件change事件传递多个参数的方法

  1. 方法一
@change="onChange($event,customParam)"

  1. 方法二
@change="((val)=>{changeEvent(val,args)})"  

其他组件的的默认事件同样的方法传递

<el-dropdown trigger="click" @command="((val)=>{handleCommand(val,scope.row)})">
  <span class="el-dropdown-link">
    <i class="el-icon-more el-icon--right"></i>
  </span>
    <el-dropdown-menu slot="dropdown">
        <el-dropdown-item command="volumes">新增</el-dropdown-item>
        <el-dropdown-item command="log">查看</el-dropdown-item>
        <el-dropdown-item command="shell">更新</el-dropdown-item>
        <el-dropdown-item command="container">删除</el-dropdown-item>
    </el-dropdown-menu>
</el-dropdown>

el-input type=number 去除聚焦时的上下箭头

解决

<el-input class="clear-number-input" type="number"></el-input>

<style scoped>
.clear-number-input ::v-deep input[type="number"]::-webkit-outer-spin-button,
.clear-number-input ::v-deep input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none !important;
}
</style>

3.el-table鼠标悬停时高亮背景颜色的修改,添加fixed后固定列高亮颜色不生效

.el-table__body .el-table__row.hover-row td{
  background-color: #00b8ff !important;
}
复制代码

参考文档:blog.csdn.net/zeng092210/…

.el-upload+xlsx实现前端上传并解析excel

安装xlsx依赖:npm install xlsx
引入XLSX: import XLSX from "xlsx";

html中文件上传组件

<el-upload
  action=""
  accept=".xls, .xlsx"
  :on-change="importFile"
  :http-request="uploadRequest"
  :show-file-list="false"
  :multiple="false"
  :limit="1"
  class="uploader">
  <span class="btn">数据录入</span>
</el-upload>
复制代码

js中methods部分

文件上传完成后的处理

importFile(file) {
  this.file2Xce(file)
    .then(res => {
      if (res && res.length > 0) {
        // 解析出来的json数据格式如:[{sheetName: sheet1, sheet: sheetData }]
        //默认只处理第一个页签的数据
        if (res[0] && res[0].sheet && res[0].sheet.length) {
          let datas = res[0].sheet;
          this.tableData = this.handleXlsData(datas, this.tableCols);
        }
      }
    })
    .catch(() => {
      this.loading = false;
    });
},
复制代码

解析excel文件的核心代码

/**
 * 解析excel文件核心代码
 * @param {Object} file
 */
file2Xce(file) {
  return new Promise(function(resolve, reject) {
    const reader = new FileReader();
    reader.onload = function(e) {
      const data = e.target.result;
      this.wb = XLSX.read(data, {
        type: "binary"
      });
      const result = [];
      this.wb.SheetNames.forEach(sheetName => {
        result.push({
          sheetName: sheetName,
          sheet: XLSX.utils.sheet_to_json(this.wb.Sheets[sheetName])
        });
      });
      resolve(result);
    };
    reader.readAsBinaryString(file.raw);
  });
},
复制代码

将直接解析出来的excel文件内容处理成适合自己表格使用的数据格式

/**
 * 解析excel文件数据为表格数据
 * @param {Array} data
 * @param {Array} tableCols
 */
handleXlsData(data,tableCols) {
  let result=[];
  //循环遍历每一条数据
  data.forEach(it => {
    let temObj = {};
    //循环遍历每条数据的每个属性,并对其进行转化
    for (let k in it) {
      if (k.includes('日期')) { 
        it[k]=this.formatDate(it[k],'-')
      }
      let colName = tableCols.filter(i => i.label === k)[0].prop;
      temObj[colName] = it[k];
    }
    result.push(temObj)
  })
  return result
},

由于直接解析出来的excel表格数据一般都以表头文字(中文)作为key,比如:

image.png
这不符合我们实际开发中的习惯,所以需要对其进行相应转化处理,变成这种:

image.png

这里我是直接使用了循环遍历生成表格时所使用的表格列配置信息,其实主要就是表格列名和前端渲染时的列字段的对应关系,自己可以灵活配置,我这边大概配置如下:

image.png

另外,在使用xlsx插件来读取excel时,会将2018/10/16这种数据自动装换成48264.12584511这种样子,所以需要自己手动再转换回来(参考:www.cnblogs.com/cazj/p/1094…

/** 
 * @param {Number} numb是传过来的整数数字
 * @param {String} format是之间间隔的符号
*/
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)
},

备注:vue2项目中如果安装完xlsx依赖发现报错 “export ‘default‘ (imported as ‘XLSX‘) was not found in ‘xlsx‘,有可能是版本不兼容问题,可以通过npm install xlsx@XXX.XXX --save安装特定版本的插件来解决。建议版本:"xlsx": "^0.16.0"

扩展1:纯前端实现下载当前表格为excel

前置条件:安装插件 npm install --save xlsx file-saver

import XLSX from 'xlsx'
import FileSaver from 'file-saver'
exportExcel() {
    var wb = XLSX.utils.table_to_book(
        document.querySelector("#tableId")
    );
    var wbout = XLSX.write(wb, {
        bookType: "xlsx",
        bookSST: true,
        type: "array"
    });
    try {
        FileSaver.saveAs(
            new Blob([wbout], { type: "application/octet-stream" }),
            this.title+".xlsx"
        );
    } catch (e) {
        if (typeof console !== "undefined") console.log(e, wbout);
    }
    return wbout;
}

参考文档:www.jianshu.com/p/a65122b9d…

注意,如果表格中有固定列,会导致生成重复表格数据
解决思路:先移除固定列Dom,然后再追加
实现代码如下:

exportExcel() {
      // 解决生成重复数据-因为使用fixed属性 注意你的fixed是left还是right
      var fix = document.querySelector("#daochu2 .el-table__fixed-right");
      var wb;
      // 判断要导出的节点中是否有fixed的表格,如果有,转换excel时先将该dom移除,然后append回去
      if (fix) {
        /* 从表生成工作簿对象 */
        wb = XLSX.utils.table_to_book(
          document.querySelector("#table").removeChild(fix),
          { raw: true }
        );
        document.querySelector("#table").appendChild(fix);
      } else {
        wb = XLSX.utils.table_to_book(document.querySelector("#table"), {
          raw: true,
        });
      }
      /* 获取二进制字符串作为输出 */
      var wbout = XLSX.write(wb, {
        bookType: "xlsx",
        bookSST: true,
        type: "array",
      });
      try {
        FileSaver.saveAs(
          new Blob([wbout], { type: "application/octet-stream" }),
          // 设置导出文件名称
          "统计核算.xlsx"
        );
      } catch (e) {
        if (typeof console !== "undefined") console.log(e, wbout);
      }
      return wbout;
    }

参考文档:www.jianshu.com/p/f6a03c734…

扩展2:纯前端实现将json数据转换为excel数据并下载

import XLSX from 'xlsx';
 const export=()=>{
    let head = ['商品','售价','颜色']
    let data = [
                ['裙子','¥500','红色'],
                ['衬衫','¥300','白色'],
                ['裤子','¥200','黑色'],
                ['皮鞋','¥800','黑色'],
               ];
    data = data.map(e => Object.values(e))
    data.unshift(head)
    let filename = "表格名字.xlsx";
    let ws_name = "SheetJS";
    let wb = XLSX.utils.book_new();
    let ws = XLSX.utils.aoa_to_sheet(data);
    XLSX.utils.book_append_sheet(wb, ws, ws_name);
    XLSX.writeFile(wb, filename);
  }

参考文档:cloud.tencent.com/developer/a…