Wiidede's blog Wiidede's blog
  • 前端
  • Python
  • 算法
  • 生活
  • 其他
  • 分类
  • 标签
  • 归档
  • 关于我
  • 赞赏
  • 我的小站 (opens new window)
GitHub (opens new window)

Wiidede

小的的写前端
  • 前端
  • Python
  • 算法
  • 生活
  • 其他
  • 分类
  • 标签
  • 归档
  • 关于我
  • 赞赏
  • 我的小站 (opens new window)
GitHub (opens new window)
  • 整理一些css样式
  • vue隔代组件层层动态插槽并且附带数据
  • vue判断字符串是否溢出来显示弹窗、解决el-table tooltip 内过多导致无法显示,内容闪烁
  • 整理一些js写法
    • 从元素为对象的数组中提取出对象的某属性为x的快速写法,摘自vuex教程
    • filter 其他用法
      • 去掉数组中值为index的项目
      • 判断数组中有没有值为index的项目
    • 获取数组中某一属性的值组成数组
    • 数组中是否包含某一值
    • 反转对象的key和value的值
    • 查找数组中,对象某个属性值为val的索引(index)
    • 判断页面上的字符是否溢出
    • 时间格式化
    • 复制到剪贴板
    • 事件中的offsetX,offsetY,只有chrome能算准,火狐firefox会时常为0,这时候就需要换一种算法
    • div支持拖动并限制四周不超过浏览器页面
    • 手动排队等待请求,一次仅允许发送n个请求
    • 30 seconds of code
      • javascript-switch-object 代替switch语法、switch的优雅写法
      • getURLParameters 在Vue中你可以使用 route 来获取参数
      • createElement
      • deep clone
    • async 中异步调用函数
    • el-upload 自定义上传文件
    • 下载文件
    • 扁平化树状结构数据
    • iframe通信
    • 根据时间排序
    • 生成UUID(lodash)
    • 字节转为单位
    • 前端分页(lodash)
    • 数组转为树
    • 给树的每个节点添加rootId
    • 给树的每个节点添加层级
    • 首字母大写
    • 转换对象key/value
    • ElementPlus 表格常用格式化
    • ElementPlus 常用校验
    • timeoutPromise
    • 给Promise加上全局loading
    • object to URLSearchParams
    • 展示n位小数
    • 获取数组重复项
  • ElementUI timePicker 增加此刻按钮 引发的dom操作的学习
  • 毕业设计(水表识别)前端知识整理
  • html小知识
  • axios请求api然后下载文件
  • vue3+ts根据高度改变元素的透明度
  • vue3 + ElementPlus 换肤方案(Css变量)
  • Moment的一些使用方法
  • echarts基础vue组件
  • element UI el-date-picker 年月日切换组件
  • 可以不选择的el-radio单选框
  • vue的小技巧总结
  • 全局动态权限判断(Vue指令)
  • vue-anchor 探索
  • Deep Dive with Evan You 笔记
  • 前端基础知识查漏补缺
  • WebPack 知识总结
  • 我写的一些可以日后参考的代码
  • 接口变化后,封装接口函数,改变返回内容
  • 项目组件整理
  • 前端框架设计想发
  • 全局进度条
  • 带有token的图片vue组件:authImg,使用axios下载图片
  • 前端npm包推荐
  • 给ElInputNumber添加prefix
  • ElPagination添加页数总数
  • el-tab做成chrome类似的tab样式
  • vue-grid-layout-组件配置
  • 项目数据字典封装
  • 图表组件响应式探索
  • ElementPlus表格table列自动合并composition
  • 简单的curd组件封装
  • ElementPlus表格自定义合计列composition
  • 一些处理表格数据composition api
  • div内容溢出后,内容向左悬浮,vue组件封装
  • 文本数字溢出后,按比例缩小,vue组件封装
  • 表格使用async-validator检验composition
  • ElementPlus Form一些简单的组件整合
  • arco-design快速使用tailwind的颜色、unocss动态颜色
  • 前端
wiidede
2020-10-22

整理一些js写法

# 整理一些js写法

在工作中遇到了许多重复需要的js方法,每次都要查阅之前写的。 为了方便特意写一篇记录这些方法。 并且这也是自己学习js一点点进步的方法。

整理一些js方法

# 从元素为对象的数组中提取出对象的某属性为x的快速写法,摘自vuex教程

说到底是Array的find()、filter()方法

find返回的是数组里第一个符合条件的选项,

filter返回的是数组里符合条件的选项集合,是一个数组

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# filter 其他用法

# 去掉数组中值为index的项目

this.arr = this.arr.filter((item) => item !== index);
1

# 判断数组中有没有值为index的项目

this.arr.filter((item) => item === index).length !== 0
1

# 获取数组中某一属性的值组成数组

const res = this.list.map(item => item.id);
1

# 数组中是否包含某一值

Array.prototype.includes()方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。

const array = [1, 2, 3];
const isinclude = array1.includes(2)
1
2

# 反转对象的key和value的值

如果直接将对象传进去,将会改变原有对象,建议传{...obj}进去

const reverseKV = (obj) => {
 Object.keys(obj).forEach(k => {
  let value = obj[k];
  obj[value] = k;
  delete obj[k];
 });
 return obj;
}
1
2
3
4
5
6
7
8

# 查找数组中,对象某个属性值为val的索引(index)

优雅一点,可以用lodash的findIndex()

const findElem = (arrayToSearch, attr, val) => {
 for (let i = 0; i < arrayToSearch.length; i++) {
  if (arrayToSearch[i][attr] === val) {
   return i;
  }
 }
 return -1;
}
1
2
3
4
5
6
7
8

# 判断页面上的字符是否溢出

const isOverflow = (element) => {
 return element ? element[0].offsetWidth >= element[0].scrollWidth : false;
}
1
2
3

集成为一个vue组件

# 时间格式化

const timeFormat = (value) => {
 return value === 0 ? '-' : moment(value).format('YYYY-MM-DD HH:mm:ss');
}
1
2
3

# 复制到剪贴板

const copyToClipboard = (content) => {
 let tempEl = document.createElement('input');
 tempEl.setAttribute('value', content);
 document.body.appendChild(tempEl);
 tempEl.select();
 if (document.execCommand('copy')) {
  this.$message.success('复制成功');
 } else {
  this.$message.error('复制失败');
 }
 document.body.removeChild(tempEl);
}
1
2
3
4
5
6
7
8
9
10
11
12

30 seconds of code (opens new window)

const copyToClipboard = str => {
  const el = document.createElement('textarea');
  el.value = str;
  el.setAttribute('readonly', '');
  el.style.position = 'absolute';
  el.style.left = '-9999px';
  document.body.appendChild(el);
  const selected =
    document.getSelection().rangeCount > 0
      ? document.getSelection().getRangeAt(0)
      : false;
  el.select();
  document.execCommand('copy');
  document.body.removeChild(el);
  if (selected) {
    document.getSelection().removeAllRanges();
    document.getSelection().addRange(selected);
  }
};
// 例子
copyToClipboard('Lorem ipsum'); // 'Lorem ipsum' copied to clipboard.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 事件中的offsetX,offsetY,只有chrome能算准,火狐firefox会时常为0,这时候就需要换一种算法

//firefox event.offsetX 时常为0
// let ox = event.offsetX; //
// let oy = event.offsetY;
let srcObj = event.target || event.srcElement;
let ox = event.offsetX || (event.clientX - srcObj.getBoundingClientRect().left);
let oy = event.offsetY || (event.clientY - srcObj.getBoundingClientRect().top);
1
2
3
4
5
6

# div支持拖动并限制四周不超过浏览器页面

需要拖动的元素:@mousedown.prevent="move"

元素中拖动无法点击的元素:@mousedown.stop

const move = (e) => {
    let draggableElement = this.$refs['popper'] || e.target;        //获取目标元素
    // 算出鼠标相对元素的位置
    let disX = e.clientX - draggableElement.offsetLeft;
    let disY = e.clientY - draggableElement.offsetTop;
    document.onmousemove = (e) => {       //鼠标按下并移动的事件
        // 用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
        let left = e.clientX - disX;
        let top = e.clientY - disY;
        let bottom = window.innerHeight - draggableElement.offsetHeight;
        let right = window.innerWidth - draggableElement.offsetWidth;
        // 限制拖出页面
        top <= 0 && (top = 0);
        left <= 0 && (left = 0);
        left >= right && (left = right);
        top >= bottom && (top = bottom);
        // 移动当前元素
        draggableElement.style.left = left + 'px';
        draggableElement.style.top = top + 'px';
        // 这两句如果拖动元素没有设置bottom和right可以去掉
        draggableElement.style.bottom = 'unset';
        draggableElement.style.right = 'unset';
    };
    document.onmouseup = () => {
        document.onmousemove = null;
        document.onmouseup = null;
    };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 手动排队等待请求,一次仅允许发送n个请求

如果真的请求特别多,建议去使用更好的(官方一些的)方法

const handleRequestQueue = (paramsArr, maxLength, callback, failCallback) => {
    const paramsLength = paramsArr.length;
    const requestsQueue = [];
    const results = [];
    let i = 0;
    const handleRequest = (param) => {
        const req = api.getSomething(param).then(res => {
            const len = results.push(res);
            typeof callback === 'function' && callback(res);
            if (len < paramsLength && i + 1 < paramsLength) {
                requestsQueue.shift();
                handleRequest(paramsArr[++i]);
            }
            // 所有请求发送完毕
        }).catch(e => {
            results.push(e);
            typeof failCallback === 'function' && failCallback(e, data);
        });
        if (requestsQueue.push(req) < maxLength) {
            handleRequest(paramsArr[++i]);
        }
    };
    handleRequest(paramsArr[i]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 30 seconds of code (opens new window)

# javascript-switch-object (opens new window) 代替switch语法、switch的优雅写法

const switchFn = (lookupObject, defaultCase = '_default') =>
  expression => (lookupObject[expression] || lookupObject[defaultCase])();

const knownFruit = () => console.log('Known fruit');
const unknownFruit = () => console.log('Unknown fruit');

const logFruit = {
  'apples': knownFruit,
  'oranges': knownFruit,
  'default': unknownFruit
};

const fruitSwitch = switchFn(logFruit, 'default');

fruitSwitch('apples'); // Logs: 'Known fruit'
fruitSwitch('pineapples'); // Logs: 'Unknown fruit'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# getURLParameters (opens new window) 在Vue中你可以使用 route 来获取参数

# JavaScript

const getURLParameters = url =>
  (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(
    (a, v) => (
      (a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a
    ),
    {}
  );
1
2
3
4
5
6
7

# Examples

getURLParameters('google.com'); // {}
getURLParameters('http://url.com/page?name=Adam&surname=Smith');
// {name: 'Adam', surname: 'Smith'}
1
2
3

# createElement (opens new window)

# JavaScript

const createElement = str => {
  const el = document.createElement('div');
  el.innerHTML = str;
  return el.firstElementChild;
};
1
2
3
4
5

# Examples

const el = createElement(
  `<div class="container">
    <p>Hello!</p>
  </div>`
);
console.log(el.className); // 'container'
1
2
3
4
5
6

# deep clone (opens new window)

const deepClone = obj => {
  if (obj === null) return null;
  let clone = Object.assign({}, obj);
  Object.keys(clone).forEach(
    key =>
      (clone[key] =
        typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
  );
  if (Array.isArray(obj)) {
    clone.length = obj.length;
    return Array.from(clone);
  }
  return clone;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# async 中异步调用函数

使用Promise.all()

async function f() {
  let f1 = func1('prama');
  let f2 = func2();

  let values = await Promise.all([f1, f2]);
}
1
2
3
4
5
6

# el-upload 自定义上传文件

之前没有对接过上传接口,这次对接后发现上传接口的数据格式一般来说不是普通的Json,而是FormData,所以我们需要这样上传:

const formData = new FormData();
formData.append('file', file);
formData.append('name', file.name);
formData.append('type', file.type);
let ret = await api.upload(this.action.id, formData);
1
2
3
4
5

这里也可以参考postman,Post的body不再是上传Raw,而是formData

# 下载文件

// 传入下载的参数
const getFile = async (url, method = 'get', params = {}, data = {}, errorFunc) => {
 const instance = axios.create({
  headers: {
   'jwt-token': Auth.getJwtToken()
  },
  responseType: 'blob'
 });
 startLoading(); // 页面开始加载
 const res = await instance.request({url, method, params, data}).finally(endLoading); // 不管怎么样结束加载
 const filename = decodeURIComponent(
  res.headers['content-disposition'] && res.headers['content-disposition'].split('filename=')[1]
 ) || '文件';
 const contentType = (res.headers['content-type'] && res.headers['content-type'].split(';')[0]) || '';
 if (res.data instanceof Blob) {
  if (res.data.type === 'application/json') { // 处理后端报错的情况
   const rawText = await res.data.text();
   const raw = JSON.parse(rawText);
   if (raw.code && raw.code !== 0) {
    console.log(raw.message);
    errorFunc && typeof errorFunc === 'function' && errorFunc(raw.message);
   }
  } else {
   downloadFile(res.data, filename);
  }
 } else {
  const blob = new Blob([res.data], {
   type: contentType
  });
  downloadFile(blob, filename);
 }
};

// 下载blob
const downloadFile = (blob, filename) => {
 if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
  window.navigator.msSaveOrOpenBlob(blob, filename);
 } else {
  const downloadElement = document.createElement('a');
  const href = window.URL.createObjectURL(blob); //创建下载的链接
  downloadElement.href = href;
  downloadElement.download = filename; //下载后文件名
  document.body.appendChild(downloadElement);
  // downloadElement.click(); //点击下载
  // 兼容火狐
  if (document.all) {
   downloadElement.click();
  } else {
   let evt = document.createEvent('MouseEvents');
   evt.initEvent('click', true, true);
   downloadElement.dispatchEvent(evt);
  }
  document.body.removeChild(downloadElement); //下载完成移除元素
  window.URL.revokeObjectURL(href); //释放掉blob对象
 }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# 扁平化树状结构数据

const treeList = [{children: []}];
const flatChildren = (rawList, resList) => {
 rawList.forEach(item => {
  item.hasOwnProperty('children') && Array.isArray(item.children) && flatChildren(item.children);
  delete item.children;
  resList.push(item);
 });
};
const resList = [];
flatChildren(treeList, resList);
1
2
3
4
5
6
7
8
9
10

# iframe通信

// iframe
window.parent.postMessage({action: 'xxx', message: 'this is a msg'}, '*');


// 外部
window.addEventListener('message', (evt) => {
    if (evt.data && evt.data.action === 'xxx') {
     console.log(evt.data.message);
    }
});
1
2
3
4
5
6
7
8
9
10

# 根据时间排序

arr.sort((a, b) => moment(a.time).diff(moment(b.time)));
1

# 生成UUID(lodash)

const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'.split('')
const uuid = _.times(4, () => _.sample(chars)).join('')
1
2

发现一个不错的方法:

const uuid = () => {
  const temp_url = URL.createObjectURL(new Blob())
  const uuid = temp_url.toString()
  URL.revokeObjectURL(temp_url)
  return uuid.substring(uuid.lastIndexOf('/') + 1)
}
1
2
3
4
5
6

# 字节转为单位

function bytesToSize(bytes) {
 if (bytes === 0) {
  return {num: 0, unit: 'B'};
 }
 const k = 1024;
 const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
 const i = Math.floor(Math.log(bytes) / Math.log(k));
 return {
  num: (bytes / Math.pow(k, i)).toFixed(2),
  unit: sizes[i]
 };
}
1
2
3
4
5
6
7
8
9
10
11
12

# 前端分页(lodash)

_.chunk(arr, size)
1

# 数组转为树

const arr2tree = (arr, idKey = 'id', parentIdKey = 'parentId', childrenKey = 'children') => {
  const map = {};
  const tree = [];
  arr.map((item) => {
    const res = {...item, [childrenKey]: []};
    map[res[idKey]] = res;
    return res;
  }).forEach((item) => {
    const parent = map[item[parentIdKey]];
    item[parentKey] = parent;
    parent ? parent[childrenKey].push(item) : tree.push(item);
  });
  return tree;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 给树的每个节点添加rootId

const addRootId = (arr, rootIdKey = 'rootId', idKey = 'id', childrenKey = 'children') => {
  const traverse = (node, rootId) => {
    node[rootIdKey] = rootId;
    node[childrenKey] && node[childrenKey].forEach(child => traverse(child, rootId));
  };
  arr.forEach(root => {
    traverse(root, root[idKey]);
  });
};
1
2
3
4
5
6
7
8
9

# 给树的每个节点添加层级

const setLevel = (val, level = 1) => {
  val.forEach(item => {
    item.kcTableLevel = level;
    if (item.children) {
      setLevel(item.children, level + 1);
    }
  });
};
1
2
3
4
5
6
7
8

# 首字母大写

const firstUpperCase = str => str.replace(/^\S/, l => l.toUpperCase())
1

# 转换对象key/value

const reverseKeyValue = (obj: Record<string, string>) => Object.fromEntries(Object.entries(obj).map(([key, value]) => [value, key]));
1

# ElementPlus 表格常用格式化

import dayjs from 'dayjs';
import type { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults';

export const tableDecimalFormatter = <T extends unknown>(row: T, column: TableColumnCtx<T>, cellValue: unknown, index: number) => {
  const number = Number(cellValue);
  return (Number.isFinite(number) ? Math.round(number * 100) / 100 : cellValue); // Number(number.toFixed(2))
};

export const tableDateFormatter = <T extends unknown>(row: T, column: TableColumnCtx<T>, cellValue: unknown, index: number) => {
  const day = dayjs(cellValue as string | number | Date);
  return day.isValid() ? day.format('YYYY-MM-DD HH:mm:ss') : cellValue;
};
1
2
3
4
5
6
7
8
9
10
11
12

# ElementPlus 常用校验

export const validateAccount = (rule: any, value: string, callback: any) => {
  if (!value) {
    return callback(new Error('请输入账户名'));
  } if (!/^[a-zA-Z0-9_]{1,32}$/.test(value)) {
    return callback(new Error('账户名只能包含字母、数字、下划线,最长32位'));
  }
  return callback();
};

export const validatePhoneNumber = (rule: any, value: string, callback: any) => {
  if (value && !/^(?:(?:\+|00)86)?1\d{10}$/.test(value)) {
    return callback(new Error('手机号格式不正确'));
  }
  return callback();
};

export const validateEmail = (rule: any, value: string, callback: any) => {
  if (value && !/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(value)) {
    return callback(new Error('邮箱格式不正确'));
  }
  return callback();
};

export const useValidateFile = (fileList: Ref<UploadUserFile[]>, message = '请上传文件') => (rule: any, value: string, callback: any) => {
  if (!fileList.value.length) {
    return callback(new Error(message));
  }
  return callback();
};

// onChange时使用
export const checkFile = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  if (!['jpg', 'mp4', 'png', 'jpeg', 'doc', 'docx', 'xls', 'xlsx', 'pdf', 'txt'].includes(uploadFile.name.split('.').pop()!)) {
    uploadFiles.splice(uploadFiles.indexOf(uploadFile), 1);
    ElMessage.error(`文件格式不正确: ${uploadFile.name}`);
  }
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# timeoutPromise

const usePromiseTimeout = (ms?: number) => new Promise((resolve) => setTimeout(resolve, ms))
1

或者

import { promiseTimeout } from '@vueuse/core'
1

# 给Promise加上全局loading

const withElLoading = async <T>(
  fn: Promise<T>,
  options?: Parameters<typeof ElLoading.service>[0],
) => {
  const loadingInstance = ElLoading.service({
    lock: true,
    fullscreen: true,
    ...options,
  });
  return fn.finally(loadingInstance.close)
}
1
2
3
4
5
6
7
8
9
10
11

# object to URLSearchParams

const obj2params = (obj: {}) => {
  const params = new URLSearchParams();
  Object.entries(obj).forEach(([key, value]) => {
    params.append(key, value as string);
  });
  return params;
};
1
2
3
4
5
6
7

# 展示n位小数

export function displayNumber(number: number, fixed = 1) {
  if (typeof number !== 'number')
    return ''
  const n = 10 ** fixed
  return String(Math.round(number * n) / n)
}
1
2
3
4
5
6

# 获取数组重复项

export function getDuplicates(arr: any[]) {
  const duplicates = arr.filter((item, index) => arr.indexOf(item) !== index)
  return [...new Set(duplicates)]
}
1
2
3
4
#前端#JavaScript
上次更新: 2023/06/01, 12:40:50

← vue判断字符串是否溢出来显示弹窗、解决el-table tooltip 内过多导致无法显示,内容闪烁 ElementUI timePicker 增加此刻按钮 引发的dom操作的学习→

Theme by Vdoing | Copyright © 2021-2023 Wiidede | Website use MIT License | Article content & logo use CC-BY-SA-4.0 License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式