前言

总结一下个人开发总封装的实用方法,以便后期开发方便

方法

合并表格行

在开发表格过程中,总是需要合并单元格子,后端会把相同的数据放在一起,前端处理合并,以下是利用哈希值处理的合并方法。

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
//tableData 合并的表格数据,matchKey为匹配的key数组,可匹配多个
function dealWithMergeData(tableData, matchKey) {
let spanObject = {}
//遍历匹配项
matchKey.forEach((spanKeyItem) => {
if (!spanObject[spanKeyItem]) spanObject[spanKeyItem] = [];
//起始索引
let startIndex = 0;
//遍历表格数据
for (let i = 0; i < tableData.length; i++) {
let tableItem = tableData[i];
if (i != 0) {
//如果当前的值和上一个一样
if (tableItem[spanKeyItem] === tableData[i - 1][spanKeyItem]) {
//起始自增
spanObject[spanKeyItem][startIndex].rowspan++;
//自身设置为空
spanObject[spanKeyItem][i] = {
rowspan: 0,
colspan: 0
}
} else {
//如果不等于,那么自身就是起始索引,重新开始
startIndex = i;
spanObject[spanKeyItem][startIndex] = {
rowspan: 1,
colspan: 1
}
}
} else {
spanObject[spanKeyItem][i] = {
rowspan: 1,
colspan: 1
}
}
}
})
return spanObject
}

//dealWithMergeData(tableData,['key','vehicleId'])

reduce 进行数据结构的转换

1
2
3
4
5
6
7
8
9
10
11
12
const arr = [
{ id: "1", name: "张三", age: 11 },
{ id: "1", name: "李四", age: 12 },
{ id: "2", name: "王五", age: 13 },
{ id: "3", name: "赵六", age: 14 },
{ id: "2", name: "孔七", age: 15 }
];

function groupArrayByKey(arr = [], key) {
return arr.reduce((t, v) => (!t[v[key]] && (t[v[key]] = []), t[v[key]].push(v), t), {})
}
groupArrayByKey(arr, "id");

动态加载 JS 文件

在一些特殊的场景下,特别是一些库和框架的开发中,我们有时会去动态的加载 JS 文件并执行,下面是利用 Promise 进行了简单的封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function loadJS(files, done) {
// 获取head标签
const head = document.getElementsByTagName('head')[0];
Promise.all(files.map(file => {
return new Promise(resolve => {
// 创建script标签并添加到head
const s = document.createElement('script');
s.type = "text/javascript";
s.async = true;
s.src = file;
// 监听load事件,如果加载完成则resolve
s.addEventListener('load', (e) => resolve(), false);
head.appendChild(s);
});
})).then(done); // 所有均完成,执行用户的回调事件
}

loadJS(["test1.js", "test2.js"], () => {
// 用户的回调逻辑
});

利用闭包实现函数只执行一次

1
2
3
4
5
6
7
8
9
10
export function once (fn) {
// 利用闭包判断函数是否执行过
let called = false
return function () {
if (!called) {
called = true
fn.apply(this, arguments)
}
}
}

实现curring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function add(a, b, c) {
return a + b + c;
}
function currying(fn, ...args) {
function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args)
} else {
return function (...arg2) {
return curried.apply(this, args.concat(arg2));
}
}
}
return curried.apply(this, args)
}
const a1 = currying(add, 1,2,3);//6
const a2 = currying(add)(1)(2)(3);、、6

递归获取对象属性

如果让我挑选一个用的最广泛的设计模式,我会选观察者模式,如果让我挑一个我所遇到的最多的算法思维,那肯定是递归,递归通过将原始问题分割为结构相同的子问题,然后依次解决这些子问题,组合子问题的结果最终获得原问题的答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const user = {
info: {
name: "张三",
address: { home: "Shaanxi", company: "Xian" },
},
};

// obj是获取属性的对象,path是路径,fallback是默认值
function get(obj, path, fallback) {
const parts = path.split(".");
const key = parts.shift();
if (typeof obj[key] !== "undefined") {
return parts.length > 0 ?
get(obj[key], parts.join("."), fallback) :
obj[key];
}
// 如果没有找到key返回fallback
return fallback;
}

console.log(get(user, "info.name")); // 张三
console.log(get(user, "info.address.home")); // Shaanxi
console.log(get(user, "info.address.company")); // Xian
console.log(get(user, "info.address.abc", "fallback")); // fallback

大屏前端自适应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//公共方法
/* 根据浏览器大小推断缩放比例 */
export const getScale = (w: number, h: number) => {
const ww = window.innerWidth / w;
const wh = window.innerHeight / h;
return ww < wh ? ww : wh;
};

/* 浏览器监听 resize 事件 */
export const screeResize = (el: HTMLDivElement, W: number, H: number) => {
if (el) {
el.style.width = `${W}px`;
el.style.height = `${H}px`;
el.style.transform = `scale(${getScale(W, H)}) translate(-50% ,-50%)`;
}
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//应用
import { debounce } from "lodash";
import { screeResize } from "@/utils";
/* @description:大屏自适应模块*/
const dataScreenRef = ref<HTMLDivElement | null>(null);
const onResize = () => {
screeResize(dataScreenRef.value as HTMLDivElement, 1920, 1080);
};

onMounted(()=>{
if (dataScreenRef.value) {
onResize();
window.addEventListener("resize", debounce(onResize, 150));
}
1
2
3
4
5
6
7
8
9
10
//大屏容器样式
.screenContainer{
transform-origin: left top;
position: fixed;
width: 1920px;
height: 1080px;
top: 50%;
left: 50%;
user-select: none;
}

js获取url中指定参数的值(兼容hash)

1
2
3
4
5
6
export const getQueryString = (name, search) => {
search = search || window.location.search.substr(1) || window.location.hash.split("?")[1];
let reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
let r = search?.match(reg);
if (r != null) return unescape(r[2]); return null;
}

elementUI 封装自定义视频上传

1
2
3
4
5
6
<el-upload action="#" ref="upload" :auto-upload="false" :file-list="fileList" list-type="picture-card"
:limit="2" :on-change="onFileChange" :on-remove="onFileRemove" :on-exceed="handleExceed">
<el-icon>
<Plus />
</el-icon>
</el-upload>
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//上传图片list
const fileList = ref([]);
//文件状态改变时的钩子
const onFileChange = (uploadFile, uploadFiles) => {
//验证上传图片
let uploadStatus = uploadVerify(uploadFile);
if (uploadStatus == false) {
uploadFiles.splice(uploadFiles.length - 1, 1);
}
fileList.value = uploadFiles;
}
//文件移除时的钩子
const onFileRemove = (uploadFile, uploadFiles) => {
fileList.value = uploadFiles;
}
// 上传超过
const handleExceed = () => {
ElMessage.warning(`最多上传2个图片`);
}
//文件自定义上传
const onFileRequest = async () => {
//获取没有上传的图片,已经上传的我会给图片加上fileId
let noUploadList = fileList.value.filter((v) => !v.fileId);
let errorImgIndex = []; //错误图片索引
if (noUploadList.length) {
let promiseArr = [];
noUploadList.forEach((v, index) => {
promiseArr.push(uploadHandle(index, v.raw, v.name, "景点图片"));
});
let results = await Promise.all(promiseArr);
dialogLoading.value = false
results.forEach((v) => {
if (v.code == "1001") {
//引用类型,直接赋值就行
noUploadList[v.index].status = "success";
noUploadList[v.index].fileId = v.data;
} else {
errorImgIndex.push(v.index);
}
});
}
if (errorImgIndex.length) {
return ElMessage.warning(
`第${errorImgIndex.join(",")}图片上传错误,请更改`
);
}

}
//封装文件上传函数
const uploadHandle = (index, file, fileName, description = "") => {
return new Promise(async resolve => {
//如果名称带逗号
let splitName = fileName.split(",");
if (splitName.length > 1) {
var fileType = fileName
.substring(fileName.lastIndexOf(".") + 1)
.toLowerCase();
fileName = `${splitName[0] || 'random'}.${fileType}`
}
let uploadData = new FormData();
uploadData.append("file", file);
uploadData.append("fileName", fileName);
uploadData.append("description", description);
let [res] = await awaitWrap(uploadPic(uploadData))
res.index = index; //方便查找是哪一个
resolve(res);
})
}
//封装图片上传验证逻辑
const uploadVerify = (file) => {
let uploadStatus = true; //文件是否支持上传
let fileType = file.name
.substring(file.name.lastIndexOf(".") + 1)
.toLowerCase();
let restrictFileTypes = ["jpg", "png", "jpeg"];
const isLt2M = file.size / 1024 / 1024 < 10;
if (!restrictFileTypes.includes(fileType)) {
ElMessage.error("上传图片只能是 jpg、png、jpeg 格式!");
uploadStatus = false;
} else if (!isLt2M) {
ElMessage.error("上传图片大小不能超过 10MB!");
uploadStatus = false;
}
return uploadStatus;
}