封装项目中常用的工具函数

☼ 注:笔者是一名卑微的小前端,文章是根据自己当前项目做的笔记,具体应用请参照自己实际项目情况

1、cookie操作
import cookie from 'cookie'

// 增加时间秒数
export function dateAddSeconds(sec) {
    return new Date((new Date()).getTime() + (sec * 1000))
}

export class Cookie {
    static setCookie(name, val, option) {
        const v = (typeof val === 'string') ? val : JSON.stringify(val)
        document.cookie = cookie.serialize(name, v, option)
    }

    static setCookieExpireInSecond(name, val, second, option) {
        Cookie.setCookie(name, val, { expires: dateAddSeconds(second), ...option })
    }

    static getCookie(cName) {
        const p = cookie.parse(document.cookie)
        if (cName in p) {
            return p[cName]
        }
        return null
    }

    static getJSONCookie(cName) {
        return JSON.parse(Cookie.getCookie(cName))
    }

    static deleteCookie(cName) {
        Cookie.setCookie(cName, '', { maxAge: -1 })
    }

}
2、本地存储
import { Cookie } from './cookie'

const PREFIX = 'cache_'

export default class Cache {
    static getItem(k) {
        try {
            return JSON.parse(localStorage.getItem(PREFIX + k))
        } catch (e) {
            return undefined
        }
    }

    static setItem(k, v) {
        const d = typeof v === 'string' ? v : JSON.stringify(v)
        localStorage.setItem(PREFIX + k, d)
    }

    static removeItem(k) {
        localStorage.removeItem(PREFIX + k)
    }

    static clear() {
        Object.keys(localStorage).filter(i => i.startsWith(PREFIX)).forEach(j => localStorage.removeItem(j))
    }

    // 用户信息及登录状态
    static getUserInfo() {
        const userData = Cookie.getCookie(`${PREFIX}userData`)
        try {
            return JSON.parse(userData)
        } catch (e) {
            return {}
        }
    }

    static setUserInfo(v) {
        Cookie.setCookie('isLogin', '0')
        const d = typeof v === 'string' ? v : JSON.stringify(v)
        Cookie.setCookie(`${PREFIX}userData`, d)
    }

    static clearUserInfo() {
        Cookie.setCookie('isLogin', '-1')
        Cookie.deleteCookie('isLogin')
        Cookie.deleteCookie(`${PREFIX}userData`)
    }
}
3、表单验证
// 表单验证
const regs = {
    number: /^[0-9]*$/,
    integer: /^[+]{0,1}(\d+)$/,
    figure: /^([1-9]\d*|0)(\.\d{1,2})?$/,
    char: /^[A-Za-z]+$/,
    bigChar: /[A-Z]/,
    chinese: /^[\u4e00-\u9fa5]+$/gi,
    anyChinese: /[\u4E00-\u9FA5\uF900-\uFA2D]/,
    mobile: /^1[3456789]{1}[0-9]{9}$/,
    phone: /^([+][0-9]{2,3}[-])?(\(\d{3,4}\)|\d{3,4}-|\s)?\d{6,8}$/,
    numChar: /^[A-Za-z0-9]+$/,
    blankSpace: /\s/,
    email: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
    password: /^[0-9A-Za-z]{6,20}$/,
    image: /^image\/(png|jpe?g|gif)$/,
    license: /(^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4}))$)|(^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$)/,
    filepack: /^(.*zip.*)$/,
}

export default class CheckForm {
    // 验证非空
    static notEmpty(str) {
        return (typeof str !== 'undefined' && String(str).trim() !== '' && str !== null)
    }

    // 限制长度
    static lengthLimit(str, minLen, maxLen) {
        let len = 0
        const strLen = str.length
        for (let i = 0; i < strLen; i += 1) {
            if (str[i].match(/[^x00-xff]/ig) !== null) {
                len += 2
            } else {
                len += 1
            }
        }
        if (!maxLen) return len >= minLen
        return (len >= minLen && len <= maxLen)
    }

    // 包含空格
    static hasBlankSpace(str) {
        return regs.blankSpace.test(str)
    }

    /** 基本格式验证 */


    // 验证数字 (包含0开头)
    static isNumber(str) {
        return regs.number.test(str)
    }

    // 验证正整数 (包括0)
    static isInteger(str) {
        return regs.integer.test(str)
    }

    // 验证正整数和小数
    static isFigure(str) {
        return regs.figure.test(str)
    }

    // 验证字母
    static isChar(str) {
        return regs.char.test(str)
    }

    // 验证所有为汉字
    static isChinese(str) {
        return regs.chinese.test(str)
    }

    // 验证有汉字
    static isAnyChinese(str) {
        return regs.anyChinese.test(str)
    }

    // 验证邮件格式
    static isEmail(str) {
        return regs.email.test(str)
    }

    // 验证手机号码格式
    static isMobile(str) {
        return regs.mobile.test(str)
    }

    // 验证电话号码
    static isPhone(str) {
        return regs.phone.test(str)
    }

    // 密码校验
    static isPassword(str) {
        return regs.password.test(str)
    }

    // 验证身份证号码
    static isIdCard(idCardNo) {
        return idCardNo && (/^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(idCardNo))
    }

    //验证上传图片的类型
    static isImage(str) {
        return str && regs.image.test(str)
    }

    // 验证车牌号
    static isLicense(str) {
        return regs.license.test(str)
    }

    //验证rar zip压缩包
    static isFilepackage(str) {
        return str && regs.filepack.test(str)
    }
}
4、时间格式化
export function dateFormat(date, format) {
    if (!date) {
        // alert('请传入需要格式化时间参数!');
        return ''
    }
  
    let da = date
    if (typeof date === 'string') da = date.replace(/-/g, '/')
  
    const d = new Date(da)
  
    if (!d.getDate() || isNaN(d.getDate())) {
        // alert('时间格式错误!');
        return ''
    }
  
    const dict = {
        yyyy: d.getFullYear(),
        M: d.getMonth() + 1,
        d: d.getDate(),
        H: d.getHours(),
        m: d.getMinutes(),
        s: d.getSeconds(),
        MM: (`${d.getMonth() + 101}`).substr(1),
        dd: (`${d.getDate() + 100}`).substr(1),
        HH: (`${d.getHours() + 100}`).substr(1),
        mm: (`${d.getMinutes() + 100}`).substr(1),
        ss: (`${d.getSeconds() + 100}`).substr(1),
    }
    try {
        return format.replace(/(yyyy|MM?|dd?|HH?|mm?|ss?)/g, f => dict[f])
    } catch (e) {
        return d
    }
}

// 字符串转时间戳
export function timestamp(time) {
    return Date.parse(new Date(time)) / 1000
}
  
const checkNum = (n, min, max) => (n > min && n < max)
  
const defaultTimeOptions = {
    isCtrYear: false,
    isCtrMonth: false,
    isCtrDay: false,
    isCtrHours: false,
    isCtrMinutes: false,
    isCtrSeconds: false,
    isShowHalfHours: false,
    isShowHalfMinutes: false,
    dateFormatText: 'yyyy-MM-dd T HH:mm:ss',
    beforeText: '前',
    yearText: '年',
    monthText: '月',
    dayText: '天',
    hoursText: '小时',
    minutesText: '分钟',
    secondsText: '秒',
    justNowText: '刚刚',
    maxYearNum: 0,
    maxMonthNum: 12,
    maxDayNum: 30,
    maxHoursNum: 24,
    maxMinutesNum: 60,
    maxSecondsNum: 60,
}
  
export function getTimeBeforeNow(date, opt) {
    if (!date) {
        alert('请输入时间!')
        return null
    }

    // debugger
    const d = dateFormat(date)
    const now = new Date()
    const sInterval = parseInt((now.getTime() - d.getTime()) / 1000, 10)
    const mInterval = parseInt(sInterval / 60, 10)
    const hInterval = parseInt(mInterval / 60, 10)

    const {
        beforeText, justNowText, isShowHalfMinutes, isShowHalfHours,
        isCtrYear, isCtrMonth, isCtrDay, isCtrHours, isCtrMinutes, isCtrSeconds,
        yearText, monthText, dayText, hoursText, minutesText, secondsText,
        maxYearNum, maxMonthNum, maxDayNum, maxHoursNum, maxMinutesNum, maxSecondsNum,
    } = { ...defaultTimeOptions, ...opt }

    if (isCtrYear && (now.getYear() - d.getYear()) <= maxYearNum) {
        return `${now.getYear() - d.getYear()}${yearText}${beforeText}`
    }
    if (isCtrMonth && (now.getMonth() - d.getMonth()) <= maxMonthNum) {
        return `${now.getMonth() - d.getMonth()}${monthText}${beforeText}`
    }
    if (isCtrDay && (now.getDay() - d.getDay()) <= maxDayNum) {
        return `${now.getDay() - d.getDay()}${dayText}${beforeText}`
    }
    if (isCtrHours && checkNum(hInterval, 1, maxHoursNum)) {
        return `${hInterval}${hoursText}${beforeText}`
    }
    if (isCtrMinutes && checkNum(mInterval, 1, maxMinutesNum)) {
        if (isShowHalfHours && mInterval === 30) {
            return `半${hoursText}${beforeText}`
        }
        return `${mInterval}${minutesText}${beforeText}`
    }
    if (isCtrSeconds && checkNum(sInterval, 1, maxSecondsNum)) {
        if (isShowHalfMinutes && sInterval === 30) {
            return `半${minutesText}${beforeText}`
        }
        return `${sInterval}${secondsText}${beforeText}`
    }
    if (isCtrSeconds && sInterval <= 1) {
        return `${justNowText}`
    }

    return dateFormat(d, opt.dateFormatText)
}
  
// 日期处理
export class DateExtend {
    static getNow(dateType) {
        const now = new Date()
        return dateFormat(now, dateType)
    }

    static getNowWeekNumber() {
        const week = new Date().getDay()
        let nowWeek = week
        if (week === 0) nowWeek = 7
        return nowWeek
    }

    static getOneDayWeekWithDateObject(deltaDay = 0, dateType, weekType = [1, 2, 3, 4, 5, 6, 7]) {
        const oneDayLong = 24 * 60 * 60 * 1000
        const now = new Date()
        const dayTime = now.getTime() + (deltaDay * oneDayLong)
        const day = new Date(dayTime)
        const weekDay = now.getDay() + deltaDay
        const week = weekDay > 0 ? weekDay % 7 : 7 + (weekDay % 7)
        return {
            date: dateFormat(day, dateType),
            week: week === 0 ? weekType[6] : weekType[week - 1],
        }
    }

    static getThisWeek(start, dateType, weekType) {
        const today = new Date().getDay()
        const weekArray = [0, 1, 2, 3, 4, 5, 6]
        return weekArray.reduce((prev, next) => {
            prev.push(DateExtend.getOneDayWeekWithDateObject((next + start) - today, dateType, weekType))
            return prev
        }, [])
    }

    static getThisWeekStartMondayArray(dateType, weekType) {
        return DateExtend.getThisWeek(1, dateType, weekType)
    }

    static getThisWeekStartSundayArray(dateType, weekType) {
        return DateExtend.getThisWeek(0, dateType, weekType)
    }
}

export function timeStampConvert(dateString) {
    const d = Date(parseInt(dateString, 10) * 1000).toLocaleString().replace(/:\d{1,2}$/, ' ')
    return new Date(d.replace(/-/g, '/'))
}

// 时间戳转换
export function dateConvert(dateString, split) {
    const dt = timeStampConvert(dateString)
    return `${dt.getFullYear()}${split}${dt.getMonth() + 1}${split}${dt.getDate()}`
}

// 计算两个日期之间的天数
export function getDateDay(date) {
    const now = new Date()
    return parseInt(Math.abs(now - date) / 1000 / 60 / 60 / 24, 10)
}

export function changeDataToNotice(date) {
    if (!date) {
        return ''
    }
    const newDate = new Date(date)
    return `${(newDate.getMonth() + 1)}月${newDate.getUTCDate()}日 ${newDate.getHours()}:${newDate.getMinutes()}`
}

export function minuteHourMinute(minutes) {
    return `${Math.floor(minutes/60)}小时${minutes%60}分`
}

/**
 * Date对象转字符串
 * @param {Date} date Date实例化对象
 * @param {String} type 时间格式
 */
export const formatDate = (date, type) => {

    let [ year, month, day ] = date.toLocaleDateString().split('/').map(item => zerofill(item))

    let [ hour, min, sec ] = [date.getHours(), date.getMinutes(), date.getSeconds()].map(item => zerofill(item))

    let week = weeks[date.getDay()]

    switch (type) {
        case 'YYYY-MM-DD':
            return year + '-' + month + '-' + day
        
        case 'MM-DD':
            return month + '-' + day

        case 'MM-DD hh:mm:ss':
            return month + '-' + day + ' ' + hour + ':' + min + ':' + sec

        case 'YYYY-MM-DD hh:mm:ss':
            return year + '-' + month + '-' + day + ' ' + hour + ':' + min + ':' + sec

        case 'YYYY年MM月DD日':
            return year + '年' + month + '月' + day + '日'

        case 'MM月DD日 W':
            return month + '月' + day + '日' + ' 周' + week

        case 'YYYY年MM月DD日 hh:mm:ss':
            return year + '年' + month + '月' + day + '日' + ' ' + hour + ':' + min + ':' + sec

        case 'YYYY年MM月DD日 hh:mm:ss W':
            return year + '年' + month + '月' + day + '日' + ' ' + hour + ':' + min + ':' + sec + ' 星期' + week
        case 'hh:mm:ss':
            return hour + ':' + min + ':' + sec
    }
}

const weeks = ['日', '一', '二', '三', '四', '五', '六']

const zerofill = n => { return String(n).padStart(2, '0') }

/**
 * 比较日期先后
 * @param {String} d1 日期
 * @param {String} d2 日期
 * @param {String} o 运算符
 * @return true/false
 */
export const compareDate = (d1, d2, o) => {
    const format = d => new Date(d.replace(/-/g, '\/'))
    switch (o) {
        case 'lt':
            return format(d1) < format(d2)
        case 'gt':
            return format(d1) > format(d2)
        case 'lte':
            return format(d1) <= format(d2)
        case 'gte':
            return format(d1) >= format(d2)
        default:
            break
    }
}

/**
 * 时间加减的方法,以月为单位
 * @param {String} date 当前时间
 * @param {Number} months 月数
 */
export const calDate = (date, months) => {
    let d = new Date(date)
    d.setMonth(d.getMonth() + parseInt(months))
    let month = d.getMonth() + 1
    let day = d.getDate()
    return `${d.getFullYear()}-${month < 10 ? `0${month}` : month}-${day < 10 ? `0${day}` : day}`
}

/**
 * 获取当前农历日期
 * @param {Boolean} withYear 是否显示农历年
 */
export const lunarCalendar = withYear => {
    let D = new Date()
    let yy = D.getFullYear()
    let mm = D.getMonth() + 1
    let dd = D.getDate()
    // let ww = D.getDay()
    // let ss = parseInt(D.getTime() / 1000)
    if (yy < 100) yy = '19' + yy
    return GetLunarDay(yy, mm, dd, withYear)
}

//定义全局变量
let madd = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
let tgString = '甲乙丙丁戊己庚辛壬癸'
let dzString = '子丑寅卯辰巳午未申酉戌亥'
let numString = '一二三四五六七八九十'
let monString = '正二三四五六七八九十冬腊'
// let weekString = '日一二三四五六'
let sx = '鼠牛虎兔龙蛇马羊猴鸡狗猪'
let cYear, cMonth, cDay, TheDate
let CalendarData = new Array(
    0xA4B, 0x5164B, 0x6A5, 0x6D4, 0x415B5, 0x2B6, 0x957, 0x2092F, 0x497, 0x60C96, 0xD4A, 0xEA5, 0x50DA9, 0x5AD, 0x2B6, 0x3126E,
    0x92E, 0x7192D, 0xC95, 0xD4A, 0x61B4A, 0xB55, 0x56A, 0x4155B, 0x25D, 0x92D, 0x2192B, 0xA95, 0x71695, 0x6CA, 0xB55, 0x50AB5,
    0x4DA, 0xA5B, 0x30A57, 0x52B, 0x8152A, 0xE95, 0x6AA, 0x615AA, 0xAB5, 0x4B6, 0x414AE, 0xA57, 0x526, 0x31D26, 0xD95, 0x70B55,
    0x56A, 0x96D, 0x5095D, 0x4AD, 0xA4D, 0x41A4D, 0xD25, 0x81AA5, 0xB54, 0xB6A, 0x612DA, 0x95B, 0x49B, 0x41497, 0xA4B, 0xA164B,
    0x6A5, 0x6D4, 0x615B4, 0xAB6, 0x957, 0x5092F, 0x497, 0x64B, 0x30D4A, 0xEA5, 0x80D65, 0x5AC, 0xAB6, 0x5126D, 0x92E, 0xC96,
    0x41A95, 0xD4A, 0xDA5, 0x20B55, 0x56A, 0x7155B, 0x25D, 0x92D, 0x5192B, 0xA95, 0xB4A, 0x416AA, 0xAD5, 0x90AB5, 0x4BA, 0xA5B,
    0x60A57, 0x52B, 0xA93, 0x40E95
)

const GetBit = (m, n) => {
    return (m>>n)&1
}

//农历转换
const e2c = (...args) => {
    TheDate= (args.length!=3) ? new Date() : new Date(args[0], args[1], args[2])
    let total, m, n, k
    let isEnd = false
    let tmp = TheDate.getYear()
    if (tmp < 1900) {
        tmp += 1900
    }
    total = (tmp - 1921) * 365 + Math.floor((tmp - 1921) / 4) + madd[TheDate.getMonth()] + TheDate.getDate() - 38
   
    if (TheDate.getYear() % 4 == 0 && TheDate.getMonth() > 1) {
        total++
    }
    for (m=0; ; m++) {
        k = (CalendarData[m] < 0xfff) ? 11 : 12
        for (n = k; n >= 0; n--){
            if (total <= 29 + GetBit(CalendarData[m], n)) {
                isEnd = true
                break
            }
            total = total - 29 - GetBit(CalendarData[m], n)
        }
        if (isEnd) break
    }
    cYear = 1921 + m
    cMonth = k - n + 1
    cDay = total
    if (k == 12) {
        if (cMonth == Math.floor(CalendarData[m] / 0x10000) + 1) {
            cMonth = 1 - cMonth
        }
        if (cMonth > Math.floor(CalendarData[m] / 0x10000) + 1){
            cMonth--
        }
    }
}

const GetcDateString = withYear => {
    let tmp = ''
    if (withYear) {
        tmp += tgString.charAt((cYear - 4) % 10)
        tmp += dzString.charAt((cYear - 4) % 12)
        tmp += '('
        tmp += sx.charAt((cYear - 4) % 12)
        tmp += ')年'
    }
    if (cMonth < 1) {
        tmp += '(闰)'
        tmp += monString.charAt(-cMonth - 1)
    } else {
        tmp += monString.charAt(cMonth - 1)
    }
    tmp += '月'
    tmp += (cDay < 11) ? '初' : ((cDay < 20) ? '十' : ((cDay < 30) ? '廿' : '三十'))
    if (cDay % 10 != 0 || cDay == 10) {
        tmp += numString.charAt((cDay - 1) % 10)
    }
    return tmp
}

const GetLunarDay = (solarYear, solarMonth, solarDay, withYear) => {
    if (solarYear < 1921 || solarYear > 2020) {
        return ''
    } else {
        solarMonth = (parseInt(solarMonth) > 0) ? (solarMonth - 1) : 11
        e2c(solarYear, solarMonth, solarDay)
        return GetcDateString(withYear)
    }
}

/**
 * 将周一至周五转为工作日输出,周六周日转为周末输出,周一至周日转为每天输出,其他情况原样输出
 * @param {Array} period 周期数组:['周一', '周二', ...] | [1, 2, ...]
 * @param {String} mode 数组模式:default | 'num'
 */
export const weekPeriod = (period, mode) => {
    if (mode === 'num') {
        period = period.map(item => weeksMap[item - 1])
    }

    const uniqPeriod = [...new Set(period)]
    
    if (uniqPeriod.length === 7) {
        return '每天'
    }
    const weekend = ['周六', '周日']
    const weekday = ['周一', '周二', '周三', '周四', '周五']

    const weekendFilter = uniqPeriod.filter(item => weekend.includes(item))
    const weekdayFilter = uniqPeriod.filter(item => weekday.includes(item))

    let result = []

    if (weekendFilter.length === 2) {
        result = ['周末']
    } else {
        result = weekendFilter
    }

    if (weekdayFilter.length === 5) {
        result.push('工作日')
    } else {
        result = [...result, ...weekdayFilter]
    }

    return result.join('、')
}

const weeksMap = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
5、类型参数判断
const toString = Object.prototype.toString

/**
  * 检查给定参数是否为数组
  * 使用Array.isArray()检查某个值是否属于数组
  * Example isArray([10, 1, 5]) -> true
  */
export const isArray = val => !!val && Array.isArray(val)

/**
 * 检查给定的参数是否为布尔元素
 * EXample: isBoolean(null) -> false
 * Example: isBoolean(false) -> true
 */
export const isBoolean = val => typeof val === 'boolean'

/**
 * 检查给定的参数是否为函数
 * Example:isFunction('x) -> false
 * Example: isFunction(x => x) => true
 */
export const isFunction = val => val && typeof val === 'function'

/**
 * 检查给定参数是否为number
 * Example: isNumber('1') -> false
 * Example: isNumber(1) -> true
 */
export const isNumber = val => typeof val === 'number'

/**
 * 检查给定参数是否为字符串
 * Example: isString(10) -> false
 * Example: isString('10') -> true
 */
export const isString = val => typeof val === 'string'

/***
 * 检查给定的参数是否为Symbol
 * Example: isSymbol('x) -> false
 * Example: isSymbol(Symbol('x')) -> true
 */
export const isSymbol = val => typeof val === 'symbol'
6、千位分割符
/**
 * 千位分割符
 * @param {Number} num 进行分割的数字
 */
export const thousandBitSeparator = num => {
    try {
        let res = num.toString().replace(/\d+/, n => { // 先提取整数部分
            return n.replace(/(\d)(?=(\d{3})+$)/g, $1 => {
                return $1 + ','
            })
        })
        return res
    } catch (err) {
        return num
    }
}
7、函数节流
/**
 * 函数节流
 *
 * @param {Function} fn 节流函数
 * @param {Number} interval 节流时间(ms),默认500ms
 * @returns {Function} 新函数
 */
export function throttle(fn, interval = 500) {
    let _self = fn
    let timer //定时器
    let firstTime = true//是否第一次调用
    let begin = new Date()
    return function () {
        let args = arguments
        let _me = this
        let current = new Date()

        //如果第一次执行,不需要延迟
        if (firstTime) {
            _self.apply(_me, args)
            return firstTime = false
        }
        //如果定时器还在,说明上一次调用还没有完成
        if (timer) {
            return false
        }
        if (current - begin >= interval) {
            _self.apply(_me, args)
            begin = current
        } else {
            //延迟1段事件执行
            timer = setTimeout(function () {
                clearTimeout(timer)//清理定时器
                timer = null//闭包内存释放
                // _self.apply(_me, args)//返回的新函数、参数借用源函数执行
            }, interval)
        }

    }
}
8、URL处理
// 获取地址上的所有参数转换为对象
export const getURLParameters = (url = window.location.href) => {
    const params = url.match(/([^?=&]+)(=([^(&|#)]*))/g)
    if (params === null) {
        return {}
    } else {
        return params.reduce(
            (a, v) => {
                a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)
                return a
            }, {}
        )
    }
}

// 把参数对象合并生产url
export function setParamObjToUrl(opt = {}, url = '') {
    if (typeof url !== 'string') return null
    let urlPro = ''
    let urlEnd = ''
    if (url !== '') {
        urlPro = url.startsWith('http') ? '' : 'http://'
        urlEnd = '?'
    }
    const completeUrl = `${urlPro}${url}${urlEnd}`
    const options = Object.keys(opt).reduce((arr, obj) => [...arr, `${obj}=${encodeURIComponent(opt[obj])}`], [])
    return `${completeUrl}${options.join('&')}`
}
9、导出Excel
/**
 * 导出Excel,暂时只适用普通url和bloburl,兼容IE10+
 * @param {a} ele a标签
 * @param {blob/url} data blob对象或普通url
 * @param {string} file 导出的文件名,针对blob有效,普通url无效,需要后台来更改文件名
 */
export const insertComponent = (ele, data, file = '数据统计') => {

    if (!data) {
        return
    }

    // 针对IE
    if (window.navigator.msSaveBlob && typeof data === 'object') {
        return window.navigator.msSaveBlob(data, `${file}.xls`)
    }

    const el = document.createElement(ele)

    if (typeof el.href !== 'undefined') {
        // 针对普通url
        el.href = typeof data === 'string' ? data : window.URL.createObjectURL(data)
        // 针对blob,edge必须有download属性,且普通url只有edge可以修改文件名
        el.download = `${file}.xls`
        // 兼容fireFox的事件触发
        const evt = document.createEvent('MouseEvents')
        evt.initEvent('click', true, true)
        el.dispatchEvent(evt)
        // 或:
        // document.body.appendChild(el)
        // el.click()
        // document.body.removeChild(el)
    }
}
10、文本框内容复制
/**
 * 文本框内容复制
 * @param {dom} dom dom对象(input)
 */
export const copyInputVal = el => {
    el.select()
    document.execCommand('copy')
}
11、canvas,dataURL,Blob,File相互转换
/**
 * canvas转图片保存,兼容IE10+
 * @param {canvas} canvas canvas对象
 * @param {string} imgName 保存的图片名
 */
export const canvasToImg = (canvas, imgName) => {
    const url = canvas.toDataURL('image/png')
    // 针对IE
    if (window.navigator.msSaveBlob) {
        return window.navigator.msSaveBlob(new Blob([getUint8Arr(url).u8arr]), `${imgName}.png`)
    }
    const a = document.createElement('a')
    a.setAttribute('href', url)
    a.setAttribute('download', `${imgName}.png`)
    const evt = document.createEvent('MouseEvents')
    evt.initEvent('click', true, true)
    a.dispatchEvent(evt)
}

/**
 * canvas转dataURL
 * @param {canvas} canvas
 */
export const convertCanvasToImage = canvas => {
    // let image = new Image()
    // image.src = canvas.toDataURL('image/png')
    return canvas.toDataURL('image/png')
}

/**
 * dataURL转blob对象
 * @param {String} dataurl
 */
export const dataURLtoBlob = dataurl => {
    let uint8 = getUint8Arr(dataurl)
    return new Blob([uint8.u8arr], { type: uint8.mime })
}

/**
 * 将blob转换为file
 * @param {Blob} theBlob
 * @param {String} fileName
 */
export const blobToFile = (theBlob, fileName) => {
    theBlob.lastModifiedDate = new Date()
    theBlob.name = fileName
    return theBlob
}

/**
 * dataURL转file对象
 * @param {String} dataurl
 * @param {String} filename
 */
export const dataURLtoFile = (dataurl, filename) => {//将base64转换为文件
    let uint8 = getUint8Arr(dataurl)
    return new File([uint8.u8arr], filename, { type: uint8.mime })
}

/**
 * 二进制容器
 * @param {String} dataurl
 */
const getUint8Arr = dataurl => {
    // 截取base64的数据内容
    let arr = dataurl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        // 获取解码后的二进制数据的长度,用于后面创建二进制数据容器
        n = bstr.length,
        // 创建一个Uint8Array类型的数组以存放二进制数据
        u8arr = new Uint8Array(n)
    // 将二进制数据存入Uint8Array类型的数组中
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
    }
    return { u8arr, mime }
}
12、生成任意长度的随机序列号
/**
 * 生成任意长度的随机序列号
 * @param {Number} length 长度
 */
export const getRandomCode = length => {
    
    if (length > 0) {
        let data = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
            'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
            'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
            'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
            'w', 'x', 'y', 'z']
        let arr = shuffle(data)
        let nums = ''
        for (let i = 0; i < length; i++) {
            let r = parseInt(Math.random() * 61)
            nums += arr[r]
        }
        return nums
    } else {
        return false
    }
}
13、生成uuid
export const uuid = () => {
    let s = []
    let hexDigits = '0123456789abcdef'
    for (let i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
    }
    s[14] = '4'
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)
    s[8] = s[13] = s[18] = s[23] = ''

    let uuid = s.join('')
    return uuid
}
14、随机生成指定长度的值为特定范围内的数字的数组
/**
 * 随机生成指定长度的值为特定范围内的数字的数组
 * @param {Number} min 最小值
 * @param {Number} max 最大值
 * @param {Number} count 个数
 */
export const randomNumArr = (min, max, count) => {
    let arr = []
    for (let i = 0; i < count; i++) {
        arr.push(Math.floor(Math.random() * (max - min + 1) ) + min)
    }
    return arr
}
15、数组求和
/**
 * 数组求和
 * @param {Array} arr 求和的数组
 */
export const sum = arr => {
    let sum = 0
    arr.map(item => sum += (Number(item) || 0))
    return sum
}
16、手机号加密
/**
 * 手机号加密,默认保留前三后四
 * @param {String} tel 待加密的手机号
 */
export const encryptionTel = (tel, front = 3, after = 4) => {
    if (!tel || tel === '--') return tel
    const length = tel.length - front - after
    const star = new Array(length > 0 ? length : tel.length).fill('*').join('')
    const re = new RegExp('^(\\d{'+ front +'})\\d{'+ length +'}(\\d+)')
    return tel.replace(re, '$1'+ star +'$2')
}
17、JSON格式化显示
/**
 * JSON格式化显示
 */
export const JSONFormat = json => {
    return JSON.stringify(JSON.parse(json), null, 4)
}
18、AES加密算法
/**
 * 加密算法
 */
import CryptoJS from 'crypto-js'

const key = CryptoJS.enc.Utf8.parse('1234123412ABCDEF') //十六位十六进制数作为密钥
const iv = CryptoJS.enc.Utf8.parse('ABCDEF1234123412') //十六位十六进制数作为密钥偏移量

// AES加密
export const AES_Encrypt = str => {
    let srcs = CryptoJS.enc.Utf8.parse(str)
    let encrypted = CryptoJS.AES.encrypt(srcs, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    })
    return encrypted.ciphertext.toString().toUpperCase()
}

// AES解密
export const AES_Decrypt = str => {
    let encryptedHexStr = CryptoJS.enc.Hex.parse(str)
    let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr)
    let decrypt = CryptoJS.AES.decrypt(srcs, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    })
    let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
    return decryptedStr.toString()
}
19、保留小数点后几位数,不足不补零
/**
 * 保留小数点后几位数,不足不补零
 * @param {Number/String} num 格式化的数字
 * @param {Number} digit 保留的小数点后位数
 * @param {Boolean} isAsItIs 非数字是否按原样输出
 */
export const fixDecimal = (num, digit, isAsItIs) => {
    const number = Number(num)
    if (isNaN(number)) {
        return isAsItIs ? (num || '--') : '--'
    }
    return +number.toFixed(digit)
}
20、指定长度为数字前面补零输出
/**
 * 指定长度为数字前面补零输出
 * @param {String/Number} num 需要补位的数字
 * @param {Number} length 补位后的总长度
 */
export const PrefixInteger = (num, length) => {
    return String(num).padStart(length, '0')
}
21、大数值转为带文字单位
/**
 * 大数值转为带文字单位,这里调用了千位分割符的方法,见方法6
 * @param {Number} number 格式化的数字
 * @param {Number} decimalDigit 小数点后位数,默认为2
 */
export const largeNumWithUnit = (number, decimalDigit = 2) => {
    let integer = Math.floor(number)
    let digit = getDigit(integer)
    let unit = []
    if (digit > 3) {
        let multiple = Math.floor(digit / 8)
        if (multiple >= 1) {
            let tmp = Math.round(integer / Math.pow(10, 8 * multiple))
            unit.push(addWan(tmp, number, 8 * multiple, decimalDigit))
            for (let i = 0; i < multiple; i++) {
                unit.push('亿')
            }
            return unit.join('')
        } else {
            return addWan(integer, number, 0, decimalDigit)
        }
    } else {
        return thousandBitSeparator(number)
    }
}

const addWan = (integer, number, mutiple, decimalDigit) => {
    let digit = getDigit(integer)
    if (digit > 3) {
        let remainder = digit % 8
        if (remainder >= 5) { // 十万、百万、千万显示为万
            remainder = 4
        }
        return thousandBitSeparator(Math.round(number / Math.pow(10, remainder + mutiple - decimalDigit)) / Math.pow(10, decimalDigit)) + '万'
    } else {
        return thousandBitSeparator(Math.round(number / Math.pow(10, mutiple - decimalDigit)) / Math.pow(10, decimalDigit))
    }
}
const getDigit = integer => {
    if (integer > 0) {
        return integer.toString().length - 1
    }
    return -1
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351