开发技巧
- 1、数组的重复项的合并
- 2、数组计算某个属性之和
- 3、枚举转换
- 4、序列化排序,根据某个属性
- 5、计算两个数据间的价格差异
- 6、vant 表单校验不通过,跳转到一个校验的地方
- 7、下载文件
- 8、判断增加样式class
- 9、创建32位随机字符串
- 10、对象数组去重
- 11、防抖函数
- 12、切换类名
- 13、输出纯文本
- 14、传换链接的参数
- 15、将json数据转换成url链接
- 16、从给定的URL或当前页面URL中获取查询参数
- 17、计算两个日期的间隔
- 18、时间格式转时间戳
- 19、组件表单检验工具函数
- 20、数字/货币金额 (只支持正数、不支持校验千分位分隔符)
- 21、 正数(大于等于零的数字包括小数)
- 22、 手机和固化校验
- 23、 含有数字字母且必须有大写字母
- 24、并发请求
- 25、el-cascader 初始默认值
- 26、数组中检查某个属性,是否相同
- 27、国标vin加权系数校验规则
- 28、防抖函数
- 29、 获取当天年月日
- 30、picker 禁用时间
- 31、el-tabel 禁选
- 32、get params 序列化传参
- 33、el-table 合计
- 34、el-form 样式问题
1、数组的重复项的合并
// prcdTermType 和 acctFnm 重复则合并,反之返回
// 重复项合并
mergeDuplicates(arr) {
debugger;
const obj = {
prcdTermType: null,
acctFnm: null,
acctNo: null,
};
const mergedArray = arr.reduce((result, item) => {
const existingItem = result.find(
existing => existing.prcdTermType === item.prcdTermType && existing.acctFnm === item.acctFnm,
);
if (existingItem) {
existingItem.pymtPrice = Number(existingItem.pymtPrice) + Number(item.pymtPrice);
obj.prcdTermType = existingItem.prcdTermType;
obj.acctFnm = existingItem.acctFnm;
obj.acctNo = existingItem.acctNo;
} else {
result.push(item);
}
return result;
}, []);
return {mergedArray, obj};
},
2、数组计算某个属性之和
// 计算数组某个属性的总额
getTableToll(arr, keyName) {
let total = 0;
total = arr.reduce((total, currentValue, currentIndex, arr) => {
return currentValue[keyName] ? Number(total) + Number(currentValue[keyName]) : Number(total);
}, 0);
return total;
},
3、枚举转换
// 项目小类枚举
prcdPrjList: [
{value: '1', label: '意向金'},
{value: '2', label: '定金'},
{value: '3', label: '上牌'},
{value: '4', label: '抵押'},
{value: '5', label: '全款车款'},
{value: '6', label: '首付款'},
{value: '7', label: '前置利息'},
{value: '8', label: '咨询费'},
{value: '9', label: 'GPS'},
{value: '10', label: '精品'},
{value: '11', label: '粘性产品'},
{value: '12', label: '小指标'},
{value: '13', label: '其他项目'},
{value: '14', label: '上牌押金'},
{value: '15', label: '置换保证金'},
{value: '16', label: 'GD应收'},
{value: '17', label: 'GD收入'},
{value: '18', label: '高开金额'},
{value: '19', label: '其他押金'},
],
getJcprcdPrjList(str) {
if (!str) {
return;
}
const arr = str.split(',');
const nameArr = [];
arr.forEach(item => {
const josn = this.prcdPrjList.find(c => {
return c.value === item;
});
if (josn === undefined) {
nameArr.push(item);
} else {
nameArr.push(josn.label);
}
});
return nameArr.toString();
},
4、序列化排序,根据某个属性
// 序列化排序
sortByPayment(array, property) {
return array.sort((a, b) => {
const paymentOptions = ['5', '6'];
const indexA = paymentOptions.indexOf(a[property]);
const indexB = paymentOptions.indexOf(b[property]);
if (indexA !== -1 && indexB === -1) {
return -1; // a排在b之前
} else if (indexA === -1 && indexB !== -1) {
return 1; // a排在b之后
}
return 0; // 保持原有顺序
});
},
5、计算两个数据间的价格差异
// 该函数用于计算两个数组中的价格差异。它通过将arr2数组中与arr中的每个元素具有相同prcdPrj属性的元素进行累加,然后将每个元素的prcdPrice属性减去累加结果得到价格差异,并返回一个新数组。
calculatePriceDifferences(arr, arr2) {
debugger;
const result = arr.map(item => {
debugger;
const sumInArr2 = arr2
.filter(i2 => i2.prcdPrj == item.prcdPrj)
.reduce(
(total, current) =>
total + Number(current.pymtPrice) + Number(current.depositAmount) + Number(current.zhDeduction),
0,
);
return {
prcdPrj: item.prcdPrj,
id: item.id,
name: item.name,
priceDifference: item.prcdPrice - sumInArr2,
};
});
return result;
},
6、vant 表单校验不通过,跳转到一个校验的地方
export function focusFirstErrorField() {
const isError = document.getElementsByClassName('van-field--error');
if (isError.length > 0) {
const firstErrorField = isError[0].querySelector('.van-field__control');
if (firstErrorField) {
firstErrorField.focus();
}
}
}
7、下载文件
// 下载文件
export function downloadFile(obj, name, suffix) {
const url = window.URL.createObjectURL(new Blob([obj]));
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
const fileName = parseTime(new Date()) + '-' + name + '.' + suffix;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
8、判断增加样式class
/**
* Check if an element has a class
* @param {HTMLElement} elm
* @param {string} cls
* @returns {boolean}
*/
export function hasClass(ele, cls) {
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
}
/**
* Add class to element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function addClass(ele, cls) {
if (!hasClass(ele, cls)) ele.className += ' ' + cls;
}
// 使用
// 导入addClass函数(假设在同一文件或已导入)
import { hasClass, addClass } from './utils.js';
// 获取HTML元素
let myElement = document.getElementById('myDiv');
// 定义要添加的CSS类
const newClass = 'myClass';
// 检查元素是否已有该类,如果没有,则添加
if (!hasClass(myElement, newClass)) {
addClass(myElement, newClass);
}
console.log(myElement.className);
9、创建32位随机字符串
/**
* @returns {string}
*/
export function createUniqueString() {
const timestamp = +new Date() + '';
const randomNum = parseInt((1 + Math.random()) * 65536) + '';
return (+(randomNum + timestamp)).toString(32);
}
10、对象数组去重
/**
* @param {Array} arr
* @returns {Array}
*/
export function uniqueArr(arr) {
return Array.from(new Set(arr));
}
11、防抖函数
/**
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result;
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp;
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
};
return function(...args) {
context = this;
timestamp = +new Date();
const callNow = immediate && !timeout;
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
context = args = null;
}
return result;
};
}
12、切换类名
/**
* @param {HTMLElement} element
* @param {string} className
*/
export function toggleClass(element, className) {
if (!element || !className) {
return;
}
let classString = element.className;
const nameIndex = classString.indexOf(className);
if (nameIndex === -1) {
classString += '' + className;
} else {
classString = classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length);
}
element.className = classString;
}
13、输出纯文本
/**
* @param {string} val
* @returns {string}
*/
export function html2Text(val) {
const div = document.createElement('div');
div.innerHTML = val;
return div.textContent || div.innerText;
}
// 使用
import { html2Text } from './utils';
let htmlString = '<p>Hello <strong>World!</strong></p>';
let plainText = html2Text(htmlString);
console.log(plainText); // 输出: "Hello World!"
14、传换链接的参数
/**
* @param {string} url
* @returns {Object}
*/
export function param2Obj(url) {
const search = url.split('?')[1];
if (!search) {
return {};
}
return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"').replace(/\+/g, ' ') + '"}');
}
// 使用
import { param2Obj } from './utils';
let url = 'http://example.com/?name=John&age=30';
console.log(param2Obj(url));
// 输出: {"name": "John", "age": "30"}
15、将json数据转换成url链接
/**
* @param {Object} json
* @returns {Array}
*/
export function param(json) {
if (!json) return '';
return cleanArray(
Object.keys(json).map(key => {
if (json[key] === undefined) return '';
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
}),
).join('&');
}
// 使用
import { param } from './utils.js';
let json = {
name: 'John Doe',
age: 30,
city: undefined,
job: 'Developer'
};
let urlParams = param(json);
console.log(urlParams); // 输出:name=John%20Doe&age=30&job=Developer
16、从给定的URL或当前页面URL中获取查询参数
/**
* @param {string} url
* @returns {Object}
*/
export function getQueryObject(url) {
url = url == null ? window.location.href : url;
const search = url.substring(url.lastIndexOf('?') + 1);
const obj = {};
const reg = /([^?&=]+)=([^?&=]*)/g;
search.replace(reg, (rs, $1, $2) => {
const name = decodeURIComponent($1);
let val = decodeURIComponent($2);
val = String(val);
obj[name] = val;
return rs;
});
return obj;
}
// 使用
import { getQueryObject } from './utils.js'; // 假设此函数在一个名为utils.js的模块中
// 示例URL
let url = 'http://example.com/?name=John&age=30';
// 调用函数获取查询参数对象
let queryObj = getQueryObject(url);
console.log(queryObj);
// 输出: { name: 'John', age: '30' }
17、计算两个日期的间隔
/** 计算来个日期的间隔
* **/
// eslint-disable-next-line camelcase
export function t_parseTime_between(startTime, endTime) {
if (!startTime || !endTime) {
return 0;
}
var betweenTIime = endTime - startTime;
return (betweenTIime / 1000 / 24 / 3600).toFixed();
}
18、时间格式转时间戳
/**
* 时间格式转时间戳
* **/
export function dataAlltime(data) {
if (data) {
let datatiem = '';
if (data.length === 19) {
datatiem = data.replace(/-/g, '/');
return new Date(datatiem).getTime();
} else {
datatiem = (data + ':00').replace(/-/g, '/');
return new Date(datatiem).getTime();
}
} else {
return null;
}
}
19、组件表单检验工具函数
// 组件表单检验工具函数
export function formValidate(ref, callback) {
ref.validate(valid => {
callback(valid);
});
}
// 使用案例
<template>
<el-form ref="myForm" :model="form" :rules="rules">
<!-- 表单元素 -->
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username"></el-input>
</el-form-item>
<!-- 其他表单元素... -->
<el-button ="handleSubmit">提交</el-button>
</el-form>
</template>
<script>
import { formValidate } from './validationUtils';
export default {
data() {
return {
form: {
username: '',
// ...其他表单字段
},
rules: {
username: [
{ required: true, message: '用户名不能为空', trigger: 'blur' },
// ...其他校验规则
],
},
};
},
methods: {
handleSubmit() {
formValidate(this.$refs.myForm, (valid) => {
if (valid) {
// 提交表单数据到服务器或执行其他操作
console.log('表单验证通过,可以提交:', this.form);
} else {
this.$message.error('表单验证未通过,请检查输入');
}
});
},
},
};
</script>
20、数字/货币金额 (只支持正数、不支持校验千分位分隔符)
// 数字/货币金额 (只支持正数、不支持校验千分位分隔符)
export function monetaryAmount(rule, value, callback) {
const reg = /(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0)$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/;
if (value === '' || value === undefined || value === null) {
callback();
} else {
if (!reg.test(value)) {
callback(new Error('请输入数字'));
} else {
callback();
}
}
}
21、 正数(大于等于零的数字包括小数)
// 正数(大于等于零的数字包括小数)
export function positive_number(rule, value, callback) {
const reg = /^[+][1-9](\d+)$|^[+]{0,1}(\d+\.\d+)$/;
if (value === '' || value === undefined || value === null) {
callback();
} else {
if (!reg.test(value)) {
callback(new Error('请输入正确的数字'));
} else {
callback();
}
}
}
22、 手机和固化校验
// 手机和固化校验
export function validateTLEPhone(rule, value, callback) {
const mobileReg = /^(([1][3,4,5,7,8]\d{9})|([0]\d{10,11})|(\d{7,8})|(\d{4}|\d{3})-(\d{7,8}))$/;
const phoneReg = /^\d{3}-\d{8}|\d{4}-\d{7}$/;
if (!value || value === '') {
callback(new Error('请输入联系方式'));
} else if (value.length === 11) {
console.log("value.indexOf('-')", value.indexOf('-') !== -1);
if (value.indexOf('-') !== -1) {
callback();
} else {
if (!mobileReg.test(value)) {
callback(new Error('请输入正确的手机号码或者固话号码(固话格式:区号-号码)'));
} else {
callback();
}
}
} else if (value.length === 12) {
if (!phoneReg.test(value)) {
callback(new Error('请输入正确的手机号码或者固话号码(固话格式:区号-号码)'));
} else {
callback();
}
} else {
callback(new Error('请输入正确的手机号码或者固话号码(固话格式:区号-号码)'));
}
}
23、 含有数字字母且必须有大写字母
// 含有数字字母且必须有大写字母
export function accountNumberTwo(rule, value, callback) {
const reg = /^(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]+$/;
if (value === '' || value === undefined || value === null) {
callback();
} else {
if (!reg.test(value)) {
callback(new Error('请输入含有数字字母且必须有大写字母'));
} else {
callback();
}
}
}
24、并发请求
async getStorefrontAccounts() {
debugger;
const promises = [
crudCwOrgAcct.lists({ cpAcctId: this.piaoForm.form.cpAcctId }),
crudCwOrgAcct.lists({ szAcctId: this.piaoForm.form.szAcctId })
];
try {
const [cpAcctRes, szAcctRes] = await Promise.all(promises);
console.log('保证金账户 (CP Acct)', cpAcctRes);
console.log('保证金账户 (SZ Acct)', szAcctRes);
// 如果需要进一步处理结果,可以在这里操作 res.content
// if (cpAcctRes && cpAcctRes.content) { ... }
// if (szAcctRes && szAcctRes.content) { ... }
} catch (error) {
console.error('获取保证金账户信息时发生错误', error);
}
},
25、el-cascader 初始默认值
<el-cascader
v-model="query.vehBrndlsit"
placeholder="车辆品牌"
:options="getBrandlist"
filterable
:props="defaultParamsSearch"
clearable
/>
getBrandlist: [],
defaultParamsSearch: {
label: 'name',
value: 'name',
children: 'children',
expandTrigger: 'hover',
checkStrictly: true
},
getFirstOptionPath(options, children = 'children', name = 'name') {
// 递归函数,用于获取第一个选项的值路径
if (options && options.length > 0) {
const firstOption = options[0];
if (children in firstOption && firstOption[children].length > 0) {
// 如果有子选项,则继续递归
return [firstOption[name], ...this.getFirstOptionPath(firstOption[children])];
} else {
// 如果是叶子节点,直接返回其值
return [firstOption[name]];
}
}
return [];
}
使用
this.query.vehBrndlsit = this.getFirstOptionPath(this.getBrandlist);
26、数组中检查某个属性,是否相同
checkUniformProperty(array, propertyName,) {
if (array.length === 0) return true; // 空数组视为所有元素相同
const firstValue = array[0][propertyName];
return array.every(item => item[propertyName] === firstValue);
},
27、国标vin加权系数校验规则
validateVIN(vin) {
// 正则表达式验证
const vinRegex = /^[A-HJ-NPR-Z0-9]{17}$/;
if (!vinRegex.test(vin)) {
return false;
}
// 校验位计算
const vinMapWeighting = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2];
const vinMapValue = {
0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9,
A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8,
J: 1, K: 2, L: 3, M: 4, N: 5, P: 7, R: 9,
S: 2, T: 3, U: 4, V: 5, W: 6, X: 7, Y: 8, Z: 9
};
let amount = 0;
for (let i = 0; i < 17; i++) {
const char = vin[i].toUpperCase();
const value = vinMapValue[char];
const weight = vinMapWeighting[i];
amount += value * weight;
}
const remainder = amount % 11;
const checkDigit = remainder === 10 ? 'X' : remainder.toString();
return vin[8].toUpperCase() === checkDigit;
}
28、防抖函数
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result;
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp;
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args);
context = args = null;
}
}
};
return function(...args) {
// context = this;
timestamp = +new Date();
const callNow = immediate && !timeout;
// 如果延时不存在,重新设定延时
if (!timeout) {
timeout = setTimeout(later, wait);
}
if (callNow) {
result = func.apply(this, args);
}
return result;
};
}
应用
import { debounce } from '@/utils/index.js'; // 确保正确引入 debounce 函数
export default {
data() {
return {
outWhList: []
};
},
methods: {
通过箭头函数方式
outWarehouseDebounced: debounce((orgId) => {
this.outWhList = [];
const params = {
orgId: orgId,
level: 1,
isValid: true,
storeMode: 2
};
crudJpAllocateDetail
.getAll(params)
.then(res => {
this.outWhList = res;
});
}, 300, false) // 300 毫秒内只调用一次
}
};
29、 获取当天年月日
-当天年月日
getNowTime() {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
const thisDayDate = `${year}-${month}-${day}`;
return thisDayDate;
},
-获取时分,转换成分钟
timeToMinutes(timeStr) {
const [hours, minutes] = timeStr.split(':').map(Number);
return hours * 60 + minutes;
},
30、picker 禁用时间
computed: {
pickerOptions() {
return {
disabledDate: (time) => {
return time.getTime() < Date.now() - 8.64e7; // 禁用以前的日期,今天不禁用
// return date.getTime() <= Date.now(); //禁用今天以及以前的日期
}
};
}
},
- 时间范围在当天到 30后的范围可选
disabledDate: time => {
const endDateTime = new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() + Number(30), 8);
const dd = new Date().getTime();
return (
time.getTime() < dd || time.getTime() > new Date(endDateTime).getTime()
);
}
31、el-tabel 禁选
- 勾选的方法
<el-table-column
align="center"
fixed="left"
type="selection"
:selectable="checkSelectable"
:reserve-selection="true"
width="40"
/>
checkSelectable(el) {
//false 表示禁选
return el.Selectable == false;
},
- 数组处理的方法
arr.forEach((item) => {
const currentQty = item.orderQty - item.executionsQty;
item.Selectable = currentQty === 0 ? false : true;
});
32、get params 序列化传参
- 对象序列化
let obj = {
methods: 'get'
id: 1,
name: 'yjn'
}
qs.stringify(obj)
methods=get&id=1&name=yjn
- 数组序列化
- 数组形式
let arr = [2,3]
qs.stringify({a:arr})
// 'arr[0]=2&arr[1]=3'
- 处理默认带下标格式
// 常用并推荐使用
let arr = [2,3]
qs.stringify({a:arr},{indices:false});
// 'arr=2&arr=3'
- 示例
export function group(params) {
if (params.page == undefined) {
params.page = 0;
params.size = 9999;
params.sort = "id,desc";
}
return request({
url: "/api/yw/ywBxConfig/group?" + qs.stringify(params, { indices: false }),
method: "get"
});
}
33、el-table 合计
- 表格
- 正常写法
getSummaries({columns}) {
const jsArr = [
'amt',
'costAmt',
'qty',
'recAmt'
];
return [
columns.map((column, index) => {
if (index === 0) {
return '合计';
}
let type = null;
if (column.property.indexOf('.') === -1) {
type = column.property;
} else {
type = column.property.split('.')[1];
}
if (jsArr.indexOf(type) !== -1) {
return this.crud.rows[`sum${this.capitalizeFirstLetter(type)}`];
}
})
];
},
- crud 的写法
tableBottomTotal({columns}) {
if (crud.columnShowArr.length === 0 && crud.sumBackend.length === 0) {
return false;
}
const jsArr = [...crud.columnShowArr];
const sumsArr = [...crud.sumBackend];
const getExistingValues = (row, keys) => {
const values = {};
if (!row) return values;
// 创建一个键映射对象,将 jsArr 的键映射到 keys 的键
// crud.columnShowArr 和 crud.sumBackend 字段顺序需要一一对应(切记)
const keyMap = {};
jsArr.forEach((j, index) => {
keyMap[keys[index]] = j;
});
keys.forEach(key => {
// 增加判断:只有当 key 在 sumBackend 中存在且 key 在 row 中存在时,才存入 values
if (sumsArr.includes(key) && key in row) {
// 使用映射关系获取对应的 jsArr 键
const mappedKey = keyMap[key];
if (mappedKey !== undefined) {
values[mappedKey] = row[key];
}
}
});
return values;
};
const arr = columns.map((column, index) => {
if (index === 0) return '合计';
const type = column.property.includes('.')
? column.property.split('.')[1]
: column.property;
// 判断 crud.rows 是否有值,防止首次进入, 接口未走完,crud.rows 为「」
if (typeof crud.rows === 'object' && crud.rows !== null && Object.keys(crud.rows).length > 0) {
if (jsArr.includes(type) && crud.rows && crud.rows.content.length > 0) {
// 提取对应数据行的值
const valueObj = getExistingValues(crud.rows, sumsArr);
if (valueObj !== null) {
const objString = valueObj[type] !== undefined ? valueObj[type] : '';
return objString;
}
}
}
return '';
});
return [arr];
},
34、el-form 样式问题
- :inline=“false” 宽度自适应
- :inline=“true” 宽度不会自适应