开荒项目记录

375 阅读3分钟

1.为避免安装依赖过慢或者丢包务必使用淘宝镜像

npm install -g cnpm --registry=registry.npm.taobao.org

之后就可以愉快地使用cnpm命令啦

2.项目中使用sass时由于版本过高导致的报错

1.先删掉整个node_moudules文件夹

2.删除package.json中sass和sass-loader相关配置项

3.按照下面的要求重新安装sass和loader

node-v 16.11.1及以上版本: npm install sass-loader@10.2.0 node-sass@6.0.1 --save-dev

node-v 16.11.1 以下版本: npm install sass-loader@7.3.1 node-sass@4.14.1 --save-dev

node版本与sass版本对应关系:

image.png

3.大屏项目自适应使用transform(scale)

data(){
    return {
      designUI:{
        width:1920,
        height:1080
      }
    }
  },
  created(){
    this.setScale();
    window.onresize=()=>this.setScale();
  },
  methods: {
    setScale(){
      let ratioX = window.innerWidth / this.designUI.width;
      let ratioY = window.innerHeight / this.designUI.height;
      let style = document.body.style;
      style.transform="scale(" + ratioX + "," + ratioY + ")";
      style.transformOrigin="left top";
      style.backgroundSize="100% 100%";
      style.overflow='hidden';
    }
  }

后续:由于对body进行了缩放,导致elment-ui中与tooltip相关的提示信息都出现了位置偏移,究其原因,在tooltip出现时,是在body内部末尾追加元素,其定位方式其实是相对于body的宽高的,想要使其能够正常显示,就要使body保持正常缩放比例,因此可以将原本加在body上的缩放样式转移到其下一层根元素document.getElementById("app")中,同样达到了自适应的效果,但又不会影响到tooltip气泡位置。注意,此时要将生命周期created改成mounted。

4.使用液晶字体

参考文档:juejin.cn/post/692236…

注意:引入字体文件后如果启动项目报错提示.TTF文件解析失败,则需要安装url-loader
如果安装url-loader后重启项目依然失败,可以检查下是build/webpack.base.conf.js文件中module-rules中是否只匹配解析了ttf,我们只需要把TTF加上就行

5.使用天地图

参考文档:zhuanlan.zhihu.com/p/121701936

注意:请一定保证在页面dom元素挂在完成后(即mounted生命周期中)进行地图初始化,如果非要在created中初始化,请配合$nectTick使用

5.一堆index.vue文件导致断点总是进入错误的位置

参考文档:www.freesion.com/article/446…

6.环境变量

开发环境:config/dev.env.js
生产环境:config/prod.env.js

7.前端配置跨域代理

按照以往的开发经验,前端处理跨域一般是在vue.config.js中通过代理服务器proxy实现,我如法炮制之后发现请求接口总是404(前提:postman测试接口是正常的),把百度翻烂之后终于发现问题所在,老项目是用vue-cli2创建的,配置代理可以在vue.config.js中,但是我的新项目是vue-cli3创建的,要配置跨域代理必须在根路径下的config/index.js中实现,配置项和vue-cli2的方式是一样的,相当于换了个地方而已,也就是说,vue.config.js已经完全没用啦。

vue-cli2老项目,在vue.config.js中配置代理解决跨域:

image.png

vue-cli3新项目,在config/index.js中配置代理解决跨域:

image.png

参考文档:t.zoukankan.com/EnSnail-p-8…

8.js中动态引入并加载图片

你如果天真的以为

<img :src="imgSrc"> this.imgSrc='../images/logo.png' 这样的代码能让你正常渲染图片的话就大错特错了,必须使用require才行 this.imgSrc=require('../images/logo.png')

9.新项目托管到git

参考文档:Git新建仓库首次提交

10.添加闪烁动画

.span{
  -webkit-animation: twinkling 1s infinite ease-in-out;
  animation: twinkling 1s infinite ease-in-out;
}
@-webkit-keyframes twinkling{  
   0%{  
       opacity: 0.5;  
   }  
   100%{  
       opacity: 1;  
   }  
}
@keyframes twinkling{  
   0%{  
       opacity: 0.5;  
   }  
   100%{  
       opacity: 1;  
   }  
}

11.vscode设置保存时自动格式化代码,自动缩进对齐

1.文件–首选项–设置 或者vscode左下角齿轮–设置
2.搜索框搜索emmet.include ,点击在settings.json中编辑
3.在打开的settings.json中添加这两行代码

"editor.formatOnType": true,
"editor.formatOnSave": true,

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

12.好用的vscode扩展程序

juejin.cn/post/705533…

13.将后台返回的文件流格式的图片进行转化并显示

    getImg() {
      axios
        .get(url, {
          responseType: "arraybuffer"
        })
        .then(res => {
          return (
            "data:image/png;base64," +
            btoa(
              new Uint8Array(res.data).reduce(
                (data, byte) => data + String.fromCharCode(byte),
                ""
              )
            )
          );
        })
        .then(data => {
          this.imgSrc = data;
        })
        .catch(ex => {
          console.error(ex);
        });
    }

14.下载后台返回的文件流格式Excel

export function exportExcel() {
  return request({
    url: `/dna/watersampledata/export`,
    method: "get",
    responseType: "blob"
  })
}
clickExport() {
  exportExcel().then(res => {
    const blob = res.data;
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    reader.onload = e => {
      const a = document.createElement("a");
      a.download = `数据.xls`;
      a.href = e.target.result;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    };
  });
}

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

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

16.记录一次犯蠢

平时自己用ES6对象简写用的很爽,但是当用到一些组件库时看到对象简写经常会反应不过来,以为是什么不可随意改变的高级用法,这不,使用天地图创建地图标注是乍一看就被唬到了,小小犯蠢

image.png

当时想要创建多个自定义标注图片,类似icon icon1 icon2这种的,然后在创建Marker时根据条件使用不同图片,就发现只有叫icon的能被渲染,其他都渲染失败,当时还在纳闷,明明是自己定义的变量,为啥改个名字就不能用了呢?后来检查代码才突然灵光乍现,麻蛋,识别不了除icon之外的名字因为天地图内置方法new Marker中规定了必须以icon为key,{icon}这种方式其实是触发了ES6的对象简写,完整写法应该是{icon:icon},即{icon:icon1},而后面的icon1才是我们在前文自定义的变量名字,随便怎么改都行

总结:当时用框架或者库时,看到一些貌似高级的语法千万不要被唬住了

17.关于Content-Type

Content-Type分类

application/x-www-form-urlencoded HTTP会将请求参数用key1=val1&key2=val2的方式进行组织,并放到请求实体里面,注意如果是中文或特殊字符如"/"、","、“:" 等会自动进行URL转码。不支持文件,一般用于表单提交(传参显示在Form Data中)。
multipart/form-data与application/x-www-form-urlencoded不同,这是一个多部分多媒体类型。首先生成了一个 boundary 用于分割不同的字段,在请求实体里每个参数以------boundary开始,然后是附加信息和参数名,然后是空行,最后是参数内容。多个参数将会有多个boundary块。如果参数是文件会有特别的文件域。最后以------boundary–为结束标识。multipart/form-data支持文件上传的格式,一般需要上传文件的表单则用该类型(传参显示在payload中)。
application/jsonJSON 是一种轻量级的数据格式,以“键-值”对的方式组织的数据。这个使用这个类型,需要参数本身就是json格式的数据,参数会被直接放到请求实体里,不进行任何处理。服务端/客户端会按json格式解析数据(传参显示在payload中)。 application/xml 和 text/xml与application/json类似,这里用的是xml格式的数据,text/xml的话,将忽略xml数据里的编码格式

Content-Type使用

request 的Content-Type

一般我们在开发的过程中需要注意客户端发送请求(Request)时的Content-Type设置,特别是使用ajax的时候,如果设置得不准确,很有可能导致请求失败。比如在spring中,如果接口使用了@RequestBody,spring强大的自动解析功能,会将请求实体的内容自动转换为Bean,但前提是请求的Content-Type必须设置为application/json,否正就会返回415错误。(注:415 错误是 Unsupported media type,即不支持的媒体类型。)
建议:如果是一个restful接口(json格式),一般将Content-Type设置为application/json; charset=UTF-8; 如果是文件上传,一般Content-Type设置为multipart/form-data 如果普通表单提交,一般Content-Type设置为application/x-www-form-urlencoded

response的Content-Type

服务端响应(Response)的Content-Type最好也保持准确,虽然一般web开发中,前端解析响应的数据不会根据Content-Type,并且服务端一般能自动设置准确的Content-Type,但是如果乱设置某些情况下可能会有问题,比如导出文件,打开图片等。如果在spring项目里使用@ResponseBody,spring会将响应的Content-Type设置为application/json;charset=UTF-8;,可能会导致文件无法导出,需要注意下。

response的Content-Type设置建议:

一般情况下不需要显示设置; 如果是文件导出,Content-Type 设置为 multipart/form-data,并且添加一个Content-Disposition设置为attachment;fileName=文件.后缀。 注:Content-Disposition是Content-Type的扩展,告诉浏览器弹窗下载框,而不是直接在浏览器里展示文件。因为一般浏览器对于它能够处理的文件类型,如txt,pdf 等,它都是直接打开展示,而不是弹窗下载框。

HTTP请求中 request payload 和 formData  区别?

FormData和Payload是浏览器传输给接口的两种格式,这两种方式浏览器是通过Content-Type来进行区分的(了解Content-Type),如果是 application/x-www-form-urlencoded的话,则为formdata方式,如果是application/json或multipart/form-data的话,则为 request payload的方式。 参考:www.cnblogs.com/kaibindirve…

18.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…

19.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
          }
        ]
      }

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

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

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

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

21.Git不识别文件大小写

git默认配置为忽略大小写,因此无法正确检测大小写的更改
解决方法:运行git config core.ignorecase false,关闭git忽略大小写配置,即可检测到大小写名称更改

22.闪烁动画

 .warning {
    background: url("~@/assets/images/home/map_marker_warning.png");
    position: relative;
    &::before {
      background: inherit;
      content: "";
      position: absolute;
      width: 100%;
      height: 100%;
      left: 0;
      top: 0;
      z-index: -2;
      animation: twinkling 0.3s ease-in-out infinite; //使用闪烁动画
    }
  }
  //定义闪烁动画
  @keyframes twinkling {
  0% {
    opacity: 0.2;
    filter: alpha(opacity=20);
    transform: scale(1);
  }

  50% {
    opacity: 1;
    filter: alpha(opacity=100);
    transform: scale(1.12);
  }

  100% {
    opacity: 0.2;
    filter: alpha(opacity=20);
    transform: scale(1);
  }
}
  

23.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;
}

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

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

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

24.对document.getElementsByClassName使用forEach报错

获取的元素与报错内容image.png

报错原因分析:document.getElementsByClassName获取到的内容不是严格意义上的数组,而是一个HTMLColletion集合,并且是只读的

解决方案:使用Array.prototype.forEach.call(els,el=>{})代替els.forEach

参考文档:www.yixzm.cn/blog/9.html

25.截取页面内容并下载为图片

安装依赖:npm install html2canvas

      let wrapper = this.$refs.domWrapper;
      html2canvas(wrapper,{这里可以写很多配置项,可以缺省}).then(canvas => {
        let dataURL = canvas.toDataURL("image/png");
        if (dataURL !== "") {
          let blob = this.dataURLToBlob(canvas.toDataURL("image/png"));
          let a = document.createElement("a");
          a.style.display = "none";
          a.setAttribute("href", URL.createObjectURL(blob));
          a.setAttribute("download", this.dlgInfo.com + ".png");
          document.body.appendChild(a);
          a.click();
          URL.revokeObjectURL(blob);
          document.body.removeChild(a);
        }
      });

25.截取页面内容并下载为PDF

前置工作:
1.npm i html2canvas
2.npm i jspdf
代码:

import html2canvas from "html2canvas";
import JsPDF from 'jspdf';
方法:
getPdf(title, html) {
  html2canvas(html, {
    allowTaint: false, 
    taintTest: false,
    logging: false, 
    useCORS: true,
    dpi: window.devicePixelRatio * 4, // 将分辨率提高到特定的DPI 提高四倍
    scale: 4, // 按比例增加分辨率

  }).then(canvas => {
    var pdf = new JsPDF('p', 'mm', 'a4'); // A4纸,纵向
    var ctx = canvas.getContext('2d'); 
    var a4w = 190; var a4h = 277; // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277  
    var imgHeight = Math.floor(a4h * canvas.width / a4w); // 按A4显示比例换算一页图像的像素高度  
    var renderedHeight = 0; 
    while (renderedHeight < canvas.height) {
      var page = document.createElement("canvas"); 
      page.width = canvas.width; 
      page.height = Math.min(imgHeight, canvas.height - renderedHeight);// 可能内容不足一页  
      // 用getImageData剪裁指定区域,并画到前面创建的canvas对象中  
      page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0); 
      pdf.addImage(page.toDataURL('image/jpeg', 1.0), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width)); // 添加图像到页面,保留10mm边距 
      renderedHeight += imgHeight;  
      if (renderedHeight < canvas.height) {
        pdf.addPage();// 如果后面还有内容,添加一个空页 
      }
    }
    // 保存文件  
    pdf.save(title + '.pdf');
  });
}
调用:
getPdf('我的PDF文件', this.$refs.pdfContainer);

源文档:blog.csdn.net/weixin_4706…