项目中常用方法汇总
防抖
export function throttle (fn, interval = 500) { let canRun = true return function () { if (!canRun) return canRun = false setTimeout(() => { fn.apply(this, arguments) canRun = true }, interval) } }
节流
export function debounce (fn, wait = 500, immediate) { let timer return function () { if (immediate) { fn.apply(this, arguments) } if (timer) clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, arguments) }, wait) } }
获取文件名
export function downloadUrlFile (url, fileName) { url = url.replace(/\\/g, '/') const xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.responseType = 'blob' // xhr.setRequestHeader('Authorization', 'Basic a2VybWl0Omtlcm1pdA=='); xhr.onload = () => { if (xhr.status === 200) { // 获取文件blob数据并保存 saveAs(xhr.response, fileName) } } xhr.send() }
/**
* URL方式保存文件到本地
* @param data 文件的blob数据
* @param name 文件名
*/
function saveAs (data, name) {
var urlObject = window.URL || window.webkitURL || window
var exportBlob = new Blob([data])
var saveLink = document.createElementNS('http://www.w3.org/1999/xhtml', 'a')
saveLink.href = urlObject.createObjectURL(exportBlob)
saveLink.download = name
saveLink.click()
}
时间戳简析
export function dateFormat (fmt, date) { let ret const opt = { 'Y+': date.getFullYear().toString(), // 年 'm+': (date.getMonth() + 1).toString(), // 月 'd+': date.getDate().toString(), // 日 'H+': date.getHours().toString(), // 时 'M+': date.getMinutes().toString(), // 分 'S+': date.getSeconds().toString() // 秒 // 有其他格式化字符需求可以继续添加,必须转化成字符串 } for (const k in opt) { ret = new RegExp('(' + k + ')').exec(fmt) if (ret) { fmt = fmt.replace(ret[1], (ret[1].length === 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, '0'))) } } return fmt }
阿拉伯数字转换成大写汉字
export function numberParseChina (money) { // 汉字的数字 let cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'] // 基本单位 let cnIntRadice = ['', '拾', '佰', '仟'] // 对应整数部分扩展单位 let cnIntUnits = ['', '万', '亿', '兆'] // 对应小数部分单位 let cnDecUnits = ['角', '分', '毫', '厘'] // 整数金额时后面跟的字符 let cnInteger = '整' // 整型完以后的单位 let cnIntLast = '圆' // 最大处理的数字 let maxNum = 999999999999999.9999 // 金额整数部分 let integerNum // 金额小数部分 let decimalNum // 输出的中文金额字符串 let chineseStr = '' // 分离金额后用的数组,预定义 let parts if (money === '') { return '' } money = parseFloat(money) if (money >= maxNum) { // 超出最大处理数字 return '' } if (money === 0) { chineseStr = cnNums[0] + cnIntLast + cnInteger return chineseStr } // 转换为字符串 money = money.toString() if (money.indexOf('.') === -1) { integerNum = money decimalNum = '' } else { parts = money.split('.') integerNum = parts[0] decimalNum = parts[1].substr(0, 4) } // 获取整型部分转换 if (parseInt(integerNum, 10) > 0) { let zeroCount = 0 let IntLen = integerNum.length for (let i = 0; i < IntLen; i++) { let n = integerNum.substr(i, 1) let p = IntLen - i - 1 let q = p / 4 let m = p % 4 if (n === '0') { zeroCount++ } else { if (zeroCount > 0) { chineseStr += cnNums[0] } // 归零 zeroCount = 0 chineseStr += cnNums[parseInt(n)] + cnIntRadice[m] } if (m === 0 && zeroCount < 4) { chineseStr += cnIntUnits[q] } } chineseStr += cnIntLast } // 小数部分 if (decimalNum !== '') { let decLen = decimalNum.length for (let i = 0; i < decLen; i++) { let n = decimalNum.substr(i, 1) if (n !== '0') { chineseStr += cnNums[Number(n)] + cnDecUnits[i] } } } if (chineseStr === '') { chineseStr += cnNums[0] + cnIntLast + cnInteger } else if (decimalNum === '') { chineseStr += cnInteger } return chineseStr }
数组去重
export function filterArrayByKey (arr, key) { if (!(Array.isArray(arr))) return [] const hash = {} return arr.filter((item) => { if (hash[item[key]]) return false hash[item[key]] = true return true }) }
格式化金额数字,第一个参数是数字,第二个是保留几位小数,三位加逗号
export function formatMoney (s, n) { if (s) { n = n > 0 && n <= 20 ? n : 2 s = parseFloat((s + '').replace(/[^\d.-]/g, '')).toFixed(n) + '' let l = s.split('.')[0].split('').reverse() let r = s.split('.')[1] let t = '' for (let i = 0; i < l.length; i++) { t += l[i] + ((i + 1) % 3 === 0 && (i + 1) !== l.length ? ',' : '') } return t.split('').reverse().join('') + '.' + r } }
清除一个数组中值为空的项
export const cleanEmptyInArray = function (array) { let [...newArray] = array const count = newArray.length for (let i = count - 1; i >= 0; i--) { if (newArray[i] === '' || newArray[i] === null || newArray[i] === undefined) { newArray.splice(i, 1) } } return newArray }
基于ekement表格合计方法
/* 合计方法 */
Vue.prototype.$calcSummaries = function (columns, data, showCheckBox) {
const sums = []
columns.forEach((column, index) => {
const values = data && data.map(item => Number(item[column.property]))
// console.log(values)
if (column.columnKey) {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr)
if (!isNaN(value)) {
switch (column.columnKey) {
case 'amount':
return (Number(prev) + Number(curr)).toFixed(2)
case 'number':
return (Number(prev) + Number(curr)).toFixed(3)
case 'int':
return (Number(prev) + Number(curr))
case 'amount0':
return (Number(prev) + Number(curr)).toFixed(0)
default:
return (Number(prev) + Number(curr)).toFixed(2)
}
} else {
switch (column.columnKey) {
case 'amount':
return Number(prev).toFixed(2)
case 'number':
return Number(prev).toFixed(3)
case 'int':
return Number(prev)
default:
return Number(prev).toFixed(6)
}
}
}, 0)
} else {
sums[index] = ''
}
})
if (showCheckBox) {
sums[2] = '合计'
} else {
sums[1] = '合计'
}
return sums
}
页面内部合计方法 getSummaries (param) { const { columns, data } = param return this.$calcSummaries(columns, data) } 需要合计行加配置项 column-key="amount"
过滤对象中空值
traverse (data) { const keys = Object.keys(this.formData) for (let item of keys) { if (!this.isDef(this.formData[item])) this.$delete(this.formData, item) } }, isDef (v) { return v !== undefined && v !== null && v !== '' },
值级map转换
// 付款条款类型 export const PAYMENT_VALUE = new Map([ ['ADVANCE', '预付款'], ['PAY_FOR_RECEIVE', '到货款'], ['PAY_FOR_PROGRESS', '进度款'], ['FINAL_FEE', '完工款'], ['PAY_FOR_ACCEPT', '验收款'], ['QUALITY_ASSURANCE', '质保金'], ['PERFORMANCE_BOND', '履约保证金'], ['GOODS_OFFSET_COST', '以货抵费'], ['OTHERS', '其他'] ])
getpaymentVal (val) {
return PAYMENT_VALUE.get(val)
},
JSON去重
unique (type, arr) { // 根据唯一标识no来对数组进行过滤 const res = new Map() // 定义常量 res,值为一个Map对象实例 // 返回arr数组过滤后的结果,结果为一个数组 过滤条件是,如果res中没有某个键,就设置这个键的值为1 return arr.filter((arr) => !res.has(arr[type]) && res.set(arr[type], 1)) }, // type 删选条件 // arr 数据源
日期格式化方法
/** * 日期格式化方法 * @param {string} date [可选] 要格式化的时间 * @param {string} fmt [可选] 时间格式 * * 月(M)、日(D)、12小时(h)、24小时(H)、分(m)、秒(s)、周(E)、季度(Q) ==> 可以用 1-2 个占位符 * 年(Y) ==> 1-4 个占位符 * 毫秒(S) ==> 1 个占位符(是 1-3 位的数字) */ export function formatDate (date = Date.now(), fmt = 'YYYY-MM-DD HH:mm:ss') { if (!date) return '-' date = new Date(date) const o = { 'M+': date.getMonth() + 1, // 月份 'D+': date.getDate(), // 日 'h+': date.getHours() % 12 || 12, // 12小时 'H+': date.getHours(), // 24小时 'm+': date.getMinutes(), // 分钟 's+': date.getSeconds(), // 秒 'Q+': Math.floor((date.getMonth() + 3) / 3), // 季度 S: date.getMilliseconds() // 毫秒 } const week = { 0: '\u65e5', 1: '\u4e00', 2: '\u4e8c', 3: '\u4e09', 4: '\u56db', 5: '\u4e94', 6: '\u516d' } if (/(Y+)/.test(fmt)) { fmt = fmt.replace( RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length) ) } if (/(E+)/.test(fmt)) { fmt = fmt.replace( RegExp.$1, (RegExp.$1.length > 1 ? RegExp.$1.length > 2 ? '\u661f\u671f' : '\u5468' : '') + week[date.getDay() + ''] ) } Object.keys(o).forEach(k => { if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace( RegExp.$1, RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length) ) } }) return fmt }
/**
* 获取格式化分时秒钟的毫秒数
*
* @param {string} time 根据分时秒格式(如: 12:11.300),获取毫秒
* @return {Number}
*/
export function getMilliseconds (time) {
const reg = /(\d+):(\d+)(\.(\d+))*/g
const ret = reg.exec(time)
if (!ret) {
return 0
}
const min = ret[1] || 0
const sec = ret[2] || 0
const mil = ret[4] || 0
return (
parseInt(min, 10) * 60 * 1000 + parseInt(sec, 10) * 1000 + parseInt(mil, 10)
)
}
/**
* 获取格式化分时秒钟的总秒数
*
* @param {string} time 根据分时秒格式(如: 12:11.300),获取秒
* @return {Number}
*/
export function getSeconds (time) {
const mil = getMilliseconds(time)
return mil / 1000
}
项目过滤器
import Vue from 'vue' import _lo from 'lodash' import { formatDate } from '@/util/datetime' import { numberFormat } from '@/util/utils' const filters = { /** * 数据特殊处理 * val: 要转换的数据 * format: 数据转换格式,是个对象,type指定要转换的类型(目前有map),其他的属性则是特殊类型的数据处理。 * 如:format: { type: 'map', statusMap: { 0: '已启用', 1: '已停用'}} * format: { type: 'rate'} * format: { type: 'date', fmt: 'YYYY-MM-DD'} * 总之,format对象传入要处理的数据类型和要转换的特殊的参数,目前type只有三种类型,有需要的可自行添加。 */ getScope: (val, format = { type: '' }, otherData) => { if (typeof val === 'undefined' || val === null || val === '') return '-' // 非空判断 if (!(val || format.type)) return '-' switch (format.type) { case 'map': return format.statusMap[val] || '-' case 'reserveZeroMap': return format.statusMap[val] ?? '-' case 'reserveZero': return val ?? '-' case 'join': let res = val.map((item) => item.valueName).join('、') return res || '-' case 'mapPost': return format.statusMap[val] || val case 'rate': return val + '%' case 'fn': return format.fn(val, otherData) case 'date': return formatDate(val, format.fmt) case 'price': return numberFormat(val, format.digit || 2, '.', ',', 'round') case 'number': // 对 NAN 数据进行过滤 if (Number.isNaN(val)) val = 0 return _lo.round(+val, 3).toFixed(3) case 'total': return '共计' + val case 'shipAddress': if (otherData.transportMode === '3') { return '-' } else { return val } case 'isHas': return val ? '有' : '无' default: return val || '-' } } } Object.keys(filters).forEach(key => Vue.filter(key, filters[key]))
// * 验证金额以100,000.00的方式 // * @param {*} number 要格式化的数字 // * @param {*} decimals 保留几位小数 // * @param {*} decPoint 小数点符号 // * @param {*} thousandsSep 千分位符号 // * @param {*} roundtag 舍入参数,默认 'ceil' 向上取,'floor'向下取,'round' 四舍五入 export function numberFormat (number, decimals, decPoint, thousandsSep, roundtag) { number = (number + '').replace(/[^0-9+-Ee.]/g, '') roundtag = roundtag || 'ceil' // 'ceil','floor','round' var n = !isFinite(+number) ? 0 : +number var prec = !isFinite(+decimals) ? 0 : Math.abs(decimals) var sep = (!thousandsSep) ? ',' : thousandsSep var dec = (typeof decPoint === 'undefined') ? '.' : decPoint var s = '' var toFixedFix = function (n, prec) { var k = Math.pow(10, prec) return '' + parseFloat(Math[roundtag](parseFloat((n * k).toFixed(prec * 2))).toFixed(prec * 2)) / k } s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.') var re = /(-?\d+)(\d{3})/ while (re.test(s[0])) { s[0] = s[0].replace(re, '$1' + sep + '$2') } if ((s[1] || '').length < prec) { s[1] = s[1] || '' s[1] += new Array(prec - s[1].length + 1).join('0') } return s.join(dec) }
基于ele 输入格式验证input
<template> <el-input v-bind="$attrs" v-bind:value="value" v-on="inputListeners"> <template slot="append"> <slot name="append"></slot> </template> <template slot="prepend"> <slot name="prepend"></slot> </template> <template slot="prefix"> <slot name="prefix"></slot> </template> <template slot="suffix"> <slot name="suffix"></slot> </template> </el-input> </template> <script> /** * @Description: input 全局注册组件,新增 mode 和 pattern 属性 (二 选 一) * @Description: mode 是定义好的一些 固定模式,可扩展 * @Description: pattern 是正则表达式,自定义程度高 * @Description: 所有 el-input 官方属性都可继承 * @use: * <vd-el-input * v-model="xxxx" * placeholder="请输入" * mode="thereDecimal" * clearable></vd-el-input> * @author Miracle TJF * @date 2020/4/10 */ export default { name: 'VdInput', inheritAttrs: false, props: { value: { type: [String, Number], default: '' }, mode: { type: String, default: '' }, pattern: { type: RegExp, default () { return /.*/ } } }, computed: { inputListeners: function () { let vm = this // `Object.assign` 将所有的对象合并为一个新对象 return Object.assign({}, // 我们从父级添加所有的监听器 this.$listeners, // 然后我们添加自定义监听器, // 或覆写一些监听器的行为 { // 这里确保组件配合 `v-model` 的工作 input: function (val) { vm.interceptValue(val) } } ) } }, created () { this.initProps() }, data () { return { currentPattern: this.pattern } }, methods: { initProps () { if (this.mode && modeMap[this.mode]) { this.currentPattern = modeMap[this.mode] } }, interceptValue (val) { if (!val) { return this.$emit('input', '') } const reg = this.generateReg() const match = val.match(reg) if (match && match[1]) { this.$emit('input', match[1]) } }, generateReg () { const source = this.currentPattern.source const flags = this.currentPattern.flags return new RegExp(`(${source})`, flags) } } } // 预置格式 (需要额外的可自行完善,注释要写清楚) const modeMap = { int: /\d+/, // 整数 twoDecimal: /([1-9]\d*|0)(\.\d{0,2})?/, // 两位小数 threeDecimal: /([1-9]\d*|0)(\.\d{0,3})?/, // 三位小数 threeIntegerTwoDecimal: /([1-9]\d{0,2}|0)(\.\d{0,2})?/, // 最多三位整数,最多两位小数 thereDecimal: /\d+(\.\d{0,3})?/, // 三位小数 twoNotEqualDecimal: /^([1-9][0-9]*)+(\.\d{0,2})?/, // 大于0两位小数 natThereDecimal: /^-?\d+(\.\d{0,3})?/, // 正负三位小数 sixeDecimal: /\d{0,9}(\.\d{0,6})?/, // 小数点前九后六 eightDecimal: /\d{0,8}(\.\d{0,2})?/, // 小数点前八后二 thirteenDecimal: /\d{0,13}(\.\d{0,2})?/, // 小数点前十八后二 tenAndTwo: /([1-9]\d{0,9}|0)(\.\d{0,2})?/, // 前十后二 canZeroInt: /^(0|[1-9][0-9]*)$/, // 只能输入0或者非0开头的数字 volumeDecimal: /^[0-9](\.\d{0,2})?$|^([1-3][0-9]{2}|[1-9][0-9]{0,1})(\.\d{0,2})?$|^400/ // 大于0小于等于400两位小数 } </script>
// 挂载全局
import Vue from 'vue'
// 输入框组件封装
import VdElInput from '@/components/globals/VdElInput'
Vue.component('vd-el-input', VdElInput)
// 使用
<vd-el-input
v-model.trim="scope.row.amountRate"
type="text"
placeholder="请输入"
style="width:100%"
:maxlength="6"
clearable
mode="twoNotEqualDecimal"
></vd-el-input>
递归查找
function getGroupName(data, id) { let a if (!data) { return } for (let i = 0; i < data.length; i++) { const item = data[i] // console.log(item) if (item.categoryCode === id) { a = item.categoryFullName return a } else if (item.children && item.children.length > 0) { if (getGroupName(item.children, id)) { return a = getGroupName(item.children, id) } } } return a } let str = getGroupName(array, 8) console.log('str', str)