js常用函数以及踩过的那些坑

本文涵盖前端开发中的实用技巧,包括Vue+ElementUI的高级用法,如表单验证、懒加载、图片与视频下载,以及数组和对象的深度操作。

(持续更新)

点击某个盒子之外的部分触发函数

假设此盒子的class为:item-operation

    hideOperation(event) {
      // 隐藏加减号图标
      var op = document.getElementsByClassName('item-operation');
      if (op) {
        for (let i = 0; i < op.length; i++) {
          if (!op[i].contains(event.target)) {
          	// 代码
            this.operationValue = false;
          }
        }
      }
    }

element表格记住展开行状态

<el-table
	:data="channelList"
	:row-key="getRowKeys"
	:expand-row-keys="expands"
	@expand-change="expandChange"
>

getRowKeys(row) { // data
	return row.id;
},
expands: []

watch: {
	// 获取展开行状态
    channelList: function() {
		if (JSON.parse(sessionStorage.getItem('expands')) && JSON.parse(sessionStorage.getItem('expands')).length) {
        JSON.parse(sessionStorage.getItem('expands')).map(item => {
          this.expands.push(item);
        });
        this.expands = this.unique(this.expands);
      } else {
        this.expands = [];
      }
	}
},

expandChange(row, expandedRows) { // methods
	this.expands = [];
	if (expandedRows.length > 0) {
	expandedRows.map((item, index) => {
		this.expands.push(item.id);
	});
		sessionStorage.setItem('expands', JSON.stringify(this.expands));
	} else {
		sessionStorage.setItem('expands', JSON.stringify([]));
	}
},

// 数组去重
unique(arr) {
	// 遍历arr,把元素分别放入tmp数组(不存在才放)
    let tmp = [];
    for (let i in arr) {
    	//该元素在tmp内部不存在才允许追加
    	if (tmp.indexOf(arr[i]) === -1) {
          tmp.push(arr[i]);
        }
    }
    return tmp;
},

js实现下载图片

function downloadPic() {
	let image = new Image();
	image.crossOrigin = "anonymous";
	image.src = "https://profile.csdnimg.cn/2/4/1/1_x1151605848";
	let fileName = image.src.split(/(\\|\/)/g).pop();
	image.onload = function () {
		let canvas = document.createElement('canvas');
		canvas.width = this.naturalWidth;
		canvas.height = this.naturalHeight;
		canvas.getContext('2d').drawImage(this, 0, 0);
		let blob;
		// get Data URI
		if (image.src.indexOf(".jpg") > -1) {
			blob = canvas.toDataURL("image/jpeg");
		} else if (image.src.indexOf(".png") > -1) {
			blob = canvas.toDataURL("image/png");
		} else if (image.src.indexOf(".gif") > -1) {
			blob = canvas.toDataURL("image/gif");
		} else {
			blob = canvas.toDataURL("image/png");
		}
		const a = document.createElement('a'); // 生成一个a元素
        a.href = blob; // 将生成的URL设置为a.href属性
        a.download = fileName; // 设置图片名称
        a.click();
	}
}

js下载视频(也可下载图片音频等)

// 转换视频为blob格式
getBlob(url, cb) {
  const xhr = new XMLHttpRequest();
  xhr.open('get', url, true);
  xhr.responseType = 'blob'; // ""|"text"-字符串 "blob"-Blob对象 "arraybuffer"-ArrayBuffer对象
  xhr.onload = function() {
    cb(xhr.response);
  };
  xhr.send();
}
// 下载
handleDownload(row) {
  console.log(row);
  if (row.download_url) {
    const fileName = row.download_url.split(/(\\|\/)/g).pop();
    this.getBlob(row.download_url, function(res) {
      const src = URL.createObjectURL(res);
      const a = document.createElement('a');
      a.href = src;
      a.download = fileName;
      console.log(a);
      a.click();
    });
  } else {
    this.$message({
      showClose: true,
      message: '下载地址为空!',
      type: 'error'
    });
  }
}

深度遍历数组嵌套,并进行相应的操作

let arr= [
  {
    name: '层1',
    children: [
      {
        name: '层2',
        children: [
          {
            name: '层3',
            children: [
              {
                name: '层4-1',
                children: [
                  {
                    name: '层5',
                    children: [
                      {
                        name: '层6-1',
                        children: []
                      },
                      {
                        name: '层6-2',
                        children: []
                      },
                      {
                        name: '层6-3',
                        children: []
                      },
                      {
                        name: '层6-4',
                        children: []
                      }
                    ]
                  }
                ]
              },
              {
                name: '层4-2',
                children: []
              }
            ]
          }
        ]
      }
    ]
  }
];
// 有一个缺点是数组只有一层的时候,无法进行遍历操作
// 调用示例:this.handlePointName(arr, '层5', 'add')
// 则可对name为‘层5’的数据进行添加子层的操作
// this.pointIndex与i的作用是防止操作同名节点
// 例如第五层有name为‘哈哈哈’的数据,第六层也有name为‘哈哈哈’的数据
// 则可以防止同时操作这两个数据,而this.pointIndex的值可另由深度遍历数组得到,此处略
handlePointName(data, name, handle) {
      // 定义i用于区分同名节点
      let i = 0;
      data.forEach(item => {
        const map = data => {
          data.children &&
            data.children.forEach((child, index) => {
              ++i;
              if (child.name === name && this.pointIndex - 1 === i) {
                switch (handle) {
                  case 'add':
                    child.children.push({
                      name: this.pointTemp,
                      children: []
                    });
                    break;
                  case 'edit':
                    child.name = this.pointTemp;
                    break;
                  case 'delete':
                    data.children.splice(index, 1);
                    break;
                }
              }
              map(child);
            });
        };
        map(item);
      });
    }

深克隆

/**
 * 克隆对象或数组
 * @param {Object, String} source type('arr'/'obj')
 * @returns {Object}
 */
deepCopy(source, type){
  if(!source || typeof source !== 'object'){
    if(type === 'arr') {
      return []
    } else if(type === 'obj') {
      return {}
    }
  }
  let target = source.constructor === Array ? [] : {};
  for(let keys in source){
     if(source.hasOwnProperty(keys)){
        if(source[keys] && typeof source[keys] === 'object'){
          target[keys] = source[keys].constructor === Array ? [] : {};
          target[keys] = deepCopy(source[keys]);
        }else{
          target[keys] = source[keys];
        }
     } 
  }
  return target;
}

深度遍历并比较两个对象,返回值不相等的属性

/**
 * 比较两个对象,返回值不同的属性
 * @param {Object, Object} newObj oldObj
 * @returns {Object}
 */
function deepCompare(newObj, oldObj) {
  if (!newObj || typeof newObj !== 'object') {
    throw new Error('error');
  }
  if (!oldObj || typeof oldObj !== 'object') {
    throw new Error('error');
  }
  const targetObj = newObj.constructor === Array ? [] : {};
  for (const keys in newObj) {
    if (newObj.hasOwnProperty(keys)) {
      if (newObj[keys] && typeof newObj[keys] === 'object') {
        targetObj[keys] = newObj[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepCompare(newObj[keys], oldObj[keys]);
      } else {
        if (newObj[keys] !== oldObj[keys]) {
          // console.log(keys);
          targetObj[keys] = newObj[keys];
        }
      }
    }
  }
  for (const key in targetObj) {
    // 删除空对象和空数组
    if (Array.prototype.isPrototypeOf(targetObj[key]) && targetObj[key].length === 0 ||
      Object.prototype.isPrototypeOf(targetObj[key]) && Object.keys(targetObj[key]).length === 0) {
      delete targetObj[key]
    }
  }
  return targetObj
}

数组转对象

let obj = {};
// 纯数组形式
let arr = ['a', 'b', 'c']
for (const key in arr) {
  obj[key] = arr[key];
}
// obj = { 0:'a', 1: 'b', 2: 'c' }
// 键值对形式
let arr = [
	{ key: 'a', value: 'apple' },
	{ key: 'b', value: 'ball' }
]
for (const keys in arr) {
  obj[arr[keys].key] = arr[keys].value;
}
// obj = { a: 'apple', b: 'ball' }

vue+element设置开始日期和结束日期的相互限制

// 开始日期的限制
startDatePicker() {
  const thz = this;
  return {
    disabledDate(time) {
      if (thz.applyForm.end_date) {
        return (
          time.getTime() < Date.now() - 3600 * 1000 * 24 ||
          time.getTime() > thz.applyForm.end_date
        );
      } else {
        return time.getTime() < Date.now() - 3600 * 1000 * 24;
      }
    },
    shortcuts: [
      {
        text: '今天',
        onClick(picker) {
          picker.$emit('pick', new Date());
        }
      },
      {
        text: '明天',
        onClick(picker) {
          const date = new Date();
          date.setTime(date.getTime() + 3600 * 1000 * 24);
          picker.$emit('pick', date);
        }
      },
      {
        text: '一周后',
        onClick(picker) {
          const date = new Date();
          date.setTime(date.getTime() + 3600 * 1000 * 24 * 7);
          picker.$emit('pick', date);
        }
      }
    ]
  };
},
// 结束日期的限制
endDatePicker() {
  const thz = this;
  return {
    disabledDate(time) {
      return (
        time.getTime() < thz.applyForm.start_date ||
        time.getTime() < Date.now() - 3600 * 1000 * 24
      );
    },
    shortcuts: [
      {
        text: '今天',
        onClick(picker) {
          picker.$emit('pick', new Date());
        }
      },
      {
        text: '明天',
        onClick(picker) {
          const date = new Date();
          date.setTime(date.getTime() + 3600 * 1000 * 24);
          picker.$emit('pick', date);
        }
      },
      {
        text: '一周后',
        onClick(picker) {
          const date = new Date();
          date.setTime(date.getTime() + 3600 * 1000 * 24 * 7);
          picker.$emit('pick', date);
        }
      }
    ]
  };
},

vue+element表单验证-部分验证以及条件验证

为解决表单中,按条件显示的某一项为必填项,这样不管是用v-if还是v-show都会在验证的时候出问题

此为后续补充:更简洁的做法

// 直接将验证条件写到标签里面,这样不需要像下面一样做各种复杂的处理,可以直接if(valid)验证
<el-form-item v-if="dataForm.type!=='once'" label="结束日期" prop="end_date" :rules="[{require: true, trigger: 'blur', message: '结束日期不能为空'}]">
   <el-date-picker v-model="dataForm.end_date" :picker-options="pickerOptions2" type="date" placeholder="结束日期" />
</el-form-item>

第一种写法

// 当频率为每周时,全部验证;为每天时排除周选项,再验证;为单次时,排除结束日期和周选项再验证
if (this.dataForm.type === 'week') {
  this.$refs.dataForm.validate(valid => {
    if (valid) {
      this.$emit('submitPlan');
    }
  });
} else {
  const arr = [];
  if (this.dataForm.type === 'day') {
    // 提取验证规则中的名称,组装为数组,并排除星期选择
    for (const item in this.dataFormRules) {
      if (item !== 'week') {
        arr.push(item);
      }
    }
  } else if (this.dataForm.type === 'once') {
    // 提取验证规则中的名称,组装为数组,并排除结束日期,和星期选择
    for (const item in this.dataFormRules) {
      if (item !== 'end_date') {
        if (item !== 'week') {
          arr.push(item);
        }
      }
    }
  }
  const validArr = [];
  let flag = true;
  this.$refs.dataForm.validateField(arr, valid => {
    validArr.push(valid);
  });
  // 当验证数组中有不为空的项时,flag置为false
  validArr.forEach((item, index) => {
    if (item !== '') {
      flag = false;
    }
  });
  if (flag) {
    this.$emit('submitPlan');
  }
}

第二种写法

// 验证表单-是否为空
checkValidateField() {
  const arr = [];
  // 提取验证规则中的名称,组装为数组,并排除结束日期,分段间隔,周选项三项
  for (const item in this.applyRules) {
    if (item !== 'end_date') {
      if (item !== 'record_mode') {
        if (item !== 'task_mode') {
          arr.push(item);
        }
      }
    }
  }
  return new Promise((resolve, reject) => {
    let flag = true;
    const validArr = [];
    this.$refs.applyForm.validateField(arr, valid => {
      validArr.push(valid);
    });
    // 当验证数组中有不为空的项时
    validArr.forEach((item, index) => {
      if (item !== '') {
        flag = false;
      }
    });
    resolve(flag);
  });
},
// 验证表单的部分项-周选项
checkValidateWeekly() {
  return new Promise((resolve, reject) => {
    let flag = '';
    this.$refs.applyForm.validateField('task_mode.day', valid => {
      if (!valid) {
        flag = true;
      } else {
        flag = false;
      }
      resolve(flag);
    });
  });
},
// 验证表单的部分项-分段间隔
checkValidateInterval() {
  return new Promise((resolve, reject) => {
    let flag = '';
    this.$refs.applyForm.validateField('record_mode.interval', valid => {
      if (!valid) {
        flag = true;
      } else {
        flag = false;
      }
      resolve(flag);
    });
  });
},
// 验证表单的部分项-结束日期
checkValidateEnddate() {
  return new Promise((resolve, reject) => {
    let flag = '';
    this.$refs.applyForm.validateField('end_date', valid => {
      if (!valid) {
        flag = true;
      } else {
        flag = false;
      }
      resolve(flag);
    });
  });
},
// 组装验证表单
async checkValidate() {
  let flag = '';
  flag = await this.checkValidateField();
  if (!flag) {
    return false;
  }
  if (this.applyForm.task_mode.type !== 'single') {
    flag = await this.checkValidateEnddate();
    if (!flag) {
      return false;
    }
    if (this.applyForm.task_mode.type === 'weekly') {
      flag = await this.checkValidateWeekly();
      if (!flag) {
        return false;
      }
    }
  }
  if (this.applyForm.record_mode.type === 'segment_edit') {
    flag = await this.checkValidateInterval();
    if (!flag) {
      return false;
    }
  }
  return new Promise((resolve, reject) => {
    resolve(flag);
  });
},

// 在父组件调用时
const flag = await this.$refs.applyForm.checkValidate();
console.log(flag);
if (!flag) {
  return;
}

vue+element实现select懒加载以及可搜索

// 首先第一个坑,如果不加载全部数据,已选值就只能以ID的值出现
// 第二个坑是,element的搜索是在已有数据中筛选,所以必须要改写搜索函数,从后端搜索

// 第一步,写一个vue指令:selectLoadMore.js 这部分代码copy自网上其他博主
export default {
  install(Vue) {
    Vue.directive('el-select-loadmore', {
      bind: function (el, binding) {
        // 获取element-ui定义好的scroll盒子
        const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap');
        SELECTWRAP_DOM.addEventListener('scroll', () => {
          /**
          * scrollHeight 获取元素内容高度(只读)
          * scrollTop 获取或者设置元素的偏移值,常用于, 计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
          * clientHeight 读取元素的可见高度(只读)
          * 如果元素滚动到底, 下面等式返回true, 没有则返回false:
          * SELECTWRAP_DOM.scrollHeight - SELECTWRAP_DOM.scrollTop === SELECTWRAP_DOM.clientHeight;
          */
          const condition = SELECTWRAP_DOM.scrollHeight - SELECTWRAP_DOM.scrollTop <= SELECTWRAP_DOM.clientHeight;
          if (condition) {
            binding.value();
          }
        })
      }
    })
  }
}
// 注册为全局指令
import selectLoadmore from '@/global/directives/selectLoadmore'
Vue.use(selectLoadmore)
// 第二步在代码中使用(节选自自己的项目)
<el-select
  v-model="dataForm.userGroupIds"
  multiple
  filterable
  :placeholder="$t('systomsetting.userApp.usergroupPlaceholder')"
  style="width: 60%;"
  v-el-select-loadmore="loadmoreUsergroup"
  :filter-method="filterUsergroups"
>
  <el-option
    v-for="item in userGroupList"
    :key="item.id"
    :label="item.name"
    :value="item.id"
  />
</el-select>
// 初始化角色列表的请求参数
this.roleQuery = {
  page: 1,
  limit: 10,
  name: '',
  total: 0
};
// 角色列表,这里需要处理加载更多,搜索,正常加载三种情况时的数据,所以需要传入type区分
async getRoleList(type) {
  this.roleQuery.page = type === 'filter' ? 1 : this.roleQuery.page;
  const obj = {
    name: this.roleQuery.name,
    currentPage: this.roleQuery.page,
    pageSize: this.roleQuery.limit
  };
  const res = await this.$store.dispatch(
    'Apps/SystemSetting/role/list',
    obj
  );
  if (res.ret.code === 0) {
    this.roleQuery.total = res.data.count;
    if (type === 'loadMore') {
      this.roleList.push(...res.data.list);
    } else {
      this.roleList = res.data.list;
    }
  }
  this.roleListTmp = deepCopy(this.roleList);  // 这一句代码用于下文-穿梭框实现懒加载和搜索
  // 编辑赋值时,数据中不包含已选值的情况,这里需要将已选值push到roleList中,然后去重
  if (
    this.dataFormTitle === this.$i18n.t('systomsetting.userApp.editTitle')
  ) {
    this.roleList = uniqueArrObj(
      this.roleList,
      this.dataForm.roleInfo,
      'id'
    );
  }
},
// deepCopy为深拷贝函数,因为js的对象和数组赋值方式是引用赋值,
// 所以很多时候需要使用深拷贝来赋值,拷贝一个副本
export function deepCopy(source) {
  if (!source || typeof source !== 'object') {
    throw new Error('error');
  }
  var targetObj = source.constructor === Array ? [] : {};
  for (var keys in source) {
    if (source.hasOwnProperty(keys)) {
      if (source[keys] && typeof source[keys] === 'object') {
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepCopy(source[keys]);
      } else {
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}
// 数组对象去重,copy自网友
/**
* 数组对象去重
* @param arr1
* @param arr2
* @param prop 对象唯一属性
* @returns {Array}
*/
export function uniqueArrObj(arr1, arr2, prop) {
  var arr = arr1.concat(arr2);
  var lastArr = [];
  var obj = {};

  arr.forEach(item => {
    if(!obj[item[prop]]) {
      obj[item[prop]] = item;
    }
  })

  for (var key in obj) {
    lastArr.push(obj[key]);
  }
  return lastArr;
}
// 用户组选择框懒加载
loadmoreUsergroup() {
  if (this.usergroupQuery.total <= this.userGroupList.length) {
    return;
  }
  this.usergroupQuery.page++;
  this.getUserGropList('loadMore');
},
// 用户组选择框重写搜索方法
filterUsergroups(val) {
  this.usergroupQuery.name = val;
  this.getUserGropList('filter');
},

vue+element实现穿梭框transfer懒加载以及可搜索

// 第一个坑,和上文一样,处理已选值的问题
// 第二个坑,也和上文一样,重写搜索方法的问题,但此处不同于select,是个巨坑,没法直接重写搜索方法,看一下element的transfer组件就知道了,机制貌似是监听输入框的值,然后遍历绑定的data数据,最后需要返回true

// 首先写一个vue指令
export default {
  install(Vue) {
    Vue.directive('el-transfer-loadmore', {
      bind: function (el, binding) {
        const SELECTWRAP_DOM = el.querySelector('.el-transfer-panel__body .el-transfer-panel__list');
        SELECTWRAP_DOM.addEventListener('scroll', () => {
          const condition = SELECTWRAP_DOM.scrollHeight - SELECTWRAP_DOM.scrollTop <= SELECTWRAP_DOM.clientHeight;
          if (condition) {
            binding.value();
          }
        })
      }
    })
  }
}

// 在项目中使用
<el-transfer
  v-model="dataForm.role"
  filterable
  :titles="[$t('systomsetting.usergroupApp.roleList'), $t('systomsetting.usergroupApp.alreadyExisting')]"
  :props="{
    key: 'id',
    label: 'name'
  }"
  :filter-placeholder="$t('systomsetting.keyPlaceholder')"
  :data="roleList"
  @change="roleChange"
  v-el-transfer-loadmore="loadmoreRole"
  :filter-method="filterRoles"
/>
// 初始化角色列表的请求参数
this.roleQuery = {
  page: 1,
  limit: 10,
  name: '',
  total: 0
};
// getRoleList见上文, roleListTmp的赋值见上文,
// 角色穿梭框懒加载
loadmoreRole() {
  if (this.roleQuery.total <= this.roleList.length) {
    return;
  }
  this.roleQuery.page++;
  this.getRoleList('loadMore');
},
// 角色穿梭框重写搜索方法,获取到input标签,然后监听值的变化
filterRoles(query, item) {
  let transfer = $(
    '.el-transfer-panel__body .el-transfer-panel__filter .el-input__inner'
  );
  // 拿到输入框的值,并监听
  if (transfer && transfer.length) {
    this.transferLeftInput = transfer[0].value;
  }
  return item.name.indexOf(query) > -1; // 这一句是穿梭框原本的过滤方法,所以我们所做的只是在这之前拿到input标签并监听其值,然后去后端请求数据
},

watch: {
  // 监听穿梭框左侧输入框的值
  transferLeftInput(val) {
    this.roleQuery.name = val;
    this.getRoleList('filter');
  },
  // 此处就是巨坑了:其一当搜索不到数据时,再去修改搜索条件,穿梭框的过滤函数filterRoles并不会触发,
  // 以至于监听不到transferLeftInput值的改变,就无法请求roleList数据;
  // 其二当搜索出的数据为已选数据,处于右侧穿梭框中,此时相当于绑定的data无数据,
  // 也不会触发过滤函数,所以需要手动请求一次。而判断在什么时候去请求,
  // 就是下面代码中的这两个条件,roleListTmp为上文中roleList在push入已选值之前的副本
  // 总结一下就是,当左侧穿梭框无数据或者全部数据为已选中而处于右侧时,
  // 左侧的所绑定的data为空,过滤函数filterRoles是无法触发的,
  // 因为filterRoles是一个循环函数,data所绑定的数据为空时,filterRoles也就不会循环了
  // 此做法暂时可行,如发现有bug,会及时更新
  roleListTmp(val) {
    if (!val.length || val.length <= this.dataForm.roleInfo.length) {
      this.roleQuery.name = '';
      this.getRoleList('filter');
    }
  }
},

获取html内鼠标的位置坐标

getMousePos(event) {
  const e = event || window.event;
  const scrollX =
    document.documentElement.scrollLeft || document.body.scrollLeft;
  const scrollY =
    document.documentElement.scrollTop || document.body.scrollTop;
  const x = e.pageX || e.touches[0].pageX || e.clientX + scrollX; // e.touches[0].pageX为移动端坐标
  const y = e.pageY || e.touches[0].pageY || e.clientY + scrollY;
  return { x: x, y: y };
}

监听组合键Ctrl+Z

listenKeyCode() {
  let code1 = 0;
  let code2 = 0;
  document.onkeydown = e => {
    if (e.keyCode === 17) {
      code1 = 1;
    }
    if (e.keyCode === 90) {
      code2 = 1;
    }
    if (code1 === 1 && code2 === 1) {
      this.handleback();
    }
  };
  document.onkeyup = e => {
    if (e.keyCode === 17) {
      code1 = 0;
    }
    if (e.keyCode === 90) {
      code2 = 0;
    }
  };
},

vue+element tree树形组件的数据分页请求及操作

以一个文件夹管理为例,先看效果
在这里插入图片描述

// html
<el-tree
  ref="folderTree"
  node-key="id"
  :key="treeKey"
  :data="dataList"
  :props="defaultProps"
  :show-checkbox="type === 'check'"
  check-strictly
  @node-click="handleNodeClick"
  @node-expand="handleNodeClick"
  @check-change="handleCheckChange"
>
  <span
    class="folderTree-node"
    slot-scope="{ node, data }"
    @contextmenu.prevent.stop="handleContext($event, data.id)"
  >
    <span class="folderTree-label">{{ node.label }}</span>
  </span>
</el-tree>

// data
data() {
  return {
    dataList: [],
    // 分页参数
    listQuery: {
      limit: 10,
      offset: 1,
    },
    total: 0,
    listQueryChild: {
      limit: 10,
      offset: 1,
    },
    defaultProps: {
      children: 'children',
      label: 'name',
    },
    treeKey: 0,
  };
},

created() {
  this.getList();
},
/
/ 请求数据
async getList() {
  let obj = {
    params: {
      parent_id: 0,
      limit: this.listQuery.limit,
      offset: this.listQuery.offset,
    },
  };
  let res = await this.axio.post(`directory/ws/list`, obj);
  if (res.data.ret.code === 0) {
    if (this.total === 0) {
      this.dataList = res.data.directory_list.rows;
    } else {
      this.dataList = [...this.dataList, ...res.data.directory_list.rows];
    }
    this.total = res.data.directory_list.count;
    res.data.directory_list.rows.forEach((item) => {
      // 添加子节点请求条件
      item.total = 0;
      item.limit = this.listQueryChild.limit;
      item.offset = this.listQueryChild.offset;
      this.getNodeFile(item);
    });

    // 默认高亮
    this.$nextTick(() => {
      this.$refs.folderTree.setCurrentKey(this.folderId);

      if (this.type === 'check') {
        this.$refs.folderTree.setCheckedKeys([this.dataList[0].id]);
      }
    });

    // 更新tree组件
    this.treeKey++;

    // 当数据很多时,递归请求
    if (this.total > this.dataList.length) {
      this.listQuery.offset++;
      this.getList();
    } else {
      this.total = 0;
      this.listQuery.offset = 1;
    }
  }
},
// 请求子文件夹
async getNodeFile(row) {
  let obj = {
    params: {
      parent_id: Number(row.id),
      limit: row.limit,
      offset: row.offset,
    },
  };
  let res = await this.axio.post(`directory/ws/list`, obj);
  if (res.data.ret.code === 0) {
    if (res.data.directory_list.rows.length > 0) {
      if (row.total === 0) {
        this.$set(row, 'children', deepCopy(res.data.directory_list.rows)); // deepCopy是一个深度克隆函数,用于防止数据污染
      } else {
        this.$set(row, 'children', [
          ...row.children,
          ...deepCopy(res.data.directory_list.rows),
        ]);
      }
    }

    row.total = res.data.directory_list.count;

    // 当数据很多时,递归请求
    if (row.children && row.total > row.children.length) {
      row.offset++;
      this.getNodeFile(row);
    } else {
      row.total = 0;
      row.offset = 1;
    }
  }
},
// 点击节点
handleNodeClick(row) {
  if (row.children) {
    row.children.forEach((item) => {
      // 添加请求条件
      item.total = 0;
      item.limit = this.listQueryChild.limit;
      item.offset = this.listQueryChild.offset;
      this.getNodeFile(item);
    });
  }
},

在使用或显示一张图片之前,判断图片是否能正常显示

/**
 * 判断图片是否能正常显示
 * @param {String} url
 * @returns {Boolean}
 */
function imageIsExist(url) {
  return new Promise(resolve => {
    let img = new Image();
    img.src = url;
    img.onload = function() {
      resolve(true);
      img = null;
    };
    img.onerror = function() {
      resolve(false);
      img = null;
    };
  });
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值