vue笔记

汇总了下日常工作中前端的小tip,增加复制粘贴的效率。 ——来自低级前端粘贴工程师

持续更新ing,话不多说先上梦想~
在这里插入图片描述
在这里插入图片描述

1、三方组件库

  1. 大屏组件库-DataV
  2. JYeontu组件库 (好玩的东西比较多)
  3. Quasar

2、Promise.all

使用场景:多个异步操作全部执行完后,再继续执行

  <a-button @click="getUserInfo">测试执行顺序</a-button>

  getAAA() {
    return new Promise((resolve) => {
      api.getAAA()
        .then(res => {
          console.log('111');
          this.nameList = res
          resolve(true)
        })
    })
  },
  getBBB() {
    return new Promise((resolve) => {
      api.getBBB()
        .then(res => {
          console.log('222');
          this.sexList = res
          resolve(true)
        })
    })
  },
  getUserInfo() {
    Promise.all[this.getAAA(), this.getBBB()]
      .then(res => {
        console.log('333');
      })
  }

打印顺序:111 222 333或者222 111 333

3、扁平数据结构转树形结构

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

4、查找树节点

4.1树-根据id查找该节点的父节点

输入一个pid,查找id=pid的父节点

  treeSelect(selectedKeys: any, info: any) {
    const nodeData = info.node.dataRef;
    
    const getParentData= (arr: any, pid: any) => {
       let obj: any;
       const find = (arr: any, pid: any) => {
         for (let i = 0; i < arr.length; i++) {
           if (obj) break;
           if (arr[i].id === pid) {
             obj = arr[i];
             break;
           } else if (arr[i].children && arr[i].children.length > 0) {
             find(arr[i].children, pid);
           }
         }
       };
       find(arr, pid);
       return obj;
     };
     const parentData: any = getParentData(this.treeData, nodeData.pid);
     console.log(companyData)
  }

4.2树-根据id查找节点的所有父元素

输入一个id,查找该id的所有上级节点

  // 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;
  }

5、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;
      }
    });
  }

6、表单校验->正则校验

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('请输入合法的手机号'));
	 }

7、节流、防抖(lodash库)

例如设置的时间为2s
节流(throttle):连续点击时,在点击时的2秒内只执行第一次,2s后 再次执行
防抖(debounce):连续点击时第一次执行,执行后出现2s的保护时间,保护时间内,再次点击时,重置2s的保护时间

   <a-button @click="testClick('第一个参数','第二个参数')">
     点击
   </a-button>

  import _ from 'lodash';
  
  /*
    例如设置的时间为2s
    节流(throttle):连续点击时,在点击时的2秒内只执行第一次,2s后 再次执行
    防抖(debounce):连续点击时第一次执行,执行后出现2s的保护时间,保护时间内,再次点击时,重置2s的保护时间

    leading // 第一次生效
    trailing // 最后一次生效
    可自由设置首尾生效效果

  */
  // 防抖
  testClick = _.debounce(this.bbb, 1000, {
    leading: false,
    trailing: true,
  });
  // 节流同理
  testClick = _.throttle(this.bbb, 1000, {
    leading: false,
    trailing: true,
  });

  bbb(param1: any, param2: any) {
    console.log(param1, param2);
  }

js写法


	import { debounce } from 'lodash-es';
	
    checkPurposeDebounce: debounce(function () {
      this.changeSystemSet('CheckPurpose')
    }, 1000, {
      leading: false,
      trailing: true
    }),

8、全屏展示某个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;
  }

9、滚动条样式

<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>

10、dayjs()

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

11、文字超出省略

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

12、文件上传相关

12.1 上传文件,并转为base64(a-upload)

适用于任何类型的文件,这里用图片为例

效果图
上传前
上传后
效果图

      <a-form-model-item label="资质证书">
        <a-upload
          :show-upload-list="false"
          list-type="picture-card"
          :before-upload="beforeUpload"
          :custom-request="selfUpload"
        >
          <img
            v-if="hosForm.qualificationImage"
            style="max-width: 300px"
            :src="hosForm.qualificationImage"
          />
          <template v-if="!hosForm.qualificationImage">
            <a-button type="primary">
              上传
            </a-button>
            <span style="color:#7b7b7b">
              *要求文件格式为照片,且文件大小小于
              <span style="color:red">3M</span></span>
          </template>
          <a-button
            v-if="hosForm.qualificationImage"
            type="primary"
            style="margin-top:6px"
            @click="deleteImg"
          >
            删除
          </a-button>
        </a-upload>
      </a-form-model-item>
  hosForm: any = {
    qualificationImage: ''
  };

  // 限制文件类型与文件大小
  beforeUpload(file: any) {
    const isJPG = file.type === 'image/jpeg' || file.type === 'image/jpg' || file.type === 'image/png' || file.type === 'image/bmp';
    if (!isJPG) {
      this.$message.error('请上传图片文件');
    }
    console.log(file.size);
    const size = file.size / 1024 / 1024;
    const isLt2M = size < 4;
    if (!isLt2M) {
      this.$message.error(`文件大小应小于3M,当前图片大小为${size.toFixed(2)}M!`);
    }
    return isJPG && isLt2M;
  }
  
  // 图片转base64
  selfUpload(e: any) {
    const { action, file, onSuccess, onError, onProgress } = e;
    const base64 = new Promise((resolve) => {
      const fileReader = new FileReader();
      fileReader.readAsDataURL(file);
      fileReader.onload = () => {
        resolve(fileReader.result);
        this.hosForm.qualificationImage = fileReader.result;
      };
    });
  }

  deleteImg(e: any) {
    this.hosForm.qualificationImage = '';
    e.stopPropagation();
  }
  
/* 调整备注文字与按钮样式 */ 
>>> .ant-upload.ant-upload-select-picture-card > .ant-upload {
  display: grid !important; 
}

12.2 自定义文件上传(a-upload)

在项目中使用<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);
	     });
	 }

12.3 文件流下载

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

13、数组去重

不解释连招

    // 数组去重
    for (var i = 0; i < arr.length; i++) {
      for (var j = i + 1; j < arr.length; j++) {
        if (arr[i].name === arr[j].name) {
          // 第一个name等于第二个name,splice方法删除第二个
          arr.splice(j, 1);
          j--;
        }
      }
    }

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

14.1 第一种(导出常规表格,无样式,无合并功能)(xlsx、file-saver)


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

14.2 第二种(适用场景同第一种)(xlsx)

相比第一种更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);
  }

14.3 第三种(可以设置导出样式——>单元格格式、表格样式、行列宽高、增加表格标题、表头)(lay-excel)

以下两种方法,均使用 LAY_EXCEL插件,使用场景以及区别如下
3.1:针对普通简单的表格,且存在标题、副标题(原理:合并单元格属性,可根据表格样式固定写死)
3.2:针对存在合并单元格的表格(原理:动态渲染合并单元格所需的坐标数组)

npm install lay-excel --save   //安装依赖
14.3.1 增加表头、表格标题(Excel单元格格式设置:numFmt: ‘@’)

注意:导出的数据中,表头的顺序必须与表头对应的字段的顺序保持一致!
如:tableHeader的顺序为 【姓名 性别 年龄 身份证号 出生日期】
则tableDatar的顺序为 【name sex age idcardno birthDy】

表格效果 在这里插入图片描述

页面样式页面样式


  import LAY_EXCEL from 'lay-excel';
	
  exportData() {
    const result = []; // 导出的数据
    this.tableData.forEach((item: any, index: any) => {
      const { companyName, serialNo, patName, sexName, patAge, hazardAgeYear, hazardFactorName, postStatusName, yszybName } = item;
      const obj = {
        orderNo: index + 1,
        companyName,
        serialNo,
        patName,
        sexName,
        patAge,
        hazardAgeYear,
        hazardFactorName,
        postStatusName
      };
      result.push(obj); // 设置数据的顺序(此顺序为columns对应的顺序)
    });
    const tableHeader = {}; // 表头
    const tableTitle = {}; // 标题
    const memoTitle = {}; // 小标题
    for (let i = 0; i < this.columns.length; i++) {
      // 设置列名
      this.$set(tableHeader, this.columns[i].dataIndex, this.columns[i].title);
      // 设置小标题
      this.$set(memoTitle, this.columns[i].dataIndex, i <= 4 ? '填报单位(盖章):山东省济南市龙奥大厦' : '填报日期: 2023-08-14');
      // 设置标题
      this.$set(tableTitle, this.columns[i].dataIndex, '_______年度职业健康检查发现疑似职业病人员名单');
    }
    result.unshift(tableHeader);
    result.unshift(memoTitle);
    result.unshift(tableTitle);
    // 设置表格样式
    /*
      setExportCellStyle(param1,param2,param3)
      param1:要导出的数据
      param2:设置样式生效范围(Excel的xy坐标,如A1到E10,则设置为 'A1:E10')
      param3:样式回调函数
    */
    LAY_EXCEL.setExportCellStyle(result, `A1:K${this.tableData.length + 4}`, {
      s: {
        numFmt: '@',// 单元格格式 设置为 文本(这里在14.3.3详细说)
        fill: {
          bgColor: {
            rgb: '217346'
          },
          fgColor: {
            rgb: 'FFFFFF'
          }
        },
        // font: {
        //   color: {
        //     rgb: 'e74032'
        //   },
        //   sz: '28',
        //   bold: false
        // },
        alignment: {
          // 文本对齐方式
          horizontal: 'center',
          vertical: 'center'
        },
        border: {
          // 边框颜色以及线条样式
          top: { style: 'thin', color: { rgb: 'd4d4d4' } },
          bottom: { style: 'thin', color: { rgb: 'd4d4d4' } },
          left: { style: 'thin', color: { rgb: 'd4d4d4' } },
          right: { style: 'thin', color: { rgb: 'd4d4d4' } }
        }
      }
    });

    // 行列合并设置(同是Excel的xy坐标)
    var mergeConf = LAY_EXCEL.makeMergeConfig([
    // 根据表格样式,写出合并坐标
      ['A1', 'K1'],
      ['A2', 'E2'],
      ['F2', 'K2']
    ]);
    // 设置行的宽度(从开头到结束)
    var colConf = LAY_EXCEL.makeColConfig({ A: 50, B: 300, C: 150, D: 80, E: 50, F: 50, G: 60, H: 300, I: 100, J: 100, K: 100 });
    // 设置列的高度
    var rowConf = LAY_EXCEL.makeRowConfig({ 0: 50, 1: 30 }); // 第0行和第一行
    LAY_EXCEL.exportExcel(
      {
        第一页: result
      },
      '职业病人员名单.xlsx',
      'xlsx',
      {
        extend: {
          // extend 中可以指定某个 sheet 的属性,如果不指定 sheet 则所有 sheet 套用同一套属性
          第一页: {
            // 以下配置仅 sheet1 有效
            '!merges': mergeConf,
            '!cols': colConf,
            '!rows': rowConf
          }
        }
      }
    );
  }
14.3.2 合并单元格表格的导出

页面样式在这里插入图片描述

页面样式
在这里插入图片描述


//与3.1相同,重点是mergeConf() 
  exportData() {
    const result = []; // 导出的数据
    this.tableData.forEach((item: any, index: any) => {
      const { seq, statisticsMonth, personNum, price, totalPrice } = item;
      result.push({ seq, statisticsMonth, personNum, price, totalPrice }); // 设置数据的顺序(此顺序为columns对应的顺序)
    });
    const tableHeader = {}; // 表头
    const tableTitle = {}; // 标题
    for (let i = 0; i < this.columns.length; i++) {
      // 设置列名
      this.$set(tableHeader, this.columns[i].dataIndex, this.columns[i].title);
      // 设置标题
      this.$set(tableTitle, this.columns[i].dataIndex, '职业病体检汇总');
    }
    result.unshift(tableHeader);
    result.unshift(tableTitle);
    // 设置表格样式
    /*
      setExportCellStyle(param1,param2,param3)
      param1:要导出的数据
      param2:设置样式生效范围(Excel的xy坐标,如A1到E10,则设置为 'A1:E10')
      param3:样式回调函数
    */
    LAY_EXCEL.setExportCellStyle(result, `A1:K${this.tableData.length + 2}`, {
      s: {
        fill: {
          bgColor: {
            rgb: '217346'
          },
          fgColor: {
            rgb: 'FFFFFF'
          }
        },
        // font: {
        //   color: {
        //     rgb: 'e74032'
        //   },
        //   sz: '28',
        //   bold: false
        // },
        alignment: {
          // 文本对齐方式
          horizontal: 'center',
          vertical: 'center'
        },
        border: {
          // 边框颜色以及线条样式
          top: { style: 'thin', color: { rgb: 'd4d4d4' } },
          bottom: { style: 'thin', color: { rgb: 'd4d4d4' } },
          left: { style: 'thin', color: { rgb: 'd4d4d4' } },
          right: { style: 'thin', color: { rgb: 'd4d4d4' } }
        }
      }
    });
    // 行列合并设置(同是Excel的xy坐标)
    var mergeConf = LAY_EXCEL.makeMergeConfig([
      ['A1', 'E1'],
      ...this.mergeConf() // 合并列
    ]);
    // 设置行的宽度(从开头到结束)
    var colConf = LAY_EXCEL.makeColConfig({ A: 50, B: 150, C: 80, D: 80, E: 80 });
    // 设置列的高度
    var rowConf = LAY_EXCEL.makeRowConfig({ 0: 50, 1: 30 }); // 第0行和第一行
    LAY_EXCEL.exportExcel(
      {
        第一页: result
      },
      '职业病体检汇总.xlsx',
      'xlsx',
      {
        extend: {
          // extend 中可以指定某个 sheet 的属性,如果不指定 sheet 则所有 sheet 套用同一套属性
          第一页: {
            // 以下配置仅 sheet1 有效
            '!merges': mergeConf,
            '!cols': colConf,
            '!rows': rowConf
          }
        }
      }
    );
  }


  mergeConf() {
    /*
    动态渲染出Excel合并单元格所需的坐标数组
    mergeArr数据格式为:[['A1', 'E1'],['A1', 'E1'],['A1', 'E1'],['A1', 'E1']]
     */
    const mergeArr: any = [];
    const mergeFiledList: any = this.tableData.map((itm: any) => {
      return {
        seq: itm.seq,
        statisticsMonth: itm.statisticsMonth
      };
    });
    // 数组去重
    for (var i = 0; i < mergeFiledList.length; i++) {
      for (var j = i + 1; j < mergeFiledList.length; j++) {
        if (mergeFiledList[i].seq === mergeFiledList[j].seq && mergeFiledList[i].statisticsMonth === mergeFiledList[j].statisticsMonth) {
          mergeFiledList.splice(j, 1);
          j--;
        }
      }
    }

    mergeFiledList.forEach((item: any) => {
      // 找出字段出现次数 -->渲染结束坐标
      const getAppearNum = (fild: any, fildData: any) => {
        const fildArr = this.tableData.map((item) => item[fild]);
        return fildArr.filter((item) => item === fildData).length;
      };
	  // 以下方法是拆分写的,可以做合并
	  /* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
      // 渲染A列的配置(序号 seq)
      // +3是因为 标题+1行,表头+1行,索引从0开始再+1
      const startA = this.tableData.findIndex((itm) => itm.seq === item.seq) + 3; // +3根据场景 视情况而定
      const seqAppearNum = getAppearNum('seq', item.seq);
      const endA = `A${startA + seqAppearNum - 1}`; // 终点坐标=起点坐标+合并列的行数-1
      // push前进行去重
      if (!mergeArr.some((itm: any) => itm[0] === 'A' + startA && itm[1] === endA)) {
        mergeArr.push(['A' + startA, endA]);
      }
      // 同理
      // 渲染B列的配置(月份:statisticsMonth)
      const startB = this.tableData.findIndex((itm) => itm.statisticsMonth === item.statisticsMonth) + 3;
      const dateAppearNum = getAppearNum('statisticsMonth', item.statisticsMonth);
      const endB = `B${startB + dateAppearNum - 1}`;
      if (!mergeArr.some((itm: any) => itm[0] === 'B' + startB && itm[1] === endB)) {
        mergeArr.push(['B' + startB, endB]);
      }
      /* ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ */

      // 简化写法
      /* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
      const renderMergeArr = (axisFiled:any, excelColumn:any) => {
        const start = this.tableData.findIndex((itm) => itm[axisFiled] === item[axisFiled]) + 3;
        const appearNum = getAppearNum(axisFiled, item[axisFiled]);
        const end = excelColumn + `${start + appearNum - 1}`;
        if (!mergeArr.some((itm: any) => itm[0] === excelColumn + start && itm[1] === end)) {
          mergeArr.push([excelColumn + start, end]);
        }
      }
      renderMergeArr('seq', 'A')
      renderMergeArr('statisticsMonth', 'B')
      /* ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ */
    });
    return mergeArr;
  }
14.3.3 导出表格(设置单元格格式)

背景:有时导出的数据存在长数字类型(Long),如雪花算法生成的ID,此时导出的表格中的long类型的数据会存在精度丢失的问题(如下图所示),此时需要将单元格格式设置为文本型
精度丢失

解决方案:目前使用lay-excel,当然也有别的方案哈,此处demo以lay-excel导出表格数据为例

	 // 表格列
  getExportColumn() {
    return [
      { title: '诊断id(为空即新增)', dataIndex: 'dictDiagnosisId' },
      { title: '诊断名称', dataIndex: 'diagnosisName' },
      { title: '排序', dataIndex: 'seq' },
      { title: '第一关键字', dataIndex: 'firstKeyword' },
      { title: '第二关键字', dataIndex: 'secondKeyword' },
      { title: '第三关键字', dataIndex: 'thirdKeyword' },
      { title: '第一过滤关键字', dataIndex: 'filterFirstKeyword' },
      { title: '第二过滤关键字', dataIndex: 'filterSecondKeyword' },
      { title: '第三过滤关键字', dataIndex: 'filterThirdKeyword' },
      { title: '性别 未知, 男, 女', dataIndex: 'sexName' },
      { title: '诊断描述', dataIndex: 'diagnosisDes' },
      { title: '是否常见病 0不是1是', dataIndex: 'comAilmFlag' },
      { title: '是否既往病 0不是1是', dataIndex: 'pastIllnessFlag' },
      { title: '是否家族病 0不是1是', dataIndex: 'familialDiseaseFlag' },
      { title: '病因', dataIndex: 'pathogeny' },
      { title: '并发症', dataIndex: 'complication' },
      { title: '复查指南', dataIndex: 'reciewGuide' },
      { title: '常用模板 0不是1是', dataIndex: 'alwaysFlag' },
      { title: '随访标记 0无关1应该随访', dataIndex: 'sfFlag' },
      { title: '诊断等级(数字1-5)', dataIndex: 'diagnosisRank' },
    ]
  }
  
  async exportTable() {
    const allColumns:any = this.getExportColumn();
    // 需要导出的列的字段
    const exportColumns = allColumns.map((it:any) => it.dataIndex)
    // 处理导出数据:根据exportColumns把tableData过滤出需要导出的字段(删除tableData中不需要导出的数据)
    const data = LAY_EXCEL.filterExportData(this.tableData, exportColumns);
    console.log(data);// 参考图1
    const tableHeader: any = {};
    allColumns.forEach((item:any) => {
      tableHeader[item.dataIndex] = item.title;
    });
    console.log(tableHeader, '---tableHeader');// 参考图2
    data.unshift(tableHeader) // 导出数据中 添加表头
    this.exportAction(data, '诊断建议导出')
  }

  exportAction(result:any, fileName:any) {
    // 设置单元格样式生效范围,例如生效范围为A1到Z9的单元格 :A1:Z9
    // 我这里设置的动态的,根据你需求来定
    const range = fileName.includes('模板') ? 'A1:U999' : `A1:U${result.length + 10}`
    LAY_EXCEL.setExportCellStyle(result, range, {
      s: {
      	// numFmt是重点!!设置单元格的格式!!!
      	// ( 找的源码,日了公狗的 别问我咋找到的,就这个吊玩意坑了我一下午,@代表文本类型,可解决长数字精度丢失的问题,其他格式类型 放下面了)
        numFmt: '@',
        fill: {
          bgColor: {
            rgb: '217346',
          },
          fgColor: {
            rgb: 'FFFFFF',
          },
        },
        // font: {
        //   color: {
        //     rgb: 'e74032'
        //   },
        //   sz: '28',
        //   bold: false
        // },
        alignment: {
          // 文本对齐方式
          horizontal: 'center',
          vertical: 'center',
        },
        border: {
          // 边框颜色以及线条样式
          top: { style: 'thin', color: { rgb: 'd4d4d4' } },
          bottom: { style: 'thin', color: { rgb: 'd4d4d4' } },
          left: { style: 'thin', color: { rgb: 'd4d4d4' } },
          right: { style: 'thin', color: { rgb: 'd4d4d4' } },
        },
      },
    });

    // 设置列宽(根据自己需求来设置,我设置的150)
    const columnType = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'G', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U'];
    const columnConfig: any = {};
    for (const k in columnType) {
      columnConfig[k] = 150;
    }
    // 设置行的宽度(从开头到结束)
    // 设置列的高度
    LAY_EXCEL.exportExcel(
      {
        第一页: result,
      },
      `${fileName}.xlsx`,
      'xlsx',
      {
        extend: {
          // extend 中可以指定某个 sheet 的属性,如果不指定 sheet 则所有 sheet 套用同一套属性
          第一页: {
            // 以下配置仅 sheet1 有效
            '!cols': LAY_EXCEL.makeColConfig(columnConfig),
            '!rows': LAY_EXCEL.makeRowConfig({ 0: 30 }),// 行高
          },
        },
      }
    );
  }

图1 图1

图2
图2

格式map如下


	    var table_fmt = {
	      0: 'General',
	      1: '0',
	      2: '0.00',
	      3: '#,##0',
	      4: '#,##0.00',
	      9: '0%',
	      10: '0.00%',
	      11: '0.00E+00',
	      12: '# ?/?',
	      13: '# ??/??',
	      14: 'm/d/yy',
	      15: 'd-mmm-yy',
	      16: 'd-mmm',
	      17: 'mmm-yy',
	      18: 'h:mm AM/PM',
	      19: 'h:mm:ss AM/PM',
	      20: 'h:mm',
	      21: 'h:mm:ss',
	      22: 'm/d/yy h:mm',
	      37: '#,##0 ;(#,##0)',
	      38: '#,##0 ;[Red](#,##0)',
	      39: '#,##0.00;(#,##0.00)',
	      40: '#,##0.00;[Red](#,##0.00)',
	      45: 'mm:ss',
	      46: '[h]:mm:ss',
	      47: 'mmss.0',
	      48: '##0.0E+0',
	      49: '@', // 文本
	      56: '"上午/下午 "hh"時"mm"分"ss"秒 "'
	    }

15、*嵌套表格-数据导出(方案待确认)

原型以及数据结构原型以及数据结构
![在这里插入图片描述](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
在这里插入图片描述

16、a-table

16.1 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 ? '已登记' : '未登记';
      }
    }
  ];

16.2 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;
  }

16.3 a-table 行选中-行样式

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

16.4a-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;
  }
}

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

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

17、ES11 空值合并运算符(Nullish coalescing Operator)

17.1 空值合并操作符(??

空值合并操作符(??)是一个逻辑操作符,当左边的操作数为 null 或 undefined 的时候,返回其右侧操作符,否则返回左侧操作符。

undefined ?? 'foo'  // 'foo'
null ?? 'foo'  // 'foo'
'foo' ?? 'bar' // 'foo'

17.2 逻辑或操作符(||

逻辑或操作符(||),会在左侧操作数为假值时返回右侧操作数,也就是说如果使用 || 来为某些变量设置默认值,可能会出现意料之外的情况。比如 0、‘’、NaN、false:

0 || 1  // 1
0 ?? 1  // 0

'' || 'bar'  // 'bar'
'' ?? 'bar'  // ''

NaN || 1  // 1
NaN ?? 1  // NaN

false || 'bar'  // 'bar'
false ?? 'bar'  // false

18、汉字排序

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

19、数组分组


  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];
    });
  }

20、生成主键

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

21、a-select

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

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

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

21.2 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 : '' // 这里是重点
  }

21.3 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}`)
      })
  }

22、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)
			}
		  })
		})
	  }

23、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);
  }
}

24、vxe

此处只用了model、table的部分功能,详情见vxe-table官方文档

24.1 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');
  }

24.2 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名来进行修改 -> <style></style>
          :row-config="{isHover: true}"   // 鼠标覆盖行变色
          :checkbox-config="{trigger: 'row', highlight: false, range: false}" // 点击行选中,选中时高亮显示,按住ctrl可以拖动选中
          @checkbox-all="selectAllEvent"   //  全选按钮回调
          @checkbox-change="selectChangeEvent" // 选择框 点击回调

24.3 vxe-table合并列

    <vxe-table
      :span-method="mergeRowMethod"
    >
  mergeRowMethod({ row, _rowIndex, column, visibleData }) {
    const fields = ['patName']; // 需要合并的列的字段
    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 };
        }
      }
    }
  }

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

transition-name=""

26、$ c o n f i r m 、 confirm、 confirmmessage提示换行

26.1 ElementUi $message提示换行

   const arr = ['测试一', '测试二', '测试三'];
   (this as any).$message({
     dangerouslyUseHTMLString: true,
     message: arr.join(' <br/> '),
     type: 'info'
   });

26.2 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(' <br/> ')
            }
          },
          []
        );
      }
    });

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

27、Echarts图表转Base64

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

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

使用场景:计算输入框中字符串的width,如果>=输入框的width,则显示tooltip全部文字
场景展示

    <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
    }

29、获取递归组件、嵌套组件的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使用案例:点击链接跳转

30、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();
  }

31、图片点击放大功能

效果图
点击前在这里插入图片描述
点击图片后在这里插入图片描述

1、安装依赖 npm install v-viewer --save
2、配置main.js

import Viewer from 'v-viewer'
import 'viewerjs/dist/viewer.css'
Vue.use(Viewer)
Viewer.setDefaults({
 Options: { 'inline': true, 'button': true, 'navbar': true, 'title': false, 'toolbar': true, 'tooltip': true, 'movable': true, 'zoomable': true, 'rotatable': true, 'scalable': true, 'transition': true, 'fullscreen': true, 'keyboard': true, 'url': 'data-source' }
})

3、应用


  <a-tab-pane
    key="2"
    tab="预览"
    force-render
  >
    <a-icon type="exclamation-circle" />
    图片缩略图,点击图像查看大图
    <viewer
      :images="remakeFileList"
      style="display: flex;"
    >
    <!-- remakeFileList必须是数组!! -->
      <a-row>
        <a-col
          v-for="src in remakeFileList"
          :key="src.filePath"
          :span="8"
        >
          <img
            :src="src.filePath"
            :title="'【'+src.orderName+'】'+src.fileName"
            style="height: 100%;width: 100%;;cursor: pointer;"
          />
        </a-col>
      </a-row>
    </viewer>
  </a-tab-pane>

32、Vue动态组件 component :is的使用

33、iframe实现缓存

33.1、 keep-alive缓存不了iframe界面原因

(1)vue中的keep-alive
【1】原理:Vue 的缓存机制并不是直接存储 DOM 结构,而是将 DOM 节点抽象成了一个个 VNode节点。因此,Vue 的 keep-alive 缓存也是基于 VNode节点 而不是直接存储 DOM 节点。在需要渲染的时候从Vnode渲染到真实DOM上。
【2】参数:Keep-alive 组件提供了 include 和 exclude 两个属性,允许组件有条件的进行缓存。
  include: 字符串或正则表达式。只有匹配的组件会被缓存。
  exclude: 字符串或正则表达式。任何匹配的组件都不会被缓存。
【3】Keep-alive 组件提供了两个生命钩子函数,分别是 activated 和 deactivated 。
  activated :当页面存在缓存的时候执行该函数。
  deactivated :在页面结束时触发该方法,可清除掉滚动方法等缓存。
(2)iframe中keep-alive机制失效原因:iframe页里的内容并不属于节点的信息,所以使用keep-alive依然会重新渲染iframe内的内容。而且iframe每一次渲染就相当于打开一个新的网页窗口,即使把节点保存下来,在渲染时iframe页还是刷新的。

33.2、 解决思路

判断当前页面是普通页面还是iframe页面,让普通页面走route-view,用v-if控制显隐,让iframe走动态组件(component :is),组件指向iframe文件,用v-show控制显隐,通过v-show不销毁组件的方式,来实现缓存iframe的效果

33.3、核心代码

layout文件


    <a-layout-content>
      <!-- 非iframe页面 -->
      <keep-alive :include="cacheViewString">
        <router-view v-if="!isIframe" :key="getRoute" style="height:100%;" />
      </keep-alive>
      <!-- iframe页面 -->
      <div v-show="isIframe">
        <component
          v-for="item in iframeList"
          :key="item"
          is="iframeComponentName"
          v-show="isIframe && $route.path.indexOf(item) > -1"
        />
      </div>
    </a-layout-content>
	// 指向iframe文件
	import iframeComponentName from '@/views/oh/OhSystemIframe.vue';
	
	@Component({
	  components: {
	    iframeComponentName
	  }
	})
	export default class Layout extends Vue {
	  iframeList:any = []; // iframe菜单集合
	  
	  @Watch('$route')
	  onRouteChange(route:any) {
	  	// 根据路由判断当前页面是否是iframe --> 判断规则视情况而定
	    this.renderIframeList(route.path)
	  }
	
	  renderIframeList(path:any) {
	    if (!path.includes('oh_iframe')) return
	    const notExit = this.iframeList.findIndex((item:any) => item === path) === -1
	    if (notExit) {
	      this.iframeList.push(path)
	    }
	  }
	
	  get isIframe() {
	    // iframe标识:当前页面是否是iframe
	    /**
	    注意:判断规则视情况而定
	    **/
	    return this.$route.path.includes('oh_iframe');
	  }
	}

OhSystemIframe文件

<template>
  <div class="peis-body__main">
    <div class="peis-body">
      <iframe
        id="ohIframe"
        :src="iframeSrc"
        frameborder="0"
      ></iframe>
    </div>
  </div>
</template>

以下ts代码为拼接iframeSrc的过程,可忽略,但有一个知识点 给iframe传参:postSession()


<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component({
  name: 'OhSystemIframe',
  components: {},
})
export default class OhSystemIframe extends Vue {
  iframeSrc: any = '';
  mounted() {
    this.renderIframeSrc();
  }

  renderIframeSrc() {
    /* 拼接iframe地址 */
    const sysFlag = '/oh/#'; // 职业病系统标识
    const query = '?systemFrom=peis'; // 增加体检系统挂载标识
    let targetUrl;
    // 本地环境
    if (window.location.href.includes('localhost')) {
      const ohHost = 'http://localhost:9528/#'; // 本地职业病项目地址 -->确保本地已启动职业病的前端项目
      targetUrl = ohHost + window.location.href.split('/#/oh_iframe')[1];
    } else {
      // 测试环境
      targetUrl = window.location.href.split('/peis/#/oh_iframe/')[0] + sysFlag + this.$route.path.split('/oh_iframe')[1];
    }
    this.iframeSrc = targetUrl + query;
    // this.postSession(); // 给职业病iframe传参 暂时用不到
  }

  postSession() {
  	// 传参方式
    const data = {
      selectedSystem: { deptCode: '72', deptId: '91', deptName: '健康管理中心', orgId: '10033', sysytemId: '100000', systemName: '职业病', url: 'oh/', userSysId: '5391553094598134274', userSysName: '职业病体检主任' },
    };
    this.$nextTick(() => {
      const iframe: any = document.getElementById('ohIframe');
      iframe.onload = () => {
        iframe.contentWindow.postMessage(JSON.stringify(data), '*');
      };
    });
  }
  // 接收参数方式
  //  window.addEventListener('message', (data: any) => {
      /* 挂载系统时 接收传入的参数 */
      // data
  //  });
}
</script>

34、vue-quill-editor富文本编辑器

这篇文章写的详细 快去看啊!快去看啊!

35、ant-design-vue modal可拖动

新建文件modalDrag.js注册拖动指令v-drag-modal

import Vue from 'vue';

/* 调用示例
<a-modal v-drag-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
  <img alt="example" style="width: 100%" :src="previewImage" />
</a-modal> */

// v-drag-modal: 弹窗拖拽
Vue.directive('drag-modal', (el, binding, vnode, oldVnode) => {
  Vue.nextTick(() => {
    const { visible, destroyOnClose } = vnode.componentInstance;
    if (!visible) return;
    const isThemeModal = el.classList.contains('grid-theme');
    const dialogHeaderEl = isThemeModal ? el.querySelector('.ant-tabs-bar') : el.querySelector('.ant-modal-header');
    const dragDom = isThemeModal ? el.querySelector('.ant-modal') : el.querySelector('.ant-modal');
    // dialogHeaderEl.style.cursor = 'move';
    dialogHeaderEl.style.cssText += ';cursor:move;';
    // dragDom.style.cssText += ';top:0px;'

    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = (function() {
      if (window.document.currentStyle) {
        return (dom, attr) => dom.currentStyle[attr];
      } else {
        return (dom, attr) => getComputedStyle(dom, false)[attr];
      }
    })();
    dialogHeaderEl.onmousedown = e => {
      // 禁止选中文字,防止拖拽时弹框粘鼠标
      document.onselectstart = function() {
        return false;
      };
      // 鼠标按下,计算当前元素距离可视区的距离
      const disX = e.clientX - dialogHeaderEl.offsetLeft;
      const disY = e.clientY - dialogHeaderEl.offsetTop;

      const screenWidth = document.body.clientWidth; // body当前宽度
      const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取)
      const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
      const dragDomheight = dragDom.offsetHeight; // 对话框高度

      const minDragDomLeft = dragDom.offsetLeft;
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth - (isThemeModal ? 10 : 0);

      const minDragDomTop = dragDom.offsetTop;
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight - (isThemeModal ? 10 : 0);
      // 获取到的值带px 正则匹配替换
      let styL = sty(dragDom, 'left');
      let styT = sty(dragDom, 'top');

      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
      if (styL.includes('%')) {
        // eslint-disable-next-line no-useless-escape
        styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
        // eslint-disable-next-line no-useless-escape
        styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
      } else {
        styL = +styL.replace(/\px/g, '');
        styT = +styT.replace(/\px/g, '');
      }
      // dialogHeaderEl 此处直接使用el 防止鼠标移动太快脱离范围,导致无法拖动
      el.onmousemove = function(e) {
        // 通过事件委托,计算移动的距离
        let left = e.clientX - disX;
        let top = e.clientY - disY;
        // 边界处理
        if (-left > minDragDomLeft) {
          left = -minDragDomLeft;
        } else if (left > maxDragDomLeft) {
          left = maxDragDomLeft;
        }

        if (-top > minDragDomTop) {
          top = -minDragDomTop;
        } else if (top > maxDragDomTop) {
          top = maxDragDomTop;
        }
        // 移动当前元素
        dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
      };

      el.onmouseup = function(e) {
        el.onmousemove = null;
        dialogHeaderEl.onmouseup = null;
      };

      document.onmouseup = function(e) {
        el.onmousemove = null;
        dialogHeaderEl.onmouseup = null;
        // 在抬起鼠标之后,取消禁用选择文字
        document.onselectstart = function() {
          return true;
        };
      };
    };
  });
});

在main.js中注册全局

import '@/utils/modalDrag.js

    <a-modal
      v-drag-modal
      :get-container="() => $refs.aff"
      :title="contrastDetailFlag?'对照详情':'项目对照'"
      :visible="true"
      append-to-body
      :width="contrastDetailFlag?'55vw !important':'80vw !important'"
      @cancel="closeDialog"
    >
    </a-modal>

效果图

36、组件库monorepo架构

monorepo架构中各模块独立方便管理,以一下测试项目为例,packages中的base存放组件,example 用于测试。此测试项目有三个组件。

1、目录结构

.
├── CHANGELOG.md  更新记录
├── README.md  项目说明文档
├── package.json  项目配置文档
├── packages  多模块配置目录
│   ├── base  模块-base
│   │   ├── dist  base的打包产出
│   │   ├── package.json  base的项目配置
│   │   ├── rollup.config.js  base的打包配置项
│   │   └── src  base的源码
│   └── example 配套的测试模块
│       ├── README.md 
│       ├── babel.config.js
│       ├── jsconfig.json
│       ├── package.json
│       ├── public
│       ├── src
│       └── yarn.lock
├── pnpm-lock.yaml  项目的依赖管理文件
└── pnpm-workspace.yaml  monorepo的配置文件

项目Git地址

1、pnpm install 安装依赖
2 、执行pnpm dev 与 pnpm serve在这里插入图片描述
3、项目预览在这里插入图片描述

语法以及架构自己研究,这套架构方便组件维护,能解决一个组件一套项目不方便维护的问题,组件引用也方便:
import { ModuleA } from 'monorepo-test';
重点来了 研究~自己研究去

37、数字输入框

1.隐藏输入框尾部的箭头

<style scoped>
>>> input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
  -webkit-appearance: none !important;
}
</style>

2.只能输入正数负数以及一个小数点

<!-- 无法输入汉字、字母、符号等非数字 -->
<a-input
  v-model="aaa"
  type="number"
  onmousewheel="return false"
  onkeypress="return(/[\d\.]/.test(String.fromCharCode(event.keyCode)))"
/>

3.只能输入大于等于0的正整数

<a-input
   type="number"
   style="width: 63px;"
   v-model="reRegCheckTime"
   :min="0"
   oninput="value=value.replace('.', '',).replace('-', '',)"
 />

4.只能输入大于0的正整数

<a-input
   type="number"
   style="width: 63px;"
   v-model="reRegCheckTime"
   :min="0"
   oninput="value=value.replace('.', '',).replace('-', '',).replace(/^0|[^0-9]/g,'')"
 />

38、a-tree右键菜单-屏蔽默认事件

<a-tree id="peis_org_group_tree_datas">
	<!-- 右键菜单 -->
   <template #title="{ key: treeKey, text, ...node }">
     <a-dropdown
       v-if="node.dataRef.attributes ==='group'"
       :trigger="['contextmenu']"
     >
       <span>{{ text }}</span>
       <template #overlay>
         <a-menu @click="hideTreeNode(node.dataRef)">
           <a-menu-item key="1"><a-icon type="eye-invisible" />隐藏分组</a-menu-item>
         </a-menu>
       </template>
     </a-dropdown>
     <span v-else>
       {{ text }}
     </span>
   </template>
 </a-tree>
  mounted() {
    const handlerDom = document.getElementById('peis_org_group_tree_datas');
    handlerDom.oncontextmenu = function (e) {
      e.stopPropagation()
    }
  },

998、$env:NODE_OPTIONS=“–max-old-space-size=514096”

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

vscode配置
安装插件
Prettier - Code formatter
ESLint
ESLint Chinese Rules
Monokai Dimmed+Vibrant

eslint

{
  // vscode默认启用了根据文件类型自动设置tabsize的选项
  "editor.detectIndentation": false,
  // html格式化-对属性进行换行。
  "html.format.wrapAttributes": "force-expand-multiline",
  // 重新设定tabsize
  "editor.tabSize": 2,
  //  #让prettier使用eslint的代码格式进行校验
  //  #去掉代码结尾的分号
  "prettier.semi": false,
  "prettier.trailingComma": "none",
  //  #使用带引号替代双引号
  "prettier.singleQuote": true,
  //  #让函数(名)和后面的括号之间加个空格
  "javascript.format.insertSpaceBeforeFunctionParenthesis": false,
  "vetur.format.defaultFormatterOptions": {
    "wrap_attributes": "force-aligned",
    "prettier": {
      "semi": false, // 格式化时不加分号
      "singleQuote": true, // 格式化时使用单引号
      "trailingComma": "none", // 格式化时末尾不添加逗号
    },
    "vetur.format.defaultFormatter.html": "prettier", //格式化.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.prettier": "explicit"
  },
  "editor.fontLigatures": false,
  /* 自定义主题颜色 */
  "editor.tokenColorCustomizations": {
    "[Monokai Dimmed+Vibrant]": {
      "keywords": "#ff00ff", //关键字
      "types": "#42b983", //类型定义
      "numbers": "#00e1ff", //数字
      "strings": "#FFFF00", // 字符串的颜色,
      // "variables": "#9814ef", //变量
      // "comments": "#beb9b9", //注释
      // "functions": "#f10070f5", //函数
      "textMateRules": [
        {
          "scope": "support.type.property-name.json", //JSON -> key
          "settings": {
            "foreground": "#00ff40",
            // "fontStyle": "italic bold "
          }
        },
        {
          "scope": "string.quoted.double.json", //JSON->Value
          "settings": {
            "foreground": "#ff00f2",
            // "fontStyle": "italic bold"
          }
        },
        {
          "scope": "meta.object.member.js", //声明的变量
          "settings": {
            "foreground": "#ffffff",
            // "fontStyle": "italic bold "
          }
        },
        {
          "scope": "meta.block.js",
          "settings": {
            "foreground": "#00fff2", // 引用的变量名
            // "fontStyle": "italic bold "
          }
        },
        {
          "scope": "storage.type.js",
          "settings": {
            // "foreground": "#2e09ff",
            "fontStyle": " bold "
          }
        },
        {
          "scope": "keyword.control", //if ,else, try 等控制符
          "settings": {
            "foreground": "#ff00dd",
            "fontStyle": "italic bold "
          }
        },
        {
          "scope": "entity.name.tag", // html标签
          "settings": {
            // "foreground": "#ff00f2",
            "fontStyle": "bold italic"
          }
        },
        {
          "scope": "entity.other.attribute-name", // html标签的属性
          "settings": {
            // "foreground": "#0066ff",
            "fontStyle": "bold italic"
          }
        },
        {
          "scope": "variable.other.object",
          "settings": {
            "foreground": "#0099ff",
            "fontStyle": ""
          }
        },
        {
          "scope": "keyword.operator", // 运算符
          "settings": {
            "foreground": "#ff0000",
            // "fontStyle": "bold",
          }
        }
      ]
    }
  },
  "[vue]": {
    "editor.defaultFormatter": "octref.vetur"
  },
  "[jsonc]": {
    "editor.defaultFormatter": "vscode.json-language-features"
  },
  "[html]": {
    "editor.defaultFormatter": "vscode.html-language-features"
  },
  "[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": "#0f0f0f", // 活动选项卡的背景色
    // "activityBar.background": "#0f0f0f", //活动栏背景色
    // "sideBar.background": "#0f0f0f", //侧边栏背景色
    // "activityBar.foreground": "#23f8c8", //活动栏前景色(例如用于图标)
    // "editor.background": "#0f0f0f", //编辑器背景颜色
    // "editor.foreground":"#ff0000", 	//编辑器默认前景色
    // "editor.findMatchBackground":"#f83123", 	//当前搜索匹配项的颜色
    // "editor.findMatchHighlightBackground":"#43ac93", 	//其他搜索匹配项的颜色
    // "editor.lineHighlightBackground":"#ff0000", 	//光标所在行高亮文本的背景颜色
    "editor.selectionBackground": "#888888", //编辑器所选内容的颜色
    // "editor.selectionHighlightBackground":"#ff0000", 	//与所选内容具有相同内容的区域颜色
    // "editor.rangeHighlightBackground":"#ff0000", 	//突出显示范围的背景颜色,例如 "Quick Open" 和“查找”功能
    // "editorBracketMatch.background":"#edfc6a98", 	//匹配括号的背景色
    "editorCursor.foreground": "#00ff15", //编辑器光标颜色
    // "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" // 活动组中非活动选项卡的前景色
  },
  "security.workspace.trust.untrustedFiles": "open",
  "git.autofetch": true,
  "explorer.confirmDelete": false,
  "[css]": {
    "editor.defaultFormatter": "vscode.css-language-features"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "terminal.integrated.profiles.windows": {
    "PowerShell": {
      "source": "PowerShell",
      "icon": "terminal-powershell"
    },
    "Command Prompt": {
      "path": [
        "${env:windir}\\Sysnative\\cmd.exe",
        "${env:windir}\\System32\\cmd.exe"
      ],
      "args": [],
      "icon": "terminal-cmd"
    },
    "Git Bash": {
      "source": "Git Bash"
    },
    "Windows PowerShell": {
      "path": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
    }
  },
  "eslint.validate": [
    "vue",
    "javascript",
    "javascriptvue",
  ],
  // "eslint.autoFixOnSave": true,
  "terminal.integrated.defaultProfile.windows": "PowerShell",
  "terminal.integrated.fontFamily": "MesloLGM Nerd Font",
  "javascript.updateImportsOnFileMove.enabled": "always",
  "git.confirmSync": false,
  "eslint.format.enable": true,
  "editor.defaultFormatter": "dbaeumer.vscode-eslint",
  "gitlens.graph.minimap.additionalTypes": [
    "stashes",
    "remoteBranches",
    "tags",
    "localBranches"
  ],
  "workbench.editorAssociations": {
    "*.dat": "default"
  },
  "editor.fontSize": 15.5,
  "sonarlint.rules": {
    "typescript:S3776": {
      "level": "off"
    }
  },
  "auto-close-tag.disableOnLanguage": [],
  "[dotenv]": {
    "editor.defaultFormatter": "foxundermoon.shell-format"
  },
  "gitlens.ai.experimental.provider": "anthropic",
  "gitlens.ai.experimental.openai.model": "gpt-4-turbo-preview",
  "gitlens.ai.experimental.anthropic.model": "claude-3-opus-20240229",
  "workbench.iconTheme": "material-icon-theme",
  "editor.minimap.showSlider": "always",
  "editor.minimap.size": "fill",
  "[markdown]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "vetur.completion.scaffoldSnippetSources": {
    "workspace": "💼",
    "user": "🗒️",
    "vetur": "✌"
  },
  "vetur.format.defaultFormatter.js": "vscode-typescript",
  "vetur.format.defaultFormatter.html": "js-beautify-html",
  "settingsSync.ignoredExtensions": [
    "shalldie.background"
  ],
  "background.enabled": false,
  "background.useFront": false,
  "editor.guides.bracketPairs": true,
  "powermode.presets": "exploding-rift",
  "powermode.explosions.backgroundMode": "image",
  "powermode.combo.threshold": 100,
  "tabnine.experimentalAutoImports": true,
  "workbench.colorTheme": "Monokai Dimmed+Vibrant",
  "git.openRepositoryInParentFolders": "always",
  "editor.gotoLocation.multipleDefinitions": "goto",
  "diffEditor.ignoreTrimWhitespace": false
}

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']
};

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值