前端工作过程遇到的问题总结(八)

 

目录

前端实现文件下载功能

前端实现图片上传功能

element ui 表单验证不通过

this.$nextTick(() => {})的作用

JavaScript 判断对象中是否有某属性的常用方法

JS根据身份证号码算年龄

element ui的表格鼠标定义右键菜单

vue 操作数组的变异方法和非变异方法

vue使用lodash

javascript页面刷新的几种方法

vue watch对象内的属性

通过window.sessionStorage.getItem获取的值,一定要 通过JSON.parse()转成对象

vue项目中,定义并使用 全局变量,全局函数

重构项目可能出现的问题


前端实现文件下载功能

原文文章:前端实现文件下载功能

1.最简单的,有文件的地址,直接使用a标签实现下载:

<a href="/user/test/xxxx.txt" download="文件名.txt">点击下载</a>

 但是有个情况,比如txt,png,jpg等这些浏览器支持直接打开的文件是不会执行下载任务的,而是会直接打开文件,这个时候就需要给a标签添加一个属性“download”;

2. window.open()方法,后端提供下载接口:

html:

<button type="button" id="btn1">window.open()方法下载</button>

js:

var $eleBtn1 = $("#btn1");
//已知一个下载文件的后端接口:
https://codeload.github.com/douban/douban-client/legacy.zip/master
//方法一:window.open()
$eleBtn1.click(function(){
	var url = "https://codeload.github.com/douban/douban-client/legacy.zip/master";
	window.open(url);
});

3.通过form表单提交的方式:

html:

<button type="button" id="btn2">form方法下载</button>

js:

var $eleBtn2 = $("#btn2");
//已知一个下载文件的后端接口:
https://codeload.github.com/douban/douban-client/legacy.zip/master
$eleBtn2.click(function(){
	var $eleForm = $("<form method='get'></form>");

	$eleForm.attr("action","https://codeload.github.com/douban/douban-client/legacy.zip/master");

	$(document.body).append($eleForm);

	//提交表单,实现下载
	$eleForm.submit();
});

还有几个参考文章:

我在项目中使用的是第一种方法,顺便把代码贴出来(表格的点击事件):

 handleDownload (index,row) {   // row为当前行的数据
    let cV = row.cV;
    let applyUrl = this.$sourcePrefix + cV; //this.$sourcePrefix为资源前缀
    if(cV!='' && cV !=undefined) {
      let url = applyUrl;
      let link = document.createElement('a');
      link.style.display = 'none';
      link.href = url;
      link.setAttribute('download', cV);
      link.setAttribute('target', "_blank");
      document.body.appendChild(link);
      link.click();
    } else {

    }
}

前端实现图片上传功能

直接上代码:最好是把组件的props、data、computed、watch、mounted、methods来看参数、函数的作用,这样更容易明白

  • 头像上传:
<template>
  <div class="upload-avatar-component">
    <div
      class="preview"
      style="height:100%"
    >
      <div>
        <img :src="fileSrc" alt="UploadImg">
      </div>
    </div>
    <div class="button-upload">
      <el-upload
        class="upload-img"
        :action="theUrl"
        :before-upload="BeforeUpload"
        ref="upload"
        :show-file-list="false"
      >编辑</el-upload>
    </div>
  </div>
</template>

<script>
import * as connector from '@api/interfaceList'
import { post } from '@api/api.request'
export default {
  name: "uploadImg",
  model: {
    prop: "uploadedFile",
    event: "updateFile"
  },
  data() {
    return {
      imgFile: null,
      fileSrc: "",
      errorNotify: {
        ImgFormatError: null,
        ImgExceededSize: null,
        ImgDimensionError: null,
        ImgDimensionRangeError: null
      },
      iconPrefix: ""
    };
  },
  props: {
    /*validateEvent: {
        type: Boolean,
        default: true
      },*/
    /*  showPreview:{
        type:Boolean,
        default:true
      }, */
    uploadedFile: {},
    disabled: {
      type: Boolean,
      default: false
    },
    defaultImg: {
      //自定义默认未上传时显示图片
      default: require("@images/home/default_avatar.png")
    },
    itemName: {
      //上传图片对应表单项名称
      type: String,
      required: true
    },
    /*itemProp:{//接收父组件传来的表单prop值,用于在父组件中应用该组件多次时,进行各子实例的区分

      },*/
    imgDisplayWidth: {
      //图片上传后的预览宽度
      type: Number,
      default: 74
    },
    /*defaultImgUrl:{
        type:String,
        required: true
      },*/
    imgDisplayHeight: {
      //图片上传后的预览高度
      type: Number,
      default: 75
    },
    itemMessage: {
      //表单项文字信息
      type: Object,
      default: function() {
        return {};
      }
      /*default:function () {
          return {
            instruction:'请提交有效期内的教师资格证照片,需确保头像和文字清晰',//String/html标记语言(换行用<br/>) ;showLimitRules为false时,不受影响,显示
            extraDescribe:['亦可提交高等学历证,专业证书等其他资格证明']//Array 上传要求的额外描述,【showLimitRules为false时不显示】
          }
        }*/
    },
    itemRule: {
      /*限制条件*/
      type: Object,
      /*required: true,*/
      validator: function(value) {
        // 宽度范围与高度范围中的最大最小的数值需符合逻辑
        if (
          value.widthRange &&
          value.widthRange.minWidth &&
          value.widthRange.maxWidth
        ) {
          return value.widthRange.minWidth < value.widthRange.maxWidth;
        } else if (
          value.heightRange &&
          value.heightRange.minHeight &&
          value.heightRange.maxHeight
        ) {
          return value.heightRange.minHeight < value.heightRange.maxHeight;
        } else {
          return true;
        }
      },
      default: function() {
        return {
          format: ["jpg", "jpeg", "png"], //Array 文件格式
          maxSize: 1024, //Number[kb] 文件大小
          width: undefined, //Number[px] 图片宽度
          height: undefined, //Number[px] 图片高度
          widthRange: {
            minWidth: undefined, //Number[px] 图片最小宽度
            maxWidth: undefined //Number[px] 图片最大宽度
          },
          heightRange: {
            minHeight: undefined, //Number[px] 图片最小高度
            maxHeight: undefined //Number[px] 图片最大高度
          }
        };
      }
    },
    showLimitRules: {
      //是否展示根据限制条件自动生成的规则提示语qualification
      type: Boolean,
      default: true
    },
    uploadUrl: {
      type: String
    }
  },
  watch: {
    /* 监听上传之后返回的值 */
    uploadedFile: function(newVal) {
      if (!newVal) {
        this.fileSrc = this.defaultImg;
        this.dispatch("ElFormItem", "el.form.change", [""]);
        return false;
      }
      if (newVal.indexOf(this.iconPrefix) > 0) {
        this.fileSrc = newVal;
      } else {
        this.fileSrc = this.iconPrefix + "/" + newVal;
      }
    },
     // 监听iconPrefix变化
    iconPrefix_C (val) {
      if(val) {
        /* 判断上传之后返回的值是否带有前缀 */
        let urlPattern = /[a-zA-z]+:\/\/[^\s]*/;
        let uploadedFile = String(this.uploadedFile || "");
        if (!uploadedFile) {
          this.fileSrc = this.defaultImg;
        } else if (urlPattern.test(uploadedFile)) {
          this.fileSrc = this.uploadedFile;
        } else {
          this.fileSrc = this.iconPrefix +"/" + this.uploadedFile;
        }
      } else {
        this.getsourcePrefix();
      }
    }
  },
  mounted: function() {
    /*this.$notify.config({
        top: 130,
        duration: 0
      });*/
    /* 获取资源前缀 */
    this.getsourcePrefix();
  },
  computed: {
    /*validateState() {
        return this.elFormItem ? this.elFormItem.validateState : '';
      },*/
    iconPrefix_C () {
      return this.iconPrefix;
    },
    /* 设置上传图片的地址 */
    theUrl: function() {
      if (!this.uploadUrl || this.uploadUrl === "") {
        return this.$uploadUrl; //todo:默认的上传地址(注册于Vue.prototype)
      } else {
        return this.uploadUrl;
      }
    },
    /* 规则提示语的 */
    qualification: function() {
      let qualification = [];
      if (this.itemRule) {
        if (this.itemRule.maxSize) {
          let maxSize =
            this.itemRule.maxSize < 1024
              ? this.itemRule.maxSize + "Kb"
              : this.itemRule.maxSize / 1024 + "Mb";
          let sizeLimit = "图片大小不超过" + maxSize;
          qualification.push(sizeLimit);
        }
        if (this.itemRule.format) {
          let formatLimit = "支持";
          for (let i = 0; i < this.itemRule.format.length; i++) {
            if (i !== this.itemRule.format.length - 1) {
              formatLimit = formatLimit + this.itemRule.format[i] + "/";
            } else {
              formatLimit = formatLimit + this.itemRule.format[i];
            }
          }
          formatLimit = formatLimit + "格式的图片";
          qualification.push(formatLimit);
        }
        if (this.itemRule.width || this.itemRule.height) {
          let widthLimit = this.itemRule.width
            ? this.itemRule.width + "px(像素)"
            : "不限";
          let heightLimit = this.itemRule.height
            ? this.itemRule.height + "px(像素)"
            : "不限";
          let dimensionLimit =
            "图片尺寸为" + "宽:" + widthLimit + ";高:" + heightLimit;
          qualification.push(dimensionLimit);
        }
        /*if(!this.itemRule.width && !this.itemRule.height){//只有穿参中没有宽或高固定限制,才进行宽度或高度范围的条件校验
            //此处为自动生成的宽度、高度范围校验的描述文字【现在不想写】
          }*/
      }
      if (this.itemMessage.extraDescribe) {
        qualification = qualification.concat(this.itemMessage.extraDescribe);
      }
      return qualification;
    }
    /*    // 设置请求头
      headers() {
        return {
          // 设置Content-Type类型为multipart/form-data
          'ContentType': 'multipart/form-data'
        }
      } */
  },
  methods: {
    /* 获取配置的地址 */
    getsourcePrefix() {
      this.$http.hosCommon.fetchConfig().then(data => {
        this.iconPrefix = "http://" + data.qiniu;
      });
    },
    /* 警告 */
    notifyWarning(title, message, duration) {
      let that = this;
      setTimeout(() => {
        that.$notify.warning({
          /*this.errorNotify['ImgFormatError'] = */
          title: title,
          message: message,
          duration: duration
        });
      }, 20); //elementUI组件bug(that.$notify的触发时间接近的提示窗叠在一起bug),需要添加20ms的延时触发(0,1的延时触发均不能解决)
    },
    /* 上传之前的验证操作 */
    BeforeUpload(file) {
      /*this.closeError();*/
      if (!this.itemRule) {
        this.customUpload(file);
        return false;
      }
      let validateResult = this.validate(file);
      /*alert(validateResult);*/
      /* 在客户端读取文件 */
      let reader = new FileReader();
      let that = this;
      //这是一个回调函数,需要设置reader.readAsDataURL(file),并在该函数执行结束后才会触发这个回调
      reader.onload = function(e) {
        let data = e.target.result;
        /*that.fileSrc = data;//本地加载展示图片*/
        //加载图片获取图片真实宽度和高度用于校验
        let image = new Image();
        /* 这是个回调,在图像加载完毕后调用 */
        image.onload = function() {
          /*宽高范围校验*/
          if (!that.itemRule.width && !that.itemRule.height) {
            let widthRangeInconform = false;
            let heightRangeInconform = false;
            if (that.itemRule.widthRange) {
              widthRangeInconform = that.itemRule.widthRange.maxWidth
                ? image.width > that.itemRule.widthRange.maxWidth
                : false;
              if (!widthRangeInconform) {
                widthRangeInconform = that.itemRule.widthRange.minWidth
                  ? image.width < that.itemRule.widthRange.minWidth
                  : false;
              }
            } else if (that.itemRule.heightRange) {
              heightRangeInconform = that.itemRule.heightRange.maxHeight
                ? image.height > that.itemRule.heightRange.maxHeight
                : false;
              if (!heightRangeInconform) {
                heightRangeInconform = that.itemRule.heightRange.minHeight
                  ? image.height < that.itemRule.heightRange.minHeight
                  : false;
              }
            } else {
            }
            if (widthRangeInconform || heightRangeInconform) {
              that.notifyWarning(
                that.itemName + "图片长宽尺寸不符合要求!",
                "请重新上传符合宽、高规定的图片",
                5000
              );
              file = null;
              that.imgFile = null;
              that.noticeParent(undefined);
              //清除展示
              that.fileSrc = that.defaultImg;
            } else if (validateResult) {
              //所有校验均通过
              that.imgFile = file;
              that.customUpload(file);
              /*console.log('进入了手动上传')*/
            } else {
              //宽高校验通过,但格式或大小校验不通过
              file = null;
              this.imgFile = null;
              that.noticeParent(undefined);
              //清除展示
              that.fileSrc = that.defaultImg;
            }
          } else {
            /*固定宽/高校验(校验通过为false)*/
            let widthInconform = that.itemRule.width
              ? image.width !== that.itemRule.width
              : false;
            let heightInconform = that.itemRule.height
              ? image.height !== that.itemRule.height
              : false;
            if (widthInconform || heightInconform) {
              //宽高校验未通过
              let widthLimit = that.itemRule.width
                ? that.itemRule.width + "px"
                : "不限";
              let heightLimit = that.itemRule.height
                ? that.itemRule.height + "px"
                : "不限";
              that.notifyWarning(
                that.itemName + "图片长宽尺寸不符合要求!",
                "请重新上传:宽度为 " +
                  widthLimit +
                  "*" +
                  heightLimit +
                  "的图片",
                5000
              );
              file = null;
              that.imgFile = null;
              that.noticeParent(undefined);
              //清除展示
              that.fileSrc = that.defaultImg;
            } else if (validateResult) {
              //所有校验均通过
              that.imgFile = file;
              /*that.$refs['upload'].post(file);//采用手动上传*/
              that.customUpload(file);
              console.log("进入了手动上传");
            } else {
              //宽高校验通过,但格式或大小校验不通过
              file = null;
              this.imgFile = null;
              that.noticeParent(undefined);
              //清除展示
              that.fileSrc = that.defaultImg;
            }
          }
        };
        image.src = data;
      };
      // 以DataURL的形式读取文件:
      reader.readAsDataURL(file);

      return false; //采用手动上传
    },
    /*   校验格式与文件大小 */
    validate(file) {
      let result = true; //校验通过旗标
      if (this.itemRule) {
        if (this.itemRule.format.length) {
          const _file_format = file.name
            .split(".")
            .pop()
            .toLocaleLowerCase();
          const checked = this.itemRule.format.some(
            item => item.toLocaleLowerCase() === _file_format
          );
          if (!checked) {
            this.handleFormatError();
            result = false;
          }
        }
        // check maxSize
        if (this.itemRule.maxSize) {
          if (file.size > this.itemRule.maxSize * 1024) {
            this.handleExceedSize();
            result = false;
          }
        }
      }
      return result;
    },
    /* 上传格式报错的函数处理 */
    handleFormatError() {
      this.notifyWarning(
        this.itemName + "图片格式错误!",
        "请重新上传" + this.itemRule.format + "格式的图片",
        5000
      );
    },
    /* 上传图片大小报错的函数处理 */
    handleExceedSize() {
      let maxSize =
        this.itemRule.maxSize < 1024
          ? this.itemRule.maxSize + "Kb"
          : this.itemRule.maxSize / 1024 + "Mb";
      this.notifyWarning(
        this.itemName + "图片体积过大!",
        "请重新上传不超过" + maxSize + "的图片",
        5000
      );
    },
    /* 手动上传的函数处理 */
    customUpload(file) {
      this.progressObj = {
        status: "text",
        progressPercentage: 0
      };
      let that = this;
      let onUploadProgress = function(progressEvent) {
        let progress = Number(
          ((progressEvent.loaded / progressEvent.total) * 100).toFixed(2)
        );
        that.progressObj.progressPercentage =
          progress >= 99.99 ? 99.99 : progress;
      };
      let reqConfig = {
        headers: {
          fileType: "file",
          "Content-Type": "multipart/form-data"
        },
        onUploadProgress: onUploadProgress
      };
      let rawFile = new FormData();
      rawFile.append("a", file);
      this.$http.hosCommon.putFile(this.theUrl, rawFile, reqConfig.headers).then(data => {
        this.noticeParent(data[0]);
        this.fileSrc = "http://w.y7edu.com/" + data[0];
        /*  console.log(this.fileSrc); */
        setTimeout(() => {
          that.progressObj = {
            status: "success",
            progressPercentage: 100
          };
        }, 150);
       
      })
      .catch(error => {
        that.progressObj.status = "exception";
      });
    },
    /* 同步父、子组件上传文件状态【null/Object[file]】 */
    noticeParent(msg) {
      /*let notice = {itemProp:this.itemProp,uploadedImg:msg};*/
      this.$emit("updateFile", msg);
      // this.dispatch("ElFormItem", "el.form.change", [msg]);
    },
    /* 使用了elFormItem组件作为父组件,该图片上传组件的v-model绑定的数据更新时,通知elFormItem组件 */
    dispatch(componentName, eventName, params) {
      let parent = this.$parent || this.$root;
      let name = parent.$options.componentName;

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;

        if (parent) {
          name = parent.$options.componentName;
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    }
  }
};
</script>
<style lang="less">
.upload-avatar-component {
  height: 110px;
  position: relative;
  display: inline-block;
  .el-upload {
    width: 100%;
  }
  .button-upload {
    position: absolute;
    top: 0;
    left: 0;
    height: 110px;
    line-height: 110px;
    width: 100%;
    .upload-img {
      display: none;
      border-radius: 50%;
      overflow: hidden;
      .el-upload--text { 
        height: 40px;
        line-height: 40px;
        border-radius: 5px;
        text-align: center;
        font-size: 18px;
        color:  #00b8c4;
        background-size: 100% 100%;
        background-position: center;
        &:hover{
         opacity: 0.85;
        }
      }
    }
    &:hover {
      .upload-img {
        display: block;
        overflow: hidden;
        background-color: #333;
        opacity: 0.85;
        border-radius: 50%;
      }
    }
  }
}
.preview {
  height: 110px;
  width: 100%;
  line-height: 110px;
  text-align: center;
  img {
    height: 110px;
    width: 110px;
    border-radius: 50%
  }
}
</style>


 使用:

<avatar
   :showPreview="false"
   :imgDisplayWidth="uploadConfig.previewWidth"
   :imgDisplayHeight="uploadConfig.previewHeight"
   :itemRule="uploadConfig.imgRequirement"
   v-model="userInfo.avatarUrl"
   :itemName="uploadConfig.label"
   @updateFile="updateFile"
></avatar>

 配置(data中定义):

uploadConfig: {
        previewWidth: 110,
        previewHeight: 110,
        label: "",
        itemMessage: {
          instruction: "请提交你的头像", //String/html标记语言(换行用<br/>) 表单上传提示语/提示语+上传文件规则
          extraDescribe: []
        },
        imgRequirement: {
          format: ["jpg", "jpeg", "png"]
          /*  width:1920,//Number[px] 图片宽度
          height:768,//Number[px] 图片高度
          maxSize:4096,//Number[kb] 文件大小 */
        }
 }

方法:

// 上传头像的事件
    updateFile (msg) {
      window.sessionStorage.setItem("avatar", msg);
      let params = {
        'icon': msg
      };
      this.$http.hosCommon.editUserInfo(params).then(data => {
        this.$message({
          message: '编辑用户头像成功',
          type: 'success'
        });
        this.$store.commit("SET_AVATAR", msg);
      })
    }

 注意:this.$http.hosCommon.editUserInfo是我封装的方法

  • 图片上传:
<template>
  <div class="upload-img-component">
    <!-- <div
      class="preview"
      :style="{width:imgDisplayWidth+'px',height:imgDisplayHeight + 'px'}"
      style="height:100%"
    >
      <div>
        <img :src="fileSrc" alt="UploadImg">
      </div>
    </div> -->
    <div class="button-upload">
      <el-upload
        class="upload-img"
        v-if="fileSrc === defaultImg"
        :action="theUrl"
        :before-upload="BeforeUpload"
        ref="upload"
        :show-file-list="false"
      >点击上传</el-upload>
      <el-upload
        class="uploaded-img"
        v-if="fileSrc != defaultImg"
        :action="theUrl"
        :before-upload="BeforeUpload"
        ref="upload"
        :show-file-list="false"
      >重新上传</el-upload>
    </div>
  </div>
</template>

<script>
import * as connector from '@api/interfaceList'
import { post } from '@api/api.request'
export default {
  name: "uploadImg",
  model: {
    prop: "uploadedFile",
    event: "updateFile"
  },
  data() {
    return {
      imgFile: null,
      fileSrc: "",
      errorNotify: {
        ImgFormatError: null,
        ImgExceededSize: null,
        ImgDimensionError: null,
        ImgDimensionRangeError: null
      },
      iconPrefix: ""
    };
  },
  props: {
    /*validateEvent: {
        type: Boolean,
        default: true
      },*/
    /*  showPreview:{
        type:Boolean,
        default:true
      }, */
    uploadedFile: {},
    disabled: {
      type: Boolean,
      default: false
    },
    defaultImg: {
      //自定义默认未上传时显示图片
      default: require("@images/login/bg_zyzs.png")
    },
    itemName: {
      //上传图片对应表单项名称
      type: String,
      required: true
    },
    /*itemProp:{//接收父组件传来的表单prop值,用于在父组件中应用该组件多次时,进行各子实例的区分

      },*/
    imgDisplayWidth: {
      //图片上传后的预览宽度
      type: Number,
      default: 74
    },
    /*defaultImgUrl:{
        type:String,
        required: true
      },*/
    imgDisplayHeight: {
      //图片上传后的预览高度
      type: Number,
      default: 75
    },
    itemMessage: {
      //表单项文字信息
      type: Object,
      default: function() {
        return {};
      }
      /*default:function () {
          return {
            instruction:'请提交有效期内的教师资格证照片,需确保头像和文字清晰',//String/html标记语言(换行用<br/>) ;showLimitRules为false时,不受影响,显示
            extraDescribe:['亦可提交高等学历证,专业证书等其他资格证明']//Array 上传要求的额外描述,【showLimitRules为false时不显示】
          }
        }*/
    },
    itemRule: {
      /*限制条件*/
      type: Object,
      /*required: true,*/
      validator: function(value) {
        // 宽度范围与高度范围中的最大最小的数值需符合逻辑
        if (
          value.widthRange &&
          value.widthRange.minWidth &&
          value.widthRange.maxWidth
        ) {
          return value.widthRange.minWidth < value.widthRange.maxWidth;
        } else if (
          value.heightRange &&
          value.heightRange.minHeight &&
          value.heightRange.maxHeight
        ) {
          return value.heightRange.minHeight < value.heightRange.maxHeight;
        } else {
          return true;
        }
      },
      default: function() {
        return {
          format: ["jpg", "jpeg", "png"], //Array 文件格式
          maxSize: 1024, //Number[kb] 文件大小
          width: undefined, //Number[px] 图片宽度
          height: undefined, //Number[px] 图片高度
          widthRange: {
            minWidth: undefined, //Number[px] 图片最小宽度
            maxWidth: undefined //Number[px] 图片最大宽度
          },
          heightRange: {
            minHeight: undefined, //Number[px] 图片最小高度
            maxHeight: undefined //Number[px] 图片最大高度
          }
        };
      }
    },
    showLimitRules: {
      //是否展示根据限制条件自动生成的规则提示语qualification
      type: Boolean,
      default: true
    },
    uploadUrl: {
      type: String
    }
  },
  watch: {
    /* 监听上传之后返回的值 */
    uploadedFile: function(newVal) {
      console.log(newVal)
      if (!newVal) {
        this.fileSrc = this.defaultImg;
        this.dispatch("ElFormItem", "el.form.change", [""]);
        return false;
      }
      if (newVal.indexOf(this.iconPrefix) > 0) {
        this.fileSrc = newVal;
      } else {
        this.fileSrc = this.iconPrefix + "/" + newVal;
      }
    }
  },
  mounted: function() {
    /*this.$notify.config({
        top: 130,
        duration: 0
      });*/
    /* 获取资源前缀 */
    this.getsourcePrefix();

    /* 判断上传之后返回的值是否带有前缀 */
    let urlPattern = /[a-zA-z]+:\/\/[^\s]*/;
    let uploadedFile = String(this.uploadedFile || "");
    if (!uploadedFile) {
      this.fileSrc = this.defaultImg;
    } else if (urlPattern.test(uploadedFile)) {
      this.fileSrc = this.uploadedFile;
    } else {
      this.fileSrc = this.iconPrefix + this.uploadedFile;
    }
  },
  computed: {
    /*validateState() {
        return this.elFormItem ? this.elFormItem.validateState : '';
      },*/

    /* 设置上传图片的地址 */
    theUrl: function() {
      if (!this.uploadUrl || this.uploadUrl === "") {
        return this.$uploadUrl; //todo:默认的上传地址(注册于Vue.prototype)
      } else {
        return this.uploadUrl;
      }
    },
    /* 规则提示语的 */
    qualification: function() {
      let qualification = [];
      if (this.itemRule) {
        if (this.itemRule.maxSize) {
          let maxSize =
            this.itemRule.maxSize < 1024
              ? this.itemRule.maxSize + "Kb"
              : this.itemRule.maxSize / 1024 + "Mb";
          let sizeLimit = "图片大小不超过" + maxSize;
          qualification.push(sizeLimit);
        }
        if (this.itemRule.format) {
          let formatLimit = "支持";
          for (let i = 0; i < this.itemRule.format.length; i++) {
            if (i !== this.itemRule.format.length - 1) {
              formatLimit = formatLimit + this.itemRule.format[i] + "/";
            } else {
              formatLimit = formatLimit + this.itemRule.format[i];
            }
          }
          formatLimit = formatLimit + "格式的图片";
          qualification.push(formatLimit);
        }
        if (this.itemRule.width || this.itemRule.height) {
          let widthLimit = this.itemRule.width
            ? this.itemRule.width + "px(像素)"
            : "不限";
          let heightLimit = this.itemRule.height
            ? this.itemRule.height + "px(像素)"
            : "不限";
          let dimensionLimit =
            "图片尺寸为" + "宽:" + widthLimit + ";高:" + heightLimit;
          qualification.push(dimensionLimit);
        }
        /*if(!this.itemRule.width && !this.itemRule.height){//只有穿参中没有宽或高固定限制,才进行宽度或高度范围的条件校验
            //此处为自动生成的宽度、高度范围校验的描述文字【现在不想写】
          }*/
      }
      if (this.itemMessage.extraDescribe) {
        qualification = qualification.concat(this.itemMessage.extraDescribe);
      }
      return qualification;
    }
    /*    // 设置请求头
      headers() {
        return {
          // 设置Content-Type类型为multipart/form-data
          'ContentType': 'multipart/form-data'
        }
      } */
  },
  methods: {
    /* 获取配置的地址 */
    getsourcePrefix() {
      this.$http.hosCommon.fetchConfig().then(data => {
        this.iconPrefix = "http://" + data.qiniu;
      });
    },
    /* 警告 */
    notifyWarning(title, message, duration) {
      let that = this;
      setTimeout(() => {
        that.$notify.warning({
          /*this.errorNotify['ImgFormatError'] = */
          title: title,
          message: message,
          duration: duration
        });
      }, 20); //elementUI组件bug(that.$notify的触发时间接近的提示窗叠在一起bug),需要添加20ms的延时触发(0,1的延时触发均不能解决)
    },
    /* 上传之前的验证操作 */
    BeforeUpload(file) {
      /*this.closeError();*/
      if (!this.itemRule) {
        this.customUpload(file);
        return false;
      }
      let validateResult = this.validate(file);
      /*alert(validateResult);*/
      /* 在客户端读取文件 */
      let reader = new FileReader();
      let that = this;
      //这是一个回调函数,需要设置reader.readAsDataURL(file),并在该函数执行结束后才会触发这个回调
      reader.onload = function(e) {
        let data = e.target.result;
        /*that.fileSrc = data;//本地加载展示图片*/
        //加载图片获取图片真实宽度和高度用于校验
        let image = new Image();
        /* 这是个回调,在图像加载完毕后调用 */
        image.onload = function() {
          /*宽高范围校验*/
          if (!that.itemRule.width && !that.itemRule.height) {
            let widthRangeInconform = false;
            let heightRangeInconform = false;
            if (that.itemRule.widthRange) {
              widthRangeInconform = that.itemRule.widthRange.maxWidth
                ? image.width > that.itemRule.widthRange.maxWidth
                : false;
              if (!widthRangeInconform) {
                widthRangeInconform = that.itemRule.widthRange.minWidth
                  ? image.width < that.itemRule.widthRange.minWidth
                  : false;
              }
            } else if (that.itemRule.heightRange) {
              heightRangeInconform = that.itemRule.heightRange.maxHeight
                ? image.height > that.itemRule.heightRange.maxHeight
                : false;
              if (!heightRangeInconform) {
                heightRangeInconform = that.itemRule.heightRange.minHeight
                  ? image.height < that.itemRule.heightRange.minHeight
                  : false;
              }
            } else {
            }
            if (widthRangeInconform || heightRangeInconform) {
              that.notifyWarning(
                that.itemName + "图片长宽尺寸不符合要求!",
                "请重新上传符合宽、高规定的图片",
                5000
              );
              file = null;
              that.imgFile = null;
              that.noticeParent(undefined);
              //清除展示
              that.fileSrc = that.defaultImg;
            } else if (validateResult) {
              //所有校验均通过
              that.imgFile = file;
              that.customUpload(file);
              /*console.log('进入了手动上传')*/
            } else {
              //宽高校验通过,但格式或大小校验不通过
              file = null;
              this.imgFile = null;
              that.noticeParent(undefined);
              //清除展示
              that.fileSrc = that.defaultImg;
            }
          } else {
            /*固定宽/高校验(校验通过为false)*/
            let widthInconform = that.itemRule.width
              ? image.width !== that.itemRule.width
              : false;
            let heightInconform = that.itemRule.height
              ? image.height !== that.itemRule.height
              : false;
            if (widthInconform || heightInconform) {
              //宽高校验未通过
              let widthLimit = that.itemRule.width
                ? that.itemRule.width + "px"
                : "不限";
              let heightLimit = that.itemRule.height
                ? that.itemRule.height + "px"
                : "不限";
              that.notifyWarning(
                that.itemName + "图片长宽尺寸不符合要求!",
                "请重新上传:宽度为 " +
                  widthLimit +
                  "*" +
                  heightLimit +
                  "的图片",
                5000
              );
              file = null;
              that.imgFile = null;
              that.noticeParent(undefined);
              //清除展示
              that.fileSrc = that.defaultImg;
            } else if (validateResult) {
              //所有校验均通过
              that.imgFile = file;
              /*that.$refs['upload'].post(file);//采用手动上传*/
              that.customUpload(file);
              console.log("进入了手动上传");
            } else {
              //宽高校验通过,但格式或大小校验不通过
              file = null;
              this.imgFile = null;
              that.noticeParent(undefined);
              //清除展示
              that.fileSrc = that.defaultImg;
            }
          }
        };
        image.src = data;
      };
      // 以DataURL的形式读取文件:
      reader.readAsDataURL(file);

      return false; //采用手动上传
    },
    /*   校验格式与文件大小 */
    validate(file) {
      let result = true; //校验通过旗标
      if (this.itemRule) {
        if (this.itemRule.format.length) {
          const _file_format = file.name
            .split(".")
            .pop()
            .toLocaleLowerCase();
          const checked = this.itemRule.format.some(
            item => item.toLocaleLowerCase() === _file_format
          );
          if (!checked) {
            this.handleFormatError();
            result = false;
          }
        }
        // check maxSize
        if (this.itemRule.maxSize) {
          if (file.size > this.itemRule.maxSize * 1024) {
            this.handleExceedSize();
            result = false;
          }
        }
      }
      return result;
    },
    /* 上传格式报错的函数处理 */
    handleFormatError() {
      this.notifyWarning(
        this.itemName + "图片格式错误!",
        "请重新上传" + this.itemRule.format + "格式的图片",
        5000
      );
    },
    /* 上传图片大小报错的函数处理 */
    handleExceedSize() {
      let maxSize =
        this.itemRule.maxSize < 1024
          ? this.itemRule.maxSize + "Kb"
          : this.itemRule.maxSize / 1024 + "Mb";
      this.notifyWarning(
        this.itemName + "图片体积过大!",
        "请重新上传不超过" + maxSize + "的图片",
        5000
      );
    },
    /* 手动上传的函数处理 */
    customUpload(file) {
      this.progressObj = {
        status: "text",
        progressPercentage: 0
      };
      let that = this;
      let onUploadProgress = function(progressEvent) {
        let progress = Number(
          ((progressEvent.loaded / progressEvent.total) * 100).toFixed(2)
        );
        that.progressObj.progressPercentage =
          progress >= 99.99 ? 99.99 : progress;
      };
      let reqConfig = {
        headers: {
          fileType: "file",
          "Content-Type": "multipart/form-data"
        },
        onUploadProgress: onUploadProgress
      };
      let rawFile = new FormData();
      rawFile.append("a", file);
      this.$http.hosCommon.putFile(this.theUrl, rawFile, reqConfig.headers).then(data => {
        this.noticeParent(data[0]);
        this.fileSrc = "http://w.y7edu.com/" + data[0];
        /*  console.log(this.fileSrc); */
        setTimeout(() => {
          that.progressObj = {
            status: "success",
            progressPercentage: 100
          };
        }, 150);
       
      })
      .catch(error => {
        that.progressObj.status = "exception";
      });
    },
    /* 同步父、子组件上传文件状态【null/Object[file]】 */
    noticeParent(msg) {
      /*let notice = {itemProp:this.itemProp,uploadedImg:msg};*/
      this.$emit("updateFile", msg);
      this.dispatch("ElFormItem", "el.form.change", [msg]);
    },
    /* 使用了elFormItem组件作为父组件,该图片上传组件的v-model绑定的数据更新时,通知elFormItem组件 */
    dispatch(componentName, eventName, params) {
      let parent = this.$parent || this.$root;
      let name = parent.$options.componentName;

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;

        if (parent) {
          name = parent.$options.componentName;
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    }
  }
};
</script>
<style lang="less">
.upload-img-component {
  height: 40px;
  position: relative;
  display: inline-block;
  .el-upload {
    width: 100%;
  }
  .button-upload {
    position: absolute;
    height: 40px;
    padding: 0 20px;
    min-width: 160px;
    top: 0;
    left: 0;
    .upload-img {
      .el-upload--text { 
        height: 40px;
        line-height: 40px;
        border-radius: 5px;
        text-align: center;
        font-size: 18px;
        color:  #FFF;
        background-color: #00b8c4;
        background-size: 100% 100%;
        background-position: center;
        &:hover{
         opacity: 0.85;
        }
      }
    }
    .uploaded-img {
      height: 40px;
      line-height: 40px;
      border-radius: 5px;
      text-align: center;
      font-size: 18px;
      color: rgba(255, 255, 255, 1);
      background-color: #909399;
      background-size: 100% 100%;
      background-position: center;
      &:hover{
        opacity: 0.85;
      }
    }
  }
  // .preview {
  //   & > div {
  //     width: 100%;
  //     height: 100%;
  //     border-radius: 6px;
  //   }
  //   img {
  //     height: 100%;
  //     width: 100%;
  //     background: #dddddd;
  //     border-radius: 6px;
  //   }
  // }
}
</style>


使用:

<upload-img
   :showPreview="false"
   :imgDisplayWidth="uploadConfig.previewWidth"
   :imgDisplayHeight="uploadConfig.previewHeight"
   :itemRule="uploadConfig.imgRequirement"
   v-model="imgUrl"
   :itemName="uploadConfig.label"
></upload-img>

这里的图片上传就不做赘述了,请看上面的头像上传的详细说明

参考文章:

element ui 表单验证不通过

参考问题:element ui 表单验证 this.$refs[formName].validate()里面的内容死活不执行

this.$nextTick(() => {})的作用

参考问题:vue中mounted函数中添加setTimeout有什么作用

JavaScript 判断对象中是否有某属性的常用方法

原文文章:JavaScript 判断对象中是否有某属性的常用方法

  • 第一种:点( . )或者方括号( [ ] )

通过点或者方括号可以获取对象的属性值,如果对象上不存在该属性,则会返回undefined。当然,这里的“不存在”指的是对象自身和原型链上都不存在,如果原型链有该属性,则会返回原型链上的属性值。

// 创建对象
let test = {name : 'lei'}
// 获取对象的自身的属性
test.name   //"lei"
test["name"]   //"lei"
 
// 获取不存在的属性
test.age    //undefined
 
// 获取原型上的属性
test["toString"]  //toString() { [native code] }
 
// 新增一个值为undefined的属性
test.un = undefined
 
test.un    //undefined 不能用在属性值存在,但可能为 undefined的场景

所以,我们可以根据 Obj.x !== undefined 的返回值 来判断Obj是否有x属性。

这种方式很简单方便,局限性就是:不能用在x的属性值存在,但可能为 undefined的场景。 in运算符可以解决这个问题

  • 第二种:in 运算符

MDN 上对in运算符的介绍:如果指定的属性在指定的对象或其原型链中,则in 运算符返回true。

'name' in test  //true
'un' in test    //true
'toString' in test //true
'age' in test   //false

示例中可以看出,值为undefined的属性也可正常判断。

这种方式的局限性就是无法区分自身和原型链上的属性,在只需要判断自身属性是否存在时,这种方式就不适用了。这时需要hasOwnProperty()

  • 第三种:hasOwnProperty()
test.hasOwnProperty('name')  //true 自身属性
test.hasOwnProperty('age')   //false 不存在
test.hasOwnProperty('toString') //false 原型链上属性

可以看到,只有自身存在该属性时,才会返回true。适用于只判断自身属性的场景。

参考的文章:

JS根据身份证号码算年龄

原文文章:JS根据身份证号码算年龄

element ui的表格鼠标定义右键菜单

上代码:


<template>
  <!-- 
@property { tableData :  {Array} 表格数据 }
@property { columData :  {Array} 表头数据 }
@property { menuList :  {Array} 右键菜单 }
@property { myKey :  {String} 唯一标识。}
@property { isRadio :  {Boolean} 是否单选,默认false。}
@property { emptyText :  {String} 空数据时显示的文本内容,默认暂无数据。}
@method  { selectItem :  {Function(selection, row)} 当用户手动勾选数据行的 Checkbox 时触发的事  }
@method  { selectAll :  {Function(selection)} 当用户手动勾选全选 Checkbox 时触发的事件  }
@method  { selectionChange :  {Function(selection)} 当选择项发生变化时会触发该事件  }
@method  { rowClick :  {Function(row, column, event)} 当某一行被点击时会触发该事件  }
@method  { rowContextmenu :  {Function(row, column, event)} 当某一行被鼠标右键点击时会触发该事件 }
@method  { rowDblclick :  {Function(row, column, event)} 当某一行被双击时会触发该事件  }
  -->
  <div class="air-table-wrapper">
    <el-table
      ref="table"
      id="table"
      :data="tableData"
      :height="height"
      :highlight-current-row="true"
      tooltip-effect="dark"
      style="width: 100%;"
      :row-class-name="rowClassName"
      :header-row-class-name="headerClassName"
      @empty-text="emptyText"
      @select="selectItem"
      @select-all="selectAll"
      @selection-change="selectionChange"
      @row-click="rowClick"
      @row-contextmenu="rowContextmenu"
      @row-dblclick="rowDblclick"
    >
      <!-- checkbox -->
      <el-table-column type="selection" v-if="showSelection" width="56" align="center"></el-table-column>
      <!-- index 序号 -->
      <el-table-column v-if="showIndex" label="序号" type="index" width="50"></el-table-column>
      <!--列表的表头循环-->
      <el-table-column
        v-for="(column, index) in columData"
        :key="index"
        :prop="column.prop"
        :label="column.label"
        :width="column.width"
        :align="column.align||'left'"
        show-overflow-tooltip
      >
        <template slot-scope="scope">
          <img
            v-if="column.isImg"
            :src="scope.row[column.prop]?$sourcePrefix+scope.row[column.prop]:'javascript:;'"
            style="max-width: 100%;height:auto"
          >
          <span
            v-if="!column.isImg&&!column.codeMap&&column.type !== 'select'&&column.type !== 'text'&&column.type !== 'op-icon' &&column.type !== 'cj-icon'
            && column.type !== 'datetime' && column.type !== 'select_input'"
            v-html="scope.row[column.prop]"
            :class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
          ></span>
          <!-- type为op-icon时,为门诊工作站的患者列表的带转入标注 -->
          <span
            class="icon"
            v-if="column.type === 'op-icon'"
            :class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
          >
            <span v-if="scope.row[column.prop] === '待转出'">
              <img src="~@images/work/icon_zhuanru.png" alt>
            </span>
            <span v-if="scope.row[column.prop] === '外院转入'">
              <img src="~@images/work/icon_zhuanchu.png" alt>
            </span>
            <span v-if="scope.row[column.prop] === '跟台手术患者'">
              <img src="~@images/work/icon_gentai.png" alt>
            </span>
            <span v-if="scope.row[column.prop] === '跟台手术患者(审核)'">
              <img src="~@images/work/icon_gentai_weishenhe.png" alt>
            </span>
          </span>
          <!-- type为cj-icon时,为采集工作站的标本类型 -->
          <span
            class="icon"
            v-if="column.type === 'cj-icon'"
            :class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
          >
            <span v-if="scope.row[column.prop] === '血沉'">
              <img src="~@images/work/icon_shiguan_hei.png" alt>
            </span>
            <span v-if="scope.row[column.prop] === '生化检验'">
              <img src="~@images/work/icon_shiguan_hong.png" alt>
            </span>
            <span v-if="scope.row[column.prop] === '凝血常规'">
              <img src="~@images/work/icon_shiguan_lan.png" alt>
            </span>
            <span v-if="scope.row[column.prop] === '血流变学'">
              <img src="~@images/work/icon_shiguan_lv.png" alt>
            </span>
            <span v-if="scope.row[column.prop] === '血常规'">
              <img src="~@images/work/icon_shiguan_zi.png" alt>
            </span>
          </span>
          <!-- type为text时,为门诊工作站的医嘱列表的带转入标注 -->
          <span
            class="text"
            v-if="column.type === 'text' && scope.row[column.prop] === item.status"
            v-for="(item,index) of column.option"
            :key="index"
            v-html="item.title"
            :class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
            :style="{'color': (scope.row[column.prop]==column.option[0].title?column.option[0].color:(scope.row[column.prop] === column.option[1].title?column.option[1].color:(scope.row[column.prop]==column.option[2].title?column.option[2].color:column.option[3].color)))}"
          ></span>
          <!-- type为select时,为下拉选择框 -->
          <span
            v-if="column.type === 'select'"
            :class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
          >
            <el-select
              v-model="scope.row[column.prop]"
              :class="scope.row[column.prop] === 2 ? column.textCalss :''"
              placeholder="请选择"
            >
              <el-option
                v-for="item in column.options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              ></el-option>
            </el-select>
          </span>
           <!-- type为select时,为input在下拉选择框中 -->
          <span
            v-if="column.type === 'select_input'"
          >
            <div v-if="column.tableCommands.type==='buttons'">
              <el-button
                v-for="(btn,index) in column.tableCommands.commandList"
                v-if="btn.hasAuth && btn.commandAvailable(scope.$index,scope.row)"
                :style="{'color':btn.label === '删除'? '#FB7B7B':''}"
                :key="index"
                type="text"
                size="small"
                @click="btn.command(scope.$index,scope.row)"
              >{{btn.label}}</el-button>
            </div>
             <!--下拉类型操作-->
            <el-dropdown
              v-if="column.tableCommands.type==='dropDown'"
              @command="handleDropDownMenuCommand"
              style="cursor:pointer"
              :trigger="column.tableCommands.trigger||'hover'"
            >
              <span class="el-dropdown-link">
                操作
                <i class="el-icon-caret-bottom el-icon--right"></i>
              </span>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item
                  v-for="(option,index) in column.tableCommands.commandList"
                  v-if="option.hasAuth && option.commandAvailable(scope.$index,scope.row)"
                  :key="index"
                  :command="{callback:option.command,row:scope.row,index:scope.$index}"
                >{{option.label}}</el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
            <!-- <el-select
              v-model="scope.row[column.prop]"
              :class="scope.row[column.prop] === 2 ? column.textCalss :''"
              placeholder="请选择"
            >
              <el-option :label="column.options[0].label" :value="column.options[0].value"></el-option>
              <el-option :label="column.options[1].label" :value="column.options[1].value">

              </el-option>
                <el-option
                v-for="item in column.options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              >
              <el-input v-if="item.value===1" v-model="scope.row[column.prop]" placeholder="请输入内容"></el-input>
              </el-option>
            </el-select> -->
          </span>
          <!-- 时间选择器 -->
          <span
            v-if="column.type === 'datetime'"
          >
            <el-date-picker
              v-model="scope.row[column.prop]"
              type="datetime"
              format="yyyy-MM-dd HH:mm:ss"
              value-format="yyyy-MM-dd HH:mm:ss"
              placeholder="选择停止时间">
            </el-date-picker>
          </span>
          <!--不含codeMap,服务器返回内容即显示内容-->
          <!--:class="column.diffStyle?column.diffStyle(scope.row[column.prop]):[]"-->
          <span
            v-if="!column.isImg&&column.codeMap&&column.type != 'select'"
            v-html="column.codeMap[scope.row[column.prop]]"
            :class="column.diffClassList?column.diffClassList(scope.row[column.prop]):[]"
          ></span>
          <!--含有codeMap(数字码与内容的映射),服务器返回的是code,根据code映射显示实际内容-->
          <!-- 右键菜单 -->
        </template>
      </el-table-column>
      <!-- 表格内的按钮 -->
      <el-table-column
        v-if="tableCommands"
        label="操作"
        :width="tableCommands"
        :align="tableCommands.align||'left'"
      >
        <template slot-scope="scope">
          <!--按钮组类型操作-->
          <div v-if="tableCommands.type==='buttons'">
            <el-button
              v-for="(btn,index) in tableCommands.commandList"
              v-if="btn.hasAuth && btn.commandAvailable(scope.$index,scope.row)"
              :style="{'color':btn.label === '删除'? '#FB7B7B':''}"
              :key="index"
              type="text"
              size="small"
              @click="btn.command(scope.$index,scope.row)"
            >{{btn.label}}</el-button>
          </div>
          <!--下拉类型操作-->
          <el-dropdown
            v-if="tableCommands.type==='dropDown'"
            @command="handleDropDownMenuCommand"
            style="cursor:pointer"
            :trigger="tableCommands.trigger||'hover'"
          >
            <span class="el-dropdown-link">
              操作
              <i class="el-icon-caret-bottom el-icon--right"></i>
            </span>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item
                v-for="(option,index) in tableCommands.commandList"
                v-if="option.hasAuth && option.commandAvailable(scope.$index,scope.row)"
                :key="index"
                :command="{callback:option.command,row:scope.row,index:scope.$index}"
              >{{option.label}}</el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
        </template>
        <!--v-if="commandAvailable(option.disabledRows,option.disabledEffectInPage,scope.$index)"-->
      </el-table-column>
      <!-- 添加按钮 -->
      <template slot="append" v-if="showAdd">
        <Button buttonType="colorButtonroundOrange" @click="addClick"></Button>
      </template>
    </el-table>
    <div class="remark" v-if="tableData.length >100">查询数据多余100条最大限制,请调整查询条件后重试!</div>
    <div v-if="dialogVisible" @click="dialogVisible = false" class="modal">
      <div
        class="air-contex-menu"
        v-if="dialogVisible"
        v-on:click="hidePanel"
        :style="{top: positionD.top + 'px',left: positionD.left+'px'}"
      >
        <!--  <el-button class="return" type="text"  icon="el-icon-close" @click="dialogVisible = false"></el-button> -->
        <ul>
          <li
            v-for="(btn,btnIndex) of menuList"
            :key="btnIndex"
            v-if="btn.hasAuth && btn.commandAvailable(listData.index,listData.row)"
          >
            <div
              class="menu_item"
              @click="btn.command(listData.index,listData.row);dialogVisible=false"
            >{{btn.label}}</div>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "airtable",
  // 框选的指令
  directives: {
    drag: {
      // 指令的定义
      inserted: function(el, binding, vnode) {
        var oDiv = el;
        vnode.context.tableTop = getY(oDiv);
        vnode.context.tableLeft = getX(oDiv);
        //方法是确定列表到屏幕的位置
        function getX(obj) {
          var parObj = obj;
          var left = obj.offsetLeft;
          while ((parObj = parObj.offsetParent)) {
            left += parObj.offsetLeft;
          }
          return left;
        }
        //方法是确定列表到屏幕的位置
        function getY(obj) {
          var parObj = obj;
          var top = obj.offsetTop;
          while ((parObj = parObj.offsetParent)) {
            top += parObj.offsetTop;
          }
          return top;
        }
        // 禁止右键默认菜单
        oDiv.oncontextmenu = function() {
          return false;
        };
        oDiv.onmousedown = function(ev) {
          // 防止奇葩操作
          if ([2, 3, 4, 5].indexOf(ev.buttons) > -1) {
            if (selDiv) {
              oDiv
                .getElementsByClassName("el-table__body")[0]
                .removeChild(selDiv);
            }
            return;
          }
          // 获取当前scrollTop
          var scrollingTop = vnode.context.targetScroll;
          if (vnode.context.isRadio) return;
          vnode.context.$selectState(true);
          //初始化不显示
          vnode.context.toggleShow = false;
          //确保用户在移动鼠标的时候只初始化一次选中
          var flag = true;
          // 获取表格的宽高
          let tableWidth = vnode.context.$el.clientWidth;
          let tableHeight = vnode.context.$el.clientHeight;
          //用来存储列表
          var selList = [];
          //获得指令下的dom对应的表格
          var fileNodes = oDiv.getElementsByTagName("tr");
          //获得鼠标
          var evt = window.event || arguments[0];
          var startX = evt.x || evt.clientX;
          var startY = evt.y || evt.clientY;
          var top, left;
          //时时获得
          top = getY(oDiv);
          left = getX(oDiv);
          var selDiv = document.createElement("div");
          selDiv.style.cssText =
            "position:absolute;width:0px;height:0px;font-size:0px;margin:0px;padding:0px;border:1px solid rgba(255,165,22,0.38);background-color:rgba(0,0,0,0.38);z-index:1000;filter:alpha(opacity:60);opacity:0.6;display:none;";
          selDiv.id = "selectDiv";
          oDiv.getElementsByClassName("el-table__body")[0].appendChild(selDiv);
          selDiv.style.left = startX + "px";
          selDiv.style.top = startY + "px";
          var _x = null;
          var _y = null;
          vnode.context.clearEventBubble(evt);
          // 打开开关
          vnode.context.mouseflag = true;
          // 鼠标拖动时画框
          document.onmousemove = function(ev) {
            evt = window.event || arguments[0];
            _x = evt.x || evt.clientX;
            _y = evt.y || evt.clientY;
            // 为了确定是点击事件还是移动事件
            if (Math.abs(_x - startX) < 5 && Math.abs(_y - startY) < 5) {
              return;
            }
            vnode.context.selectItemFlag = false;
            // 为了确保只有一次的渲染每次框选都把默认选中为空(针对当前页)
            if (flag) {
              // 重置选中css
              for (var i = 0; i < fileNodes.length; i++) {
                if (fileNodes[i].className.indexOf("el-table__row") != -1) {
                  fileNodes[i].className = "el-table__row";
                  selList.push(fileNodes[i]);
                }
              }
              // 判断翻页是否保存选中数据
              // if (vnode.context.pageDataFlag) {
              // 判断当前页是否有选中的
              vnode.context.tableData.forEach(item => {
                vnode.context.$refs.table.toggleRowSelection(item, false);
                vnode.context.multipleSelection.forEach((ele, i) => {
                  if (item[vnode.context.myKey] === ele[vnode.context.myKey]) {
                    vnode.context.multipleSelection.splice(i, 1);
                    i = i - 1;
                  }
                });
              });
              // } else {
              //     vnode.context.multipleSelection = []
              // }

              flag = false;
            }
            // 判断鼠标移动是否超出在当前表格区域
            if (
              _x <= left ||
              _x >= left + tableWidth ||
              _y <= top ||
              _y >= top + tableHeight
            ) {
              document.onmousemove = null;
              if (selDiv) {
                oDiv
                  .getElementsByClassName("el-table__body")[0]
                  .removeChild(selDiv);
              }
              (selList = null),
                (_x = null),
                (_y = null),
                (selDiv = null),
                (startX = null),
                (startY = null),
                (evt = null);
              vnode.context.mouseflag = false;
              vnode.context.$handleSelect();
            }
            if (vnode.context.mouseflag) {
              if (selDiv.style.display === "none") {
                selDiv.style.display = "";
              }
              var scrolling = oDiv.getElementsByClassName(
                "el-table__body-wrapper"
              );
              // 两个表格的时候需增加滚屏的高度
              // var main_scrolling=document.getElementsByClassName("common_table_height");
              // selDiv.style.left = Math.min(_x, startX)-left+scrolling[0].scrollLeft +main_scrolling[0].scrollLeft+ "px";
              // console.log(scrolling)
              selDiv.style.left =
                Math.min(_x, startX) - left + scrolling[0].scrollLeft + "px";
              //48是表头的高度
              // selDiv.style.top = Math.min(_y, startY)-top - 48 + scrolling[0].scrollTop+main_scrolling[0].scrollTop+ "px";
              selDiv.style.top =
                Math.min(_y, startY) -
                top -
                oDiv.getElementsByClassName("is-leaf")[0].offsetHeight +
                scrollingTop +
                "px";
              selDiv.style.width = Math.abs(_x - startX) + "px";
              if (scrolling[0].scrollTop > scrollingTop) {
                selDiv.style.height =
                  Math.abs(_y - startY) + scrolling[0].scrollTop + "px";
              } else {
                selDiv.style.height = Math.abs(_y - startY) + "px";
              }

              // ---------------- 关键算法确定列表的选中靠的是index---------------------
              var _l = selDiv.offsetLeft,
                _t = selDiv.offsetTop;
              var _w = selDiv.offsetWidth,
                _h = selDiv.offsetHeight;
              for (var i = 0; i < selList.length; i++) {
                var sl = selList[i].offsetWidth + selList[i].offsetLeft;
                var st = selList[i].offsetHeight + selList[i].offsetTop;
                if (
                  sl > _l &&
                  st > _t &&
                  selList[i].offsetLeft < _l + _w &&
                  selList[i].offsetTop < _t + _h
                ) {
                  if (
                    selList[i].className.indexOf("seled") === -1 &&
                    !vnode.context.tableData[i][vnode.context.rowDisabled]
                  ) {
                    selList[i].className = selList[i].className + " seled";
                    vnode.context.$refs.table.toggleRowSelection(
                      vnode.context.tableData[i],
                      true
                    );
                    vnode.context.multipleSelection.push(
                      vnode.context.tableData[i]
                    );
                  }
                } else {
                  if (selList[i].className.indexOf("seled") != -1) {
                    selList[i].className = "el-table__row";
                    vnode.context.$refs.table.toggleRowSelection(
                      vnode.context.tableData[i],
                      false
                    );
                    let index = vnode.context.multipleSelection.indexOf(
                      vnode.context.tableData[i]
                    );
                    vnode.context.multipleSelection.splice(index, 1);
                  }
                }
              }
              // ---------------- 关键算法结束---------------------
            }
            if (document.onmousemove) {
              vnode.context.clearEventBubble(evt);
            }
          };
          //在鼠标抬起后做的重置
          oDiv.onmouseup = function() {
            //把鼠标移动事初始化
            document.onmousemove = null;
            if (selDiv) {
              oDiv
                .getElementsByClassName("el-table__body")[0]
                .removeChild(selDiv);
            }
            (selList = null),
              (_x = null),
              (_y = null),
              (selDiv = null),
              (startX = null),
              (startY = null),
              (evt = null);
            vnode.context.mouseflag = false;
            vnode.context.$selectState(false);
            // 防止重复$handleSelect()事件
            if (!vnode.context.selectItemFlag) {
              vnode.context.$handleSelect();
              vnode.context.selectItemFlag = false;
            }
          };
        };
      }
    }
  },
  props: {
    // 对于列表中唯一的字段myKey默认为id
    myKey: {
      type: String,
      default: "id"
    },
    //有没有checkbox
    showSelection: {
      type: Boolean,
      default: false
    },
    // 是否显示表头
    showIndex: {
      type: Boolean,
      default: false
    },
    //列表的数据
    tableData: {
      type: Array,
      default: () => []
    },
    //传过来的表头信息
    columData: {
      type: Array,
      default: () => []
    },
    /* 表格类的按钮 */
    tableCommands: {
      type: Object,
      default: () => {}
    },
    //右键菜单
    menuList: {
      type: Array,
      default: () => []
    },
    /* 是否设置右键菜单 */
    iscontextmenu: {
      type: Boolean,
      default: true
    },
    /* 设置是否有双击事件 */
    isDblclick: {
      type: Boolean,
      default: true
    },
    // 当表格需要单选的时候
    isRadio: {
      type: Boolean,
      default: false
    },
    // 是否显示添加按钮
    showAdd: {
      type: Boolean,
      default: false
    },
    // 表格高度
    height: {
      type: [Number, String],
      default: 500
    },
    // 空数据时显示的文本内容
    emptyText: {
      type: String,
      default: "暂无数据"
    }
  },
  data() {
    return {
      // 当前行的数据
      listData: {
        index: "",
        row: ""
      },
      //指令中确定的时候是鼠标按下事件
      mouseflag: false,
      //选中的数组
      multipleSelection: [],
      //控制右键菜单弹出显示
      dialogVisible: false,
      //右键鼠标的位置
      position: {
        left: 0,
        top: 0
      },
      //当前右键点击的列
      currentRow: [],
      //当前滚动的距离,
      targetScroll: 0,
      // 是否点击单选按钮
      selectItemFlag: false
    };
  },
  computed: {
    // 通过滚动距计算阴影 class
    headerClassName() {
      if (this.targetScroll === 0) {
        return "air-table-header__class";
      } else if (this.targetScroll > 0 && this.targetScroll <= 100) {
        return "air-table-header__class air-table-header__scroll1";
      } else if (this.targetScroll > 100 && this.targetScroll <= 200) {
        return "air-table-header__class air-table-header__scroll2";
      } else {
        return "air-table-header__class air-table-header__scroll3";
      }
    },
    menuH() {
      return 25 + this.menuList.length * 34;
    },
    positionD() {
      return this.position;
    },
    size() {
      return this.getViewportSize();
    }
  },
  mounted() {
    this.$refs.table.$refs.bodyWrapper.addEventListener(
      "scroll",
      this.handleScroll
    );
  },
  beforeDestroy() {
    this.$refs.table.$refs.bodyWrapper.removeEventListener(
      "scroll",
      this.handleScroll
    );
    clearTimeout(this.timer);
  },
  methods: {
    //清除默认事件
    clearEventBubble(evt) {
      if (evt.stopPropagation) evt.stopPropagation();
      else evt.cancelBubble = true;
      if (evt.preventDefault) evt.preventDefault();
      else evt.returnValue = false;
    },
    hidePanel(event) {
      event.stopPropagation();
    },
    //当单选时触发的事件
    selectItem(selection, row) {
      this.$emit("clickSelect", selection);
    },
    //当表头全选是触发的事件
    selectAll(selection) {
      this.$emit("clickSelect", selection);
    },
    // 选项发生改变事件
    selectionChange(selection) {
      this.$emit("clickSelect", selection);
    },
    //列表单击选中事件
    rowClick(row, column, event) {
      this.$emit("rowClick", row, column, event);
    },
    //列表右键点击事件
    rowContextmenu(row,column, event) {
      if (this.iscontextmenu && this.menuList.length > 0) {
        this.dialogVisible = false;
        this.dialogVisible = true;
        let defaultIndex = 0;
        this.tableData.forEach((el, index) => {
          if ((el === row)) {
            defaultIndex = index;
          }
        });
        let toRightX = this.size.width - event.clientX;
        let toBottomY = this.size.height - event.clientY;
        if (toRightX < 250) {
          this.position.left = event.clientX - 250;
        } else {
          this.position.left = event.clientX;
        }
        if (toBottomY < this.menuH) {
          this.position.top = event.clientY - this.menuH;
        } else {
          this.position.top = event.clientY;
        }
        //阻止默认事件
        event.preventDefault();
        event.returnValue = false;
        /*  event.stopPropagation(); */
        this.listData.index = defaultIndex;
        this.listData.row = row;
        //显示弹出窗
        this.$emit("contextmenu", row, event);
      }
    },
    //双击事件
    rowDblclick(row, column, event) {
      if (this.isDblclick === true) {
        this.$emit("rowDblclick", row, column, event);
      }
    },
    //监听表格的滚动
    handleScroll(e) {
      this.targetScroll = e.target.scrollTop;
    },
    // 添加按钮点击事件
    addClick() {
      this.$emit("addClick");
    },
    handleClose(e) {
      this.dialogVisible = false;
    },
    rowClassName() {
      return "rowClassName";
    },
    handleDropDownMenuCommand(commandObj) {
      let { callback, row, index } = commandObj;
      callback(index, row);
    }
  }
};
</script>
<style lang="less">
.air-table-wrapper {
  position: relative;
  .air-table-header__class {
    background-color: #e9e9e9;
    th {
      background-color: #e9e9e9;
      & > .cell {
        font-size: 12px;
        font-weight: 400;
        color: #666666;
      }
    }
  }
  .el-table__body-wrapper {
    background: #f9f9f9;
  }
  .remark {
    position: absolute;
    box-sizing: border-box;
    z-index: 100;
    bottom: 0;
    left: 0;
    width: 100%;
    line-height: 30px;
    padding: 0 10px;
    background: #e9e9e9;
    color: #00b8c4;
    text-align: center;
    font-size: 14px;
  }
}
.el-table td {
  background: #f9f9f9;
  border-bottom: 1px solid #dddddd;
}
.el-table {
  .el-table__row {
    .cell.el-tooltip {
      position: relative;
      font-size: 12px;
      font-weight: 400;
      color: #666666;
      .icon {
        position: absolute;
        top: 0;
        left: 0;
        text-align: center;
        width: 100%;
        height: 100%;
        line-height: 0;
        z-index: 1;
      }
    }
  }
}
.rowClassName:hover,
.current-row {
  td {
    background-color: #f1f7f9;
  }
  .cell.el-tooltip {
    color: #00b8c4;
  }
}

#table {
}
.red_style {
  .el-input__inner {
    color: #fb7b7b;
  }
}
</style>

<style lang="less" scoped>
@import "./index";
</style>

使用:

 <CommonTable
      :tableData="WtableList"
      :columData="tableObj.tableColumns"
      :menuList="tableObj.commandList" //tableObj.commandList为菜单配置
      :height="tableHeight"
      @rowDblclick="rowDblclick"
      @rowClick="rowClick"
></CommonTable>
 commandList: [
          {
            label: "编辑患者信息",
            hasAuth: true,
            command: this.handleEdit,
            commandAvailable: function() {
              return true;
            }
          },
          {
            label: "下达医嘱",
            hasAuth: true,
            command: this.handleRDAdvice,
            commandAvailable: function() {
              return true;
            }
          },
          {
            label: "预约住院",
            hasAuth: true,
            command: this.handleAhospitalization,
            commandAvailable: function() {
              return true;
            }
          },
          {
            label: "打印预约入院证明",
            hasAuth: true,
            command: this.handlePrint,
            commandAvailable: function() {
              return true;
            }
          },
          {
            label: "完成看诊",
            hasAuth: true,
            command: this.handleCVisit,
            commandAvailable: function() {
              return true;
            }
          },
          {
            label: "撤回",
            hasAuth: true,
            command: this.handleRetract,
            commandAvailable: function() {
              return true;
            }
          }
]

这些方法就可以在methods中使用了

参考的文章:

vue 操作数组的变异方法和非变异方法

文章地址: vue 操作数组的变异方法和非变异方法

vue文档:变异方法 (mutation method)

变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组:

  • push( )
  • pop( )
  • shift( )
  • unshift( )
  • splice( ) 
  • sort( )
  • reverse( )

也有非变异 (non-mutating method) 方法,例如:

  • filter( )
  • concat( )
  • slice( )

这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组

vue使用lodash

原文文章:vue使用lodash

使用lodash可以做什么:

javascript页面刷新的几种方法

原文文章:javascript页面刷新的几种方法

vue watch对象内的属性

原文文章:vue watch对象内的属性

通过window.sessionStorage.getItem获取的值,一定要 通过JSON.parse()转成对象

原文文章:通过window.sessionStorage.getItem获取的值,一定要 通过JSON.parse()转成对象

vue项目中,定义并使用 全局变量,全局函数

原文文章:vue项目中,定义并使用 全局变量,全局函数

重构项目可能出现的问题

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值