mixin.js 混入
在每个组件库混入这些基础公共方法,不再手动按需导入。
import {
_get,
_typeof,
_typein,
isValid,
isVoid,
friendly,
isHidden,
isVisible,
isDisabled
} from '@/components/libs/util'
const mixin = {
methods: {
_typeof,
_typein,
_get,
isVoid,
isValid,
friendly,
isHidden,
isVisible,
isDisabled
}
}
export default mixin
util.js 工具
定义基础公共方法。
import { getDataType } from '@/utils/libs/support'
/**
* 获得参数的数据类型
* @param text 目标文本
* @returns {undefined|null|string|Function|Number|Array|Object|Boolean|BigInt|Symbol}
*/
export function _typeof(text) {
const textType = typeof text
switch (textType) {
case 'undefined':
return undefined
case 'function':
return Function
case 'number':
return Number
case 'object':
if (text === null) return null
if (Array.isArray(text)) return Array
return Object
case 'boolean':
return Boolean
case 'string':
return String
case 'bigint':
return BigInt
case 'symbol':
return Symbol
}
}
/**
* 校验数据类型
* @param text 目标文本
* @param type 预期类型
* @returns {void, boolean}
*/
export function _typein(text, type) {
const TYPE = _typeof(type)
const TEXT = _typeof(text)
if (TYPE === Array) return type.includes(TEXT)
return type === TEXT
}
/**
* 判断文本是否无效
* @param text 待检测的文本
* @param includeZero 是否包括零,默认false
* @returns {boolean}
*/
export function isVoid(text, includeZero = false) {
if (text === null || text === undefined || text === '') return true
return includeZero && (text === 0)
}
/**
* 数据是否有效
* @param text 目标文本
* @returns {boolean}
*/
export function isValid(text) {
return !isVoid(text)
}
/**
* 对象多层级取值,避免报错
* @param data 取值的对象
* @param path 取值的路径
* @returns {undefined}
* @private
*/
export function _get(data, path) {
if (isVoid(data)) return undefined
if (_typeof(path) !== String) return undefined
const pathArr = path.split('.')
let result = data
for (let i = 0; i < pathArr.length; i++) {
if (result[pathArr[i]] === undefined) return undefined
result = result[pathArr[i]]
}
return result
}
/**
* 友好展示文本信息
* @param text 待展示的文本
* @param displace 当文本无效时,取代展示的文本
* @param other 当文本有效时,要求展示其他文本
* @param added 补充显示的条件,默认值true
* @returns {string|*}
*/
export function friendly(text, displace = '', added = true, other) {
if (isVoid(text) && added) return displace
return other !== undefined ? other : text
}
/**
* 防止无效的输入
* 去掉字符串左边的空格,连续的空格替换为一个空格
* @param params
* -params.text 需要去除多余空格的文本,默认值“”
* -params.trimStart 是否需要去除文本左边的空格,默认值true
* -params.trimEnd 是否需要去除文本右边的空格,默认值false
* -params.trimAll 是否需要去除文本所有的空格,默认值false
* @returns {*}
*/
export function preventVoidInput(params) {
const dataType = getDataType(params)
if (dataType === 'Sting') {
params = { text: params }
} else if (dataType !== 'object') {
return console.error(`params: 期望接收一个“object”参数,意外的获得一个“${dataType}”参数!`)
}
const { trimStart = true, trimEnd = false, trimAll = false } = params // 参数对象解构
let { text = '' } = params // 参数对象解构
if (isVoid(text)) return ''
if (trimAll) return text.replace(/\s+/g, '')
if (trimStart) {
text = text.trimStart()
}
if (trimEnd) return text.trimEnd()
return text.replace(/\s+/g, ' ')
}
/**
* 是否隐藏
* @param item 当前级
* @param parent 父级
* @returns {boolean}
*/
export function isHidden(item, parent = null) {
if (_typeof(item) !== Object) return false
if (_typeof(item.hidden) === Function) return Boolean(item.hidden(parent || item))
return Boolean(item.hidden)
}
/**
* 是否可见
* @param item 当前级
* @param parent 父级
* @returns {boolean}
*/
export function isVisible(item, parent = null) {
return !isHidden(item, parent)
}
/**
* 是否禁用
* @param item 当前级
* @param parent 父级
* @returns {boolean}
*/
export function isDisabled(item, parent = null) {
if (_typeof(item) !== Object) return false
if (_typeof(item.disabled) === Function) return Boolean(item.disabled(parent || item))
return Boolean(item.disabled)
}
support.js 支持
定义公共方法,按需导入。
/**
* FPC 是 Function Param Console 的简称
* 方法参数控制
*/
export function FPC() {
/**
* 错误参数提示
*/
this.error = (param, text, accepts) => {
return this.matching({
level: 'error',
param: param,
text: text,
accepts: accepts
})
}
/**
* 警告参数提示,是 warning 的简写
*/
this.warn = (param, text, accepts) => {
return this.matching({
level: 'warn',
param: param,
text: text,
accepts: accepts
})
}
/**
* 废弃参数提示,是 obsolete 的简写
*/
this.obs = (param, text, accepts) => {
return this.matching({
level: 'error',
param: param,
text: text,
accepts: accepts
})
}
/**
* 匹配参数提示
* @params { level = 'error', param = '', text, accepts = '*' }
*/
this.matching = (params) => {
const { level = 'error', param = '', text, accepts = '*' } = params // 参数对象解构
const T_TYPE = getDataType(text) // 文本的数据类型
if (accepts !== '*' && !accepts.includes(T_TYPE)) {
console[level](`${param}:数据类型不匹配,期望接收[${accepts.join('、')}]参数,意外的获得“${T_TYPE}”参数!`)
return {
dataType: T_TYPE,
result: false
}
}
return {
dataType: T_TYPE,
result: true
}
}
}
/**
* 获得参数的数据类型
* @param text 目标文本
* @returns {undefined|null|string}
*/
export function getDataType(text) {
const textType = typeof text
switch (textType) {
case 'undefined':
return undefined
case 'function':
return 'function'
case 'number':
return 'number'
case 'object':
if (text === null) return null
if (text.constructor.name === 'Array') return 'array'
return 'object'
case 'boolean':
return 'boolean'
case 'string':
return 'string'
case 'bigint':
return 'bigint'
case 'symbol':
return 'symbol'
}
}
/**
* 可接收的数据类型
* @param text 目标文本
* @param dataTypes 可接受的数据类型列表
* @returns {void, boolean}
*/
export function acceptDataTypes(text, dataTypes) {
const A_TYPES = getDataType(dataTypes)
if (text === undefined) return console.error(`text:期望接收一个非“undefined”参数,意外的获得一个“undefined”参数!`)
if (A_TYPES !== 'array') return console.error(`dataTypes:期望接收一个“array”参数,意外的获得一个“${A_TYPES}”参数!`)
return A_TYPES.includes(getDataType(text))
}
/**
* 对象多层级取值,避免报错
* @param data 取值的对象
* @param path 取值的路径
* @returns {undefined}
* @private
*/
export function _get(data, path) {
if (isVoid(data)) return undefined
if (_typeof(path) !== String) return undefined
const pathArr = path.split('.')
let result = data
for (let i = 0; i < pathArr.length; i++) {
if (result[pathArr[i]] === undefined) return undefined
result = result[pathArr[i]]
}
return result
}
/**
* 获得参数的数据类型
* @param text 目标文本
* @returns {undefined|null|string|Function|Number|Array|Object|Boolean|BigInt|Symbol}
*/
export function _typeof(text) {
const textType = typeof text
switch (textType) {
case 'undefined':
return undefined
case 'function':
return Function
case 'number':
return Number
case 'object':
if (text === null) return null
if (Array.isArray(text)) return Array
return Object
case 'boolean':
return Boolean
case 'string':
return String
case 'bigint':
// eslint-disable-next-line no-undef
return BigInt
case 'symbol':
return Symbol
}
}
/**
* 校验数据类型
* @param text 目标文本
* @param type 预期类型
* @returns {void, boolean}
*/
export function _typein(text, type) {
const TYPE = _typeof(type)
const TEXT = _typeof(text)
if (TYPE === Array) return type.includes(TEXT)
return type === TEXT
}
/**
* 判断文本是否无效
* @param text 待检测的文本
* @param includeZero 是否包括零,默认false
* @returns {boolean}
*/
export function isVoid(text, includeZero = false) {
if (text === null || text === undefined || text === '') return true
return includeZero && (text === 0)
}
/**
* 数据是否有效
* @param text 目标文本
* @returns {boolean}
*/
export function isValid(text) {
return !isVoid(text)
}
/**
* 输入限制/只能输入数字且不能以0开头
* @param val 输入值
* @returns {Number}
*/
export function limitInputNumber(val) {
return val.replace(/\D/g, '').replace(/^0{1,}/g, '')
}
/**
* 友好展示文本信息
* @param text 待展示的文本
* @param displace 当文本无效时,取代展示的文本
* @param other 当文本有效时,要求展示其他文本
* @param added 补充显示的条件,默认值true
* @returns {string|*}
*/
export function friendly(text, displace = '', added = true, other) {
if (isVoid(text) && added) return displace
return other !== undefined ? other : text
}
/**
* 防止无效的输入
* 去掉字符串左边的空格,连续的空格替换为一个空格
* @param params
* -params.text 需要去除多余空格的文本,默认值“”
* -params.trimStart 是否需要去除文本左边的空格,默认值true
* -params.trimEnd 是否需要去除文本右边的空格,默认值false
* -params.trimAll 是否需要去除文本所有的空格,默认值false
* @returns {*}
*/
export function preventVoidInput(params) {
const dataType = getDataType(params)
if (dataType === 'Sting') {
params = { text: params }
} else if (dataType !== 'object') {
return console.error(`params: 期望接收一个“object”参数,意外的获得一个“${dataType}”参数!`)
}
const { trimStart = true, trimEnd = false, trimAll = false } = params // 参数对象解构
let { text = '' } = params // 参数对象解构
if (isVoid(text)) return ''
if (trimAll) return text.replace(/\s+/g, '')
if (trimStart) {
text = text.trimStart()
}
if (trimEnd) return text.trimEnd()
return text.replace(/\s+/g, ' ')
}
/**
* 获取数组对象的值,返回一个值的数组
* @param list 数组对象
* @param func 需要获取的值,处理方法
* @returns {void|*}
*/
export function getObjectValues(list, func) {
const LIST_DATA_TYPE = getDataType(list) // 数据类型
if (LIST_DATA_TYPE !== 'array') return console.error(`list: 期望接收一个“array”参数,意外的获得一个“${LIST_DATA_TYPE}”参数!`)
const FUNC_DATA_TYPE = getDataType(func) // 数据类型
if (FUNC_DATA_TYPE !== 'function') return console.error(`func: 期望接收一个“function”参数,意外的获得一个“${FUNC_DATA_TYPE}”参数!`)
return list.map(obj => func(obj))
}
/**
* 参数填充
* 接口设计采用 Restfull API 需要遵循的规则,动参一律采用$符号定义。
* 具名参数: $符号后面带参数
* 匿名参数: $符号后面不带参数
* 具名参数示例1: const api1 = '/api/$mode/$id/evaluations/$eid/'
* paramsPadding(api1, { $mode: 'course', $id: 1, $eid: 2 })
* 匿名参数示例2: const api2 = '/api/$/$/evaluations/$/'
* paramsPadding(api2, ['course', 1, 2])
* @param text 待填充接口
* @param params 参数对象
* @returns {*}
*/
export function paramsPadding(text, params) {
const fpc = new FPC()
const TEXT = fpc.error('text', text, ['string'])
if (!TEXT.result) return
const PARAMS = fpc.error('params', params, ['object', 'array'])
if (!PARAMS.result) return
if (PARAMS.dataType === 'object') {
Object.keys(params).map(key => {
text = text.replace(key, params[key])
})
return text
}
params.map(value => {
text = text.replace('$', value)
})
return text
}
/**
* 文本替换
* @param str 原始字符串
* @param index 指定下标
* @param oldText 旧文本
* @param newText 新文本
* @returns {string} 新的字符串
*/
export function textReplace(str, index, oldText, newText) {
return str.substring(0, index) + newText + str.substring(index + oldText.length)
}
/**
* 寻找子字符串在父字符串中所有出现的下标
* @param supStr 父字符串
* @param subStr 子字符串
* @returns {*[]} 数组
*/
export function findSubStrIndex(supStr, subStr) {
let index = supStr.indexOf(subStr)
const result = []
while (index > -1) {
result.push(index)
index = supStr.indexOf(subStr, index + 1)
}
return result
}
/**
* 解析 Blob文件内容
* @param blob
* @returns {Promise<unknown>}
*/
export function parseBlob(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = function(res) {
resolve(JSON.parse(res.target.result))
} // 成功回调
reader.onerror = function(err) {
reject(err)
} // 失败回调
reader.readAsText(blob, 'utf-8') // 按照utf-8编码解析
})
}
/**
* 深度克隆对象
* 适用范围:
* JSON.stringify()只能序列化对象的可枚举的自有属性,obj中的对象由构造函数生成,使用方法后该对象__proto__不再为其构造函数的prototype,失去与其构造函数的链接,不可枚举的属性丢失
* 以下场景不适用:
* object包含:Date对象、RegExp对象、Map对象、Set对象、Error对象、包装对象(new Number()、new String()、new Boolean())、Function对象、Symbol、BigInt、undefined、NaN、Infinity、-Infinity
* object中存在循环引用的情况也无法正确实现深拷贝,会引发 TypeError(TypeError: Converting circular structure to JSON)
* @param object 目标对象
*/
export function deepClone(object) {
return JSON.parse(JSON.stringify(object))
}
/**
* 下载文件
* @param fileSource blob文件格式或者下载地址URL
* @param fileName 是文件的别名是一个可选参数
*
*/
export function downloadFile(fileSource, fileName) {
if (isVoid(fileSource)) return false
const fileSourceType = _typeof(fileSource)
if (fileSourceType === String && isValid(fileSource)) return downloadFileAsHref(fileSource, fileName)
if (fileSourceType === Object && fileSource.constructor.name === 'Blob') {
if (window.navigator['msSaveOrOpenBlob']) {
navigator['msSaveBlob'](fileSource, fileName)
return true
} else {
const url = window.URL ? window.URL.createObjectURL(fileSource) : window.webkitURL.createObjectURL(fileSource)
downloadFileAsHref(url, fileName)
window.URL ? window.URL.revokeObjectURL(url) : window.webkitURL.revokeObjectURL(url)
return true
}
}
return false
}
/**
* 下载文件-通过URL
* @param fileUrl,是文件的下载地址
* @param fileName,是文件的别名是一个可选参数
* return boolean,true 已执行下载请求,false fileUrl是空的,未执行下载请求
*/
export function downloadFileAsHref(fileUrl, fileName) {
if (!fileUrl) return false
const link = document.createElement('a')
link.style.display = 'none'
link.href = fileUrl
link.download = fileName
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
return true
}
/**
* 计算表达式的值, 解决eval表达式报错的问题
* @param fn
* @returns {*}
*/
export function evil(fn) {
const Fun = Function // 一个变量指向Function,防止有些前端编译工具报错
return new Fun('return ' + fn)()
}
/**
* 转换树结构
* @param treeData 一维数组或者一维对象
* @param id 节点id
* @param pId 节点的父id
* @param rootPId 根节点id(一般为0)
* @returns {*[]}
*/
export function transTree(treeData, { id, pId, rootPId }) {
const keyNodes = {}
const rootNodeList = []
const nodeList = []
if (_typeof(treeData) === Array) {
// Fill in the map
treeData.map(node => {
const clone = { ...node }
const key = clone[id]
keyNodes[key] = clone
clone.key = clone.key || key
nodeList.push(clone)
})
} else if (_typeof(treeData) === Object) {
// Fill in the map
Object.keys(treeData).map(index => {
const node = treeData[index]
const clone = { ...node }
const key = clone[id]
keyNodes[key] = clone
clone.key = clone.key || key
nodeList.push(clone)
})
}
// Connect tree
nodeList.forEach(node => {
const parentKey = node[pId]
const parent = keyNodes[parentKey]
// Fill parent
if (parent) {
parent.children = parent.children || []
parent.children.push(node)
}
// Fill root tree node
if (parentKey === rootPId || (!parent && rootPId === null)) {
rootNodeList.push(node)
}
})
return rootNodeList
}
/**
* 文本域 Enter 分割转数组
* @param str
* @returns {string[]|*[]}
*/
export function textareaToArray(str) {
if (typeof str === 'string') {
return str.split(/[(\r\n)\r\n]+/)
} else {
return []
}
}
/**
* 解析JSON格式的数组
* 特殊符号,如英文逗号隔开的也可以转成数组
* @param text 需要解析的文本对象
* @param symbol 特殊间隔符号
* @returns {*[]|*}
*/
export function parseJsonArray(text, symbol = ',') {
try {
if (isVoid(text)) return []
if (_typeof(text) === Array) return text
const original = evil(text)
if (_typeof(original) === Array) return original
return [original]
} catch (e) {
if (text.includes(symbol)) return text.split(symbol)
return [text]
}
}
/**
* 过滤HTML标签
* @param text 需要过滤的文本
* @returns {string} 过滤后的文本
*/
export function filterHtmlTag(text) {
return text.replace(/</g, '<').replace(/</g, '>')
}
/**
* 回车符替换为<br>标签
* @param text
* @returns {string}
*/
export function replaceEnterSymbol(text) {
return text.replace(/\r\n/g, '<br>').replace(/\/r\/n/g, '<br>').replace(/\/n/g, '<br>').replace(/\n/g, '<br>')
}
/**
* 是否是IE浏览器
* @param version
* @returns {boolean}
*/
export function isIE(version) {
function isAnyIeVersion() {
const agent = navigator.userAgent.toLowerCase()
return agent.indexOf('msie') !== -1 || agent.indexOf('trident') !== -1 || agent.indexOf(' edge/') !== -1
}
if (!isAnyIeVersion()) {
return false
}
if (!version) {
return true
}
// Shamelessly stolen from https://gist.github.com/padolsey/527683
const ieVersion = (function() {
let v = 3
const div = document.createElement('div')
const all = div.getElementsByTagName('i')
do {
div.innerHTML = '<!--[if gt IE ' + ++v + ']><i></i><![endif]-->'
} while (all[0])
return v > 4 ? v : void (0)
})()
return version === ieVersion
}
/**
* 获取滚动条的宽度
* @returns {number}
*/
export function getScrollWidth() {
// 创建一个div元素
const oDiv = document.createElement('div')
oDiv.style.cssText = 'position:absolute; top:-1000px; width:100px; height:100px; overflow:hidden;'
const noScroll = document.body.appendChild(oDiv).clientWidth
// 没有滚动条的clientWidth为content+paddingLeft+paddingRight不包括滚动条,可以利用这个获取差值来求滚动条的宽度
oDiv.style.overflowY = 'scroll'
const scroll = oDiv.clientWidth
document.body.removeChild(oDiv)
return noScroll - scroll
}
/**
* 节流函数,表示一段时间内只执行一次
* 使用方法: throttle(func, 200)
* @param fn 需要执行方法
* @param {number} delay 毫秒,节流期限值
* @param {boolean} immediate 是否立即执行一次, 再等待节流
*/
let flagThrottle = true
export function throttle(fn, delay = 200, immediate = true) {
return (function() {
if (!flagThrottle) return
flagThrottle = false
const args = arguments
if (immediate) {
fn.apply(this, args) // 立即执行
}
setTimeout(() => {
if (!immediate) {
fn.apply(this, args) // 非立即执行
}
flagThrottle = true
}, delay)
}())
}
/**
* 在一个区间内获取一个随机整数
* @param min
* @param max
* @returns {number}
*/
export function randomIn(min, max) {
return Math.round(Math.random() * (max - min) + min)
}