前端笔记(VUE)

前端笔记-VUE

    • 扁平数据结构转树形结构
    • 树-查找节点的所有父元素
    • a-form-model跳转到校验失败的位置
    • 表单校验->正则校验
    • 全屏展示某个DOM
    • 滚动条样式
    • dayjs()
    • 文字超出省略
    • 文件上传(a-upload)
    • 文件流下载
    • 导出表格选中数据(前端导出,不涉及后端文件流)
      • 第一种
      • 第二种
    • *嵌套表格-数据导出
    • a-table-表格列渲染
    • a-table列合并
    • a-table 行选中-行样式
    • a-table鼠标覆盖时 行变色(:hover)
    • a-table 缩小表格选择框间距
    • 汉字排序
    • 数组分组
    • 生成主键
    • a-select下拉框-模糊搜索(在现有的数据里,根据关键词过滤)
    • a-select下拉框-可输入可选择
    • a-select下拉框-接口检索(输入关键词调用接口检索)
    • sortablejs 拖动排序
    • base64转文件流 并下载
    • vxe-modal 弹窗
    • vxe-table表格
    • vxe-table合并列
    • 解决a-toolTip a-popover等浮窗组件上下浮动的问题
    • Antd $ m e s s a g e 、 message、 messageconfirm提示换行
    • Echarts图表转Base64
    • 计算字符串的宽度(css:width)
    • 获取递归组件、嵌套组件的DOM --> vue-ref
    • antd 日期选择器-开始日期< 结束日期
    • vscode eslint setting.json && .eslintrc.js 配置(备份)

持续更新中··· 先上梦想~
前端笔记(VUE)_第1张图片

扁平数据结构转树形结构

  flap2tree(arr, key = 'id', parentKey = 'pid') {
    const result:any = [];
    const mapObj = {};
    const copyArr:any = [];
    arr.forEach((item) => {
      const newItem = { ...item };
      copyArr.push(newItem);
      mapObj[item[key]] = newItem;
    });
    copyArr.forEach((item) => {
      const pid = item[parentKey];
      let parent:any = null;
      if (pid) {
        parent = mapObj[pid];
        if (parent) {
          if (!parent.children) {
            parent.children = [];
          }
          parent.children.push(item);
        }
      }
      if (!parent) {
        result.push(item);
      }
      mapObj[item[key]] = item;
    });
    return result;
  }

树-查找节点的所有父元素

  // allMenuTreeData 树数据
  // menuId 需要查找的节点
  getFamilyData(allMenuTreeData, menuId) {
    // 返回数据集合
    const targetArr: any = [];
    // 声明递归函数
    const forFn = function(arr, id) {
      // 遍历树
      for (let i = 0; i < arr.length; i++) {
        const item = arr[i];
        if (item.menuId.toString() === id.toString()) {
          // 查找到指定节点加入集合
          targetArr.push(item);
          // 查找其父节点
          forFn(allMenuTreeData, item.parentId);
          // 不必向下遍历,跳出循环
          break;
        } else {
          if (item.children) {
            // 向下查找到id
            forFn(item.children, id);
          }
        }
      }
    };
    // 调用函数
    forFn(allMenuTreeData, menuId);
    // 返回结果
    return targetArr;
  }

a-form-model跳转到校验失败的位置

		 <a-form-model
		    ref="gbFormRef"
		    :label-col="{span: 5}"
		    :wrapper-col="{span: 18}"
		    :model="gbForm"
		    :rules="gbFormRules">
	          <a-form-model-item>
	            <a-button
		          type="primary"
		          :loading="dialogLoading"
		          @click="submit()">
		          保存
		        a-button>
	          a-form-model-item>
		  a-form-model>

  submit() {
    (this.$refs.gbFormRef as any).validate((valid: any) => {
      if (valid) {
      	// 校验成功
      } else {
        setTimeout(() => {
          const isError:any = document.getElementsByClassName('has-error'); // antd为has-error
          isError[0].querySelector('input').focus();
        }, 1);
        return false;
      }
    });
  }

表单校验->正则校验

formRules = {
    contactNo: [
      { required: true, message: '请输入联系人电话', trigger: 'blur' },
      { pattern: /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|16[0-9]|14[57])[0-9]{8}$/, message: '电话格式不正确', trigger: 'blur' }
    ],
    telephone: [
      { required: false, message: '请输入联系方式', trigger: 'change' },
      {
        validator: this.telephoneValidate,
        message: '请输入正确的手机号码',
        trigger: 'change'
      }
    ]
  };
  
  telephoneValidate = (rule, value, cb) => {
	   if (!value) {
		 return cb();
	   }
	   // 验证手机号的正则表达式
	   const regMobile = /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/;
	   if (regMobile.test(value)) {
		 return cb();
	   }
	   cb(new Error('请输入合法的手机号'));
	 }

全屏展示某个DOM

  fullscreen:false
  
  screenShow() {
    const element = this.$refs.dashboardRef; // 选中DOM
    if (this.fullscreen) {
      if (document.exitFullscreen) {
        document.exitFullscreen();
      } else if (document.webkitCancelFullScreen) {
        document.webkitCancelFullScreen();
      } else if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
      } else if (document.msExitFullscreen) {
        document.msExitFullscreen();
      }
    } else {
      if (element.requestFullscreen) {
        element.requestFullscreen();
      } else if (element.webkitRequestFullScreen) {
        element.webkitRequestFullScreen();
      } else if (element.mozRequestFullScreen) {
        element.mozRequestFullScreen();
      } else if (element.msRequestFullscreen) {
        // IE11
        element.msRequestFullscreen();
      }
    }
    this.fullscreen = !this.fullscreen;
  }

滚动条样式

<style>
	*::-webkit-scrollbar {
	  width: 6px;
	  height: 14px;
	}
	/*滚动条的滑轨背景颜色,可以用display:none让其不显示,也可以添加背景图片,颜色改变显示效果。*/
	*::-webkit-scrollbar-track {
	  display: none;
	  background-color: #f5f5f5;
	  -webkit-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.1);
	  border-radius: 5px;
	}
	*::-webkit-scrollbar-thumb {
	  /* 滚动条 */
	  background-color: rgba(0, 0, 0, 0.2) !important;
	  border-radius: 5px;
	}
	*::-webkit-scrollbar-button {
	  /* 上下翻页的按钮 */
	  background-color: #eee;
	  display: none;
	}
	*::-webkit-scrollbar-corner {
	  background-color: black;
	}
style>

dayjs()

	dayjs().subtract(3, 'week').format('YYYY-MM-DD')  //取前三个周的日期 	
	dayjs().add(3, 'week').format('YYYY-MM-DD')    //取三周以后的日期	

文字超出省略

.displayClass {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

文件上传(a-upload)

在项目中使用自身的上传功能时,会存在没有鉴权信息(请求不携带token)的问题,因此使用组件的customRequest 属性来解决。
customRequest:通过覆盖默认的上传行为,实现自定义自己的上传。

参考文献:https://blog.csdn.net/qy8189/article/details/127547540 点此跳转

	 <a-upload
	    name="file"
	    accept=".xls,.xlsx"
	    :custom-request="uploadExecl"
	  >
	    <a-button
	      :loading="uploadLoading"
	      type="primary"
	      icon="upload"
	    >
	      <span v-if="uploadLoading">
	        导入中...
	      span>
	      <span v-else>
	        导入名单
	      span>
	    a-button>
	  a-upload>
	uploadLoading: boolean = false
	
	uploadExecl(info: any) {
	   // 使用提交form表单形式,渲染入参
	   const param: any = new FormData();
	   param.append('file', info.file);
	   param.append('orgId', '123456789');
	   this.uploadLoading = true;
	   uploadApi.uploadFile(param)
	     .then(res => {
          this.$message.success('导入成功!')
	       this.uploadLoading= false;
	     })
	     .catch(err => {
	       this.uploadLoading= false;
	       this.$message.error(err);
	     });
	 }

文件流下载

  exportByFileStream() { 
    exportApi.downExcel()
      .then((res: any) => {
        const filename = `人员导入模板.xls`;
        const blob = new Blob([res], { type: 'application/octet-stream;' });
        if ('download' in document.createElement('a')) {
          const elink = document.createElement('a');
          elink.download = filename;
          elink.style.display = 'none';
          elink.href = URL.createObjectURL(blob);
          document.body.appendChild(elink);
          elink.click();
          URL.revokeObjectURL(elink.href);
          document.body.removeChild(elink);
        } else {
          (navigator as any).msSaveBlob(blob, filename);
        }
      })
      .catch(err => {
        this.$message.error(`下载失败:${err}`);
      });
  }

导出表格选中数据(前端导出,不涉及后端文件流)

第一种


  exportTable() {
    if (this.selectedRowKeys.length === 0) {
      this.$message.warn('请选择需要导出的数据!');
      return;
    }
    const excelTitle = '预约列表';
    const tHeader: any = [];
    const filterVal: any = []; // 字段名称
    this.columns.forEach(item => {
      if (item.title !== '套餐项目') {
        tHeader.push(item.title);
        filterVal.push(item.dataIndex);
      }
    });
    // selectedRows:选中的表格数据
    const data = this.selectedRows.map(v => filterVal.map(j => v[j]));
    import('@/utils/Export2Excel').then(excel => {
      excel.export_json_to_excel({
        header: tHeader, // 表头   可以指定表头,如['id','name']
        data, // 表头所对应的数据    如['李四','张三']
        filename: excelTitle // 文件标题
      });
    });
  }

下载依赖xlsx、file-saver
npm i xlsx --save
npm i file-saver --save

新建文件 utils/Export2Excel

/* 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}`
  );
}

第二种

相比第一种更j简洁,代码量更少
npm install xlsx --save

  import XLSX from 'xlsx';
  
  exportExcel() {
    if (!this.selectedRowsList || this.selectedRowsList.length === 0) {
      this.$message.info('请选择导出人员!');
      return;
    }
    const columns = this.column.filter((item: { exportFlag: any; }) => item.exportFlag).map((item: { title: any; }) => item.title)
    const dataIndex = this.column.filter((item: { exportFlag: any; }) => item.exportFlag).map((item: { dataIndex: any; }) => item.dataIndex)
    const data = this.selectedRowsList.map((v: { [x: string]: any; }) => dataIndex.map((j:any) => v[j]))
    const param = [columns, ...data]
    const excelName = '人员信息' + '.xlsx';
    const ws = XLSX.utils.aoa_to_sheet(param);
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, '人员信息');
    XLSX.writeFile(wb, excelName);
  }

*嵌套表格-数据导出

原型以及数据结构前端笔记(VUE)_第2张图片
![在这里插入图片描述](https://img-blog.csdnimg.cn/43ad2e3eceee4300a3b35ed642ece404.jpeg

exportData() {
    if (this.selectedRowKeys.length === 0) return this.$message.warn('请勾选需要导出的数据!')
    const selectedRowsList = this.tableData.filter(item => this.selectedRowKeys.findIndex((key: any) => key === item.ohOrderCheckId) > -1)
    const columns = this.columns.map(item => item.title)
    const parentDataIndex = this.columns.map((item: { dataIndex: any; }) => item.dataIndex)
    const data:any = []
    selectedRowsList.forEach(item => {
      data.push(parentDataIndex.map((j:any) => item[j]))
      if (item.children) {
        const childDataIndex = this.childColumns.map((itm: any) => itm.dataIndex)
        const cHeader = this.childColumns.map(itm => itm.title)
        cHeader.unshift('')
        data.push(cHeader)
        item.children.forEach((cItem:any) => {
          const cData = childDataIndex.map((j:any) => cItem[j])
          cData.unshift('')
          data.push(cData)
        })
      }
    })

    const param = [columns, ...data]  //param数据结构如下图
    console.log(param);
    const excelName = '人员信息' + '.xlsx';
    const ws = XLSX.utils.aoa_to_sheet(param);
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, '人员信息');
    XLSX.writeFile(wb, excelName);
  }

构造出来的param
前端笔记(VUE)_第3张图片

a-table-表格列渲染


  columns = [
    {
      title: '登记状态',
      dataIndex: 'regFlag',
      customCell: (row:any) => {
        return {
          style: {
            color: row.reviewStatus === 3 ? 'red' : ''
          }
        };
      },
      customRender: val => {
        // customRender接收三个参数(val,row,index)
        return val === 1 ? '已登记' : '未登记';
      }
    }
  ];

a-table列合并

 // 定义一个全局变量,存储重复的值,支持多列
  temp:any ={}

  columns = [
    {
      title: '性别',
      dataIndex: 'sexName',
      customRender: (value, row, index) => {
        const obj = {
          children: value,
          attrs: {}
        };
        obj.attrs.rowSpan = this.mergeCells(row.sexName, this.tableData, 'sexName');
        // 第一个参数:列对应的值
        // 第二个参数:表格数据tableData
        // 第三个参数:列名
        return obj;
      }
    }
  ];
  
  mergeCells(text, array, columns) {
    // text:列对应的值  array:表格数据tableData  columns:列名
    let i = 0;
    if (text !== this.temp[columns]) {
      this.temp[columns] = text;
      array.forEach(item => {
        if (item[columns] === this.temp[columns]) {
          i += 1;
        }
      });
    }
    return i;
  }

a-table 行选中-行样式

      <a-table
        :row-class-name="row => (row.clicked ? 'selectedRowClass' : '')"
      />  
			  
	 .selectedRowClass {
		background: #3b9dff5e;
	  }

a-table鼠标覆盖时 行变色(:hover)


.ant-table-tbody {
  > tr:hover:not(.ant-table-expanded-row) > td,
  .ant-table-row-hover,
  .ant-table-row-hover > td {
    background: #3b7cff !important;
  }
}

a-table 缩小表格选择框间距

.ant-table colgroup > col.ant-table-selection-col {
  width: 45px;
}

汉字排序

    const newData = resData.sort((item1, item2) => {
      return item1.userName.localeCompare(item2.userName, 'zh');
    })

数组分组


  renderPayUserData() {
    const sortedArr = this.groupBy(this.selectedRows, item => {
      return [item.groupId]; // 按照groupId进行分组
    });
  }

  groupBy(array, f) {
    const groups = {};
    array.forEach(item => {
      const group = JSON.stringify(f(item));
      groups[group] = groups[group] || [];
      groups[group].push(item);
    });
    return Object.keys(groups).map(group => {
      return groups[group];
    });
  }

生成主键

  getUUid() {
    let guid = '';
    for (let i = 1; i <= 32; i++) {
      var n = Math.floor(Math.random() * 16.0).toString(16);
      guid += n;
      if (i === 8 || i === 12 || i === 16 || i === 20) {
        guid += '-';
      }
    }
    return guid;
  }

a-select下拉框-模糊搜索(在现有的数据里,根据关键词过滤)

使用场景:下拉框数据量大时,可只获取50条,通过输入关键词来调用接口来选择

  filterOption(input, option) {
    return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0;
  }

a-select下拉框-可输入可选择

      <a-select
        v-model="payForm.patName"
        :show-search="true"
        :not-found-content="null"
        :filter-option="true"
        @search="handleSearch"
        @blur="handleBlur"
        @change="handleChange"
      >
        <a-select-option
          v-for="item of patNameList"
          :key="item.patId"
          :value="item.patName"
        >
          {{ item.patName }}
        a-select-option>
      a-select>
  handleBlur(name) {
    this.payForm.patName = name;
  }

  handleSearch(name) {
    this.handleChange(name)
  }

  handleChange(value) {
    this.healCertForm.healthOrgName = (value != null && value !== '') ? value : '' // 这里是重点
  }

a-select下拉框-接口检索(输入关键词调用接口检索)

在这里插入代码片
        <a-select
          v-model="personListForm.industrialClassification"
          show-search
          :filter-option="false"
          :not-found-content="workTypeLoading ? undefined : null"
          @search="wokrTypeSelectSearch"
        >
          <a-spin
            v-if="workTypeLoading"
            slot="notFoundContent"
            size="small" />
          <a-select-option
            v-for="d in industrialClassificationList"
            :key="d.industryTypeId"
            :value="d.industryTypeId">
            {{ d.industryTypeName }}
          a-select-option>
        a-select>
  wokrTypeSelectSearch(industryTypeName: any) {
    this.workTypeLoading = true
    basicMaintenance.dictIndustryType({
      industryTypeName,
      pageNum: 1,
      pageSize: 100
    })
      .then((result:any) => {
        this.industrialClassificationList = result.list
        this.workTypeLoading = false
      })
      .catch(err => {
        this.workTypeLoading = false
        this.$message.error(`行业查询失败:${err}`)
      })
  }

sortablejs 拖动排序

文档地址 http://www.sortablejs.com/options.html

	  import Sortable from 'sortablejs'
	   
	  mounted() {
		  this.initSortable()
	   }
 
	  initSortable() {
		this.$nextTick(() => {
		  const el = document.querySelectorAll('.ant-table-tbody')[2]
		  // 创建拖拽对象
		  this.sortable = Sortable.create(el, {
			sort: !this.tableCompDisabledFlag, // 是否可进行拖拽排序
			animation: 150,// ms, number 单位:ms,定义排序动画的时间
			// 拖拽完成,移除拖拽之前的位置上的元素,在拖拽之后的位置上添加拖拽元素
			onEnd: ({ newIndex, oldIndex }) => {   
			// 注意:这里要直接操作原数据,不能深拷贝,否则存在缓存问题
			// 业务代码自行忽略
			  const val = this.itemAdviceTableData[oldIndex]
			  this.itemAdviceTableData.splice(oldIndex, 1)
			  this.itemAdviceTableData.splice(newIndex, 0, val)
			}
		  })
		})
	  }

base64转文件流 并下载

export function base64toFile(dataurl, filename = 'file') {
  // base64转文件流 并下载
  const arr = dataurl.split(',')
  const mime = arr[0].match(/:(.*?);/)[1]
  const suffix = mime.split('/')[1]
  const bstr = atob(arr[1])
  let n = bstr.length
  const u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  const file = new File([u8arr], `${filename}.${suffix}`, {
    type: mime
  })
  // 下载文件流 ↓↓↓
  const content = file;
  const blob = new Blob([content], { type: 'application/octet-stream' });
  if ('download' in document.createElement('a')) {
    const elink = document.createElement('a');
    elink.download = `${filename}.${suffix}`; // 文件名
    elink.style.display = 'none';
    elink.href = URL.createObjectURL(blob);
    document.body.appendChild(elink);
    elink.click();
    URL.revokeObjectURL(elink.href);
    document.body.removeChild(elink);
  }
}

vxe-modal 弹窗

解决a-moadal不可拖动、不可伸缩的问题
官方文档 https://vxetable.cn/v3/#/table/module/modal点此跳转

      <vxe-modal
        id="myModal6"
     	:value="true"
        size="small"
        :show-zoom="true"
        resize
        show-footer
        fullscreen
        esc-closable
        width="70vw"
        height="60vh"
        remember
        transfer
        draggable
	    @hide="closeDialog"
	    @close="closeDialog"
      >
        <template #footer>
          <a-button type="primary" @click="closeDialog">
            关闭
          a-button>
        template>
        <template #title>
          总检诊台
        template>
        <template #default>
          aaaaaaaa
        template>
      vxe-modal>
   // @hide的作用是使用Esc方式退出时,会造成打不开的问题,所以需要对value绑定值进行重置
  closeDialog() {
    this.$emit('closeDetailDialog');
  }

vxe-table表格

使用场景:解决数据量大,表格卡顿的问题


        <vxe-table
		  :column-config="{resizable: true}"
          ref="vxeTableRef"
          class="vxeTableClass"
          :data="peopleTableData"
          :loading="peopleTableLoading"
          :max-height="cardHeight - 190"
          show-overflow
          :row-class-name="rowClassName"
          :row-config="{isHover: true}"
          :checkbox-config="{trigger: 'row', highlight: false, range: false}"
          @checkbox-all="selectAllEvent"
          @checkbox-change="selectChangeEvent"
		  @cell-dblclick="dblClick"
        >
          <template #empty>
            <a-empty />
          template>
		  <vxe-table-column field="department" title="部门" width="110">
            <template #default="{row}">
              <a-tooltip>
                <template slot="title">
                  {{ row.department }}
                template>
                <span class="displayClass">
                  {{ row.department }}
                span>
              a-tooltip>
            template>
          vxe-table-column>
        vxe-table>
		  // API含义
		  @cell-dblclick="dblClick"      //双击的回调
		  :column-config="{resizable: true}"  // 列拖动
          :max-height="cardHeight - 190" // show-overflow    两个组合使用,固定高度
          :row-class-name="rowClassName"  // 设置行的css样式,通过class名来进行修改 -> 
          :row-config="{isHover: true}"   // 鼠标覆盖行变色
          :checkbox-config="{trigger: 'row', highlight: false, range: false}" // 点击行选中,选中时高亮显示,按住ctrl可以拖动选中
          @checkbox-all="selectAllEvent"   //  全选按钮回调
          @checkbox-change="selectChangeEvent" // 选择框 点击回调

vxe-table合并列

    <vxe-table
      :span-method="mergeRowMethod"
    >
  mergeRowMethod({ row, _rowIndex, column, visibleData }) {
    const fields = ['dept_name'];
    const cellValue = row[column.property];
    if (cellValue && fields.includes(column.property)) {
      const prevRow = visibleData[_rowIndex - 1];
      let nextRow = visibleData[_rowIndex + 1];
      if (prevRow && prevRow[column.property] === cellValue) {
        return { rowspan: 0, colspan: 0 };
      } else {
        let countRowspan = 1;
        while (nextRow && nextRow[column.property] === cellValue) {
          nextRow = visibleData[++countRowspan + _rowIndex];
        }
        if (countRowspan > 1) {
          return { rowspan: countRowspan, colspan: 1 };
        }
      }
    }
  }

解决a-toolTip a-popover等浮窗组件上下浮动的问题

transition-name=""

Antd $ m e s s a g e 、 message、 messageconfirm提示换行

    const arr = ['测试一', '测试二', '测试三'];
    that.$message.open({
      type: 'error',
      content: h => {
        return h(
          'div',
          {
            domProps: {
              innerHTML: arr.join(' 
'
) } }, [] ); } }); const arr = ['测试一', '测试二', '测试三']; this.$confirm({ title: '是否继续操作?', content: h => { return h( 'div', { domProps: { innerHTML: arr.join('
'
) } }, [] ); }, onOk() {}, onCancel() {} });

Echarts图表转Base64

const echartBase64 = (document as any).getElementById('echartId').getElementsByTagName('canvas')[0].toDataURL()

计算字符串的宽度(css:width)

使用场景:计算输入框中字符串的width,如果>=输入框的width,则显示tooltip全部文字
前端笔记(VUE)_第4张图片

    <a-tooltip>
      <span v-if="computeShowTip(inputContent)" slot="title">
        {{ inputContent }}
      span>
      <input v-model="item4.inputContent" />
    a-tooltip>
    computeShowTip(inputContent){
      const dom = document.createElement('span');
      dom.style.display = 'inline-block';
      dom.textContent = inputContent;
      document.body.appendChild(dom);
      const width = dom.clientWidth;
      document.body.removeChild(dom);
      return width >= 90 // 假设输入框的width:90px
    }

获取递归组件、嵌套组件的DOM --> vue-ref

使用场景:1、使用递归组件时,只能获取到第一次渲染的ref,后续递归出的组件或取不到,此时使用vue-ref插件。2、获取父子组件、兄弟组件的ref

// npm安装
npm install vue-ref --save

// main.js中
import ref from "vue-ref"
Vue.use(ref, { name: "ant-ref" }) // name是自定义api名称

父组件

  provide() {
    return {
      setChildrenRef: (name, ref) => {
        this.$set(this.domMap,name,ref)
      },
      getChildrenRef: name => {
        return this[name];
      }
    }
  }data() {
    return {
      domMap: {}
    }
  }

子组件

	<a-input v-model="input" v-ant-ref="dom => setChildrenRef('inpitItemRef', dom )" />
  inject: ['setChildrenRef'], // 用到父组件的哪个方法就引用哪个

这样加载页面时,子组件中的v-ant-ref指令会调用父组件的方法,把子组件中的dom存储到父组件的domMap中.
vue-ref使用案例:点击链接跳转

antd 日期选择器-开始日期< 结束日期


  <a-date-picker
    v-model="searchForm.startRegTime"
    :disabled-date="regDisabledStartDate"
  />

  <a-date-picker
    v-model="searchForm.endRegTime"
    :disabled-date="regDisabledEndDate"
  />

  regDisabledStartDate(startValue: any) {
    const endValue = this.searchForm.endRegTime;
    if (!startValue || !endValue) {
      return false;
    }
    const data = new Date(endValue).getTime();
    return data < startValue.valueOf();
  }

  regDisabledEndDate(endValue: any) {
    const startValue = this.searchForm.startRegTime;
    if (!startValue) {
      return false;
    }
    const data = new Date(startValue).getTime();
    return data >= endValue.valueOf();
  }

vscode eslint setting.json && .eslintrc.js 配置(备份)

eslint配置

{
  // vscode默认启用了根据文件类型自动设置tabsize的选项
  "editor.detectIndentation": false,
  // 重新设定tabsize
  "editor.tabSize": 2,
  // #每次保存的时候自动格式化
  // "editor.formatOnSave": true,
  //  #让prettier使用eslint的代码格式进行校验
  //  #去掉代码结尾的分号
  "prettier.semi": false,
  "prettier.trailingComma": "none",
  //  #使用带引号替代双引号
  "prettier.singleQuote": true,
  //  #让函数(名)和后面的括号之间加个空格
  "javascript.format.insertSpaceBeforeFunctionParenthesis": false,
  // #这个按用户自身习惯选择
  "vetur.format.defaultFormatter.html": "js-beautify-html",
  // #让vue中的js按编辑器自带的ts格式进行格式化
  "vetur.format.defaultFormatter.js": "vscode-typescript",
  "vetur.format.defaultFormatterOptions": {
    "vetur.format.defaultFormatter.html": "js-beautify-html", //格式化.vue中html
    "js-beautify-html": {
      // "wrap_attributes": "aligned-multiple"
      // "wrap_attributes": "force-expand-multiline" //属性强制换行对齐
      // "wrap_attributes": "force-aligned" //属性强制折行对齐
    }
  },
  "editor.suggestSelection": "first",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "editor.fontLigatures": false,
  "[vue]": {
    "editor.defaultFormatter": "octref.vetur"
  },
  "workbench.colorTheme": "Monokai Dimmed+Vibrant", // 两个选择器中是否换行    
  /* 自定义主题颜色 */
  "editor.tokenColorCustomizations": {
    "[Monokai Dimmed+Vibrant]": {
      "keywords": "#db00f8", //关键字
      "types": "#42b983", //类型定义
      "numbers": "#00e1ff", //数字
      "textMateRules": [
        {
          "scope": "keyword.control", //if ,else, try 等控制符
          "settings": {
            "foreground": "#50c571",
            "fontStyle": "italic bold "
          }
        },
        {
          "scope": "entity.name.tag",
          "settings": {
            // "foreground": "#ee258d",
            "fontStyle": "bold italic"
          }
        },
        {
          "scope": "entity.other.attribute-name",
          "settings": {
            // "foreground": "#0066ff",
            "fontStyle": "bold italic"
          }
        },
        {
          "scope": "variable.other.object",
          "settings": {
            "foreground": "#0099ff",
            "fontStyle": ""
          }
        },
        {
          "scope": "keyword.operator",
          "settings": {
            // "foreground": "#fa3434",
            // "fontStyle": "bold",
          }
        }
      ]
      //   "variables": "#9814ef",//变量
      // "comments": "#beb9b9" //注释
      //   "functions": "#f10070f5",//函数
      //   "strings": "#e72499", // 字符串的颜色,
    }
  },
  "[jsonc]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[html]": {
    "editor.defaultFormatter": "mohd-akram.vscode-html-format"
  },
  "[less]": {
    "editor.defaultFormatter": "vscode.css-language-features"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  // "editor.smoothScrolling": true, //使编辑器滚动变平
  // 自定义vscode面板颜色
  "workbench.colorCustomizations": {
    // "tab.activeBackground": "#253046", // 活动选项卡的背景色
    // "activityBar.background": "#253046", //活动栏背景色
    // "sideBar.background": "#253046", //侧边栏背景色
    // "activityBar.foreground": "#23f8c8", //活动栏前景色(例如用于图标)
    // "editor.background": "#292a2c", //编辑器背景颜色
    // "editor.foreground":"#ff0000", 	//编辑器默认前景色
    // "editor.findMatchBackground":"#f83123", 	//当前搜索匹配项的颜色
    // "editor.findMatchHighlightBackground":"#43ac93", 	//其他搜索匹配项的颜色
    // "editor.lineHighlightBackground":"#ff0000", 	//光标所在行高亮文本的背景颜色
    // "editor.selectionBackground":"#d3fd84", 	//编辑器所选内容的颜色
    // "editor.selectionHighlightBackground":"#ff0000", 	//与所选内容具有相同内容的区域颜色
    // "editor.rangeHighlightBackground":"#ff0000", 	//突出显示范围的背景颜色,例如 "Quick Open" 和“查找”功能
    // "editorBracketMatch.background":"#edfc6a98", 	//匹配括号的背景色
    // "editorCursor.foreground":"#ff0000", 	//编辑器光标颜色
    // "editorGutter.background":"#ff0000", 	//编辑器导航线的背景色,导航线包括边缘符号和行号
    // "editorLineNumber.foreground":"#ff0000", 	//编辑器行号颜色
    // "sideBar.foreground":"#ff0000", 	//侧边栏前景色
    // "sideBarSectionHeader.background":"#ff0000", 	//侧边栏节标题的背景颜色
    // "statusBar.background":"#ff0000", 	//标准状态栏背景色
    // "statusBar.noFolderBackground":"#ff0000", 	//没有打开文件夹时状态栏的背景色
    // "statusBar.debuggingBackground":"#ff0000", 	//调试程序时状态栏的背景色
    // "tab.activeForeground":"#ff0000", 	//活动组中活动选项卡的前景色
    // "tab.inactiveBackground":"#ff0000", 	//非活动选项卡的背景色
    // "tab.inactiveForeground":"#ff0000" // 活动组中非活动选项卡的前景色
  },
}

eslintrc

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true,
    es6: true
  },
  extends: ['eslint:recommended', 'plugin:vue/recommended', '@vue/standard', '@vue/typescript'],
  rules: {
    'camelcase': 'off',
    'no-console': process.env.NODE_ENV === 'production' ? 'off' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'off' : 'off',
    'no-prototype-builtins': 'off',
    'no-unused-vars': 'off',
    semi: 'off',
    'space-before-function-paren': [2, 'never'],
    // 'vue/html-closing-bracket-newline': 'off',
    'vue/html-self-closing': 'off',
    'vue/no-v-html': 'off',
    'vue/array-bracket-spacing': 'error',
    'vue/arrow-spacing': 'error',
    'vue/block-spacing': 'error',
    'vue/brace-style': 'error',
    'vue/camelcase': 'error',
    'vue/comma-dangle': 'error',
    'vue/component-name-in-template-casing': 'error',
    'vue/eqeqeq': 'error',
    'vue/key-spacing': 'error',
    'vue/match-component-file-name': 'error',
    'vue/object-curly-spacing': 'error',
    "vue/max-attributes-per-line": ["error", {
      "singleline": 2,
      "multiline": {
        "max": 1,
        "allowFirstLine": false
      }
    }],
    "vue/html-indent": ["error", 2, {
      "attribute": 1,
      "baseIndent": 1,
      "closeBracket": 0,
      "alignAttributesVertically": true,
      "ignores": []
    }],
    'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }]
  },
  globals: {
    _: true,
    dayjs: true,
    $: true,
    echarts: true,
    Echarts: true
  },
  parserOptions: {
    parser: '@typescript-eslint/parser'
  },
  overrides: [
    {
      files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
      env: {
        jest: true
      }
    }
  ],
  ignorePatterns: ['src/assets/font/iconfont.js']
};

你可能感兴趣的:(前端,vue.js,javascript)