wft-utils
Version:
The commonly used tool functions in daily development
1,311 lines (1,223 loc) • 34.1 kB
JavaScript
/**
* 深拷贝
* @param {object} source
* @returns
*/
export function deepClone(source) {
if (typeof source !== 'object' || source === null) return source
const target = source.constructor === Array ? [] : {}
for (let key in source) {
if (source.hasOwnProperty(key)) {
if (source[key] && typeof source[key] === 'object') {
target[key] = deepClone(source[key])
} else {
target[key] = source[key]
}
}
}
return target
}
/**
* 异步深拷贝(不支持函数、Symbol、WeakMap、WeakSet等)
* @param {*} obj
* @param {*} cb
* @returns
*/
export function deepCloneAsync(obj, cb) {
return new Promise((resolve, reject) => {
try {
const { port1, port2 } = new MessageChannel()
port1.postMessage(obj)
port2.onmessage = data => {
if (cb && typeof cb === 'function') cb(data.data)
resolve(data.data)
}
} catch (e) {
reject(e)
}
})
}
/**
* 同步监听事件
* @param {HTMLElement} el
* @param {click | dblclick | contextmenu | mousedown | mousemove | mouseup | mousewheel | mouseenter | mouseover | mouseleave | mouseout} name
* @param {Function} cb
* @param {number} iteration
* @returns
*/
export function syncEvent(el, name, cb, iteration = 0) {
if (iteration < 0) {
return console.error('SyncEvent: Iteration must be greater than or equal to 0')
}
function getElement(el) {
return new Proxy(el, {
get(target, key) {
if (!key.startsWith('wait')) {
return target[key]
}
return new Promise(resolve => {
el.addEventListener(key.replace('wait', '').toLowerCase(), resolve, { once: true })
})
}
})
}
(async () => {
const target = getElement(el)
if (iteration === 0) {
while (true) {
const event = await target[`wait${name}`]
cb(event)
}
} else {
for (let i = 0; i < iteration; i++) {
const event = await target[`wait${name}`]
cb(event)
}
}
})()
}
/**
* 表单序列化
* @param {object} data
* @returns
*/
export function serialize(data) {
const list = []
Object.keys(data).forEach(key => {
list.push(`${key}=${data[key]}`)
})
return list.join('&')
}
/**
* 随机打乱数组顺序
* @param {Array} arr
* @returns
*/
export function shuffle(arr) {
for (let i = arr.length - 1; i > 0; i--) {
const randomIndex = Math.floor(Math.random() * (i + 1));
[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]
}
return arr
}
/**
* 随机获取一个Boolean值
* @returns
*/
export function getRandomBoolean() {
return 0.5 - Math.random() >= 0
}
/**
* 随机获取一个颜色
* @returns
*/
export function getRandomColor() {
return `#${Math.floor(Math.random() * 0xffffff).toString(16)}`;
}
/**
* hex 转 rgba 格式
* @param {string} hex
* @param {number} opacity
* @returns
*/
export function hexToRgba(hex, opacity = 1) {
return {
r: parseInt("0x" + hex.slice(1, 3)),
g: parseInt("0x" + hex.slice(3, 5)),
b: parseInt("0x" + hex.slice(5, 7)),
a: opacity
}
}
/**
* hex 转 rgb
* @param {string} str
* @returns
*/
export function hexToRgb(str) {
str = str.replace('#', '')
let hexs = str.match(/../g)
for (let i = 0; i < 3; i++) {
hexs[i] = parseInt(hexs[i], 16)
}
return hexs
}
/**
* rgb 转 Hex
* @param {number|string} r
* @param {number|string} g
* @param {number|string} b
* @returns
*/
export function rgbToHex(r, g, b) {
let hexs = [r.toString(16), g.toString(16), b.toString(16)]
for (let i = 0; i < 3; i++) {
if (hexs[i].length == 1) {
hexs[i] = `0${hexs[i]}`
}
}
return `#${hexs.join('')}`
}
/**
* 匹配body之间的内容 富文本编辑器Editor中使用到了
* @param {Object} content
*/
export function getBody(content) {
const REG_BODY = /<body[^>]*>([\s\S]*)<\/body>/;
const result = REG_BODY.exec(content);
if (result && result.length === 2)
return result[1];
return content;
}
/**
* 判断是否为空 校验
* @param {Object} val
*/
export function isEmpty(val) {
if (Array.isArray(val) || val instanceof Array) {
return val.length === 0
} else if (val instanceof Object || val.constructor === Object) {
if (JSON.stringify(val) === '{}') return true
} else {
if (val === null || val === undefined || val === '') return true
return false
}
return false
}
/**
* 预览pdf
* @param {File} blob
* @param {string} docTitle
*/
export function preViewPdf(blob, docTitle) {
const URL = window.URL || window.webkitURL;
const href = URL.createObjectURL(new Blob([blob], { type: 'application/pdf; charset=utf-8' }))
// 打开新页签 预览pdf
const wo = window.open(href)
// 设置title
let timer = setInterval(() => {
if (wo.closed) {
clearInterval(timer)
} else {
wo.document.title = docTitle
}
}, 500)
}
/**
* 文件下载
* @param {File} blob
* @param {string} filename
*/
export function download(blob, filename = 'WFT.xlsx') {
const link = document.createElement('a')
const URL = window.URL || window.webkitURL;
const href = URL.createObjectURL(new Blob([blob]))
link.href = href
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
// URL.revokeObjectURL(href) // 释放掉blob对象
}
/**
* 从视频文件中提取一帧图片
* @param {Blob} vdoFile 视频文件
* @param {number} time 秒数
* @returns
*/
export function captureFrame(vdoFile, time = 0) {
return new Promise((resolve, reject) => {
try {
const video = document.createElement('video');
video.currentTime = time;
video.muted = true;
video.autoplay = true;
video.src = URL.createObjectURL(vdoFile);
video.oncanplay = () => {
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
canvas.toBlob(blob => {
const url = URL.createObjectURL(blob)
resolve({ blob, url })
}, 'image/jpeg', 0.8)
}
} catch (error) {
reject(error)
}
})
}
/**
* 防抖
* @param {Function} fn
* @param {Number} wait
* @param {Boolean} immediate
* @returns
*/
export function debounce(fn, wait, immediate = false) {
let timer = null
let isInvoke = false
function _debounce() {
return new Promise((resolve, reject) => {
try {
if (timer) clearTimeout(timer)
// 第一次操作是否不需要延迟、立即执行
let res = undefined
if (immediate && !isInvoke) {
res = fn.apply(this, Array.from(arguments))
resolve(res)
isInvoke = true
return
}
// 延迟执行
timer = setTimeout(() => {
res = fn.apply(this, Array.from(arguments))
resolve(res)
timer = null
isInvoke = false
}, wait)
} catch (error) {
reject(error)
}
})
}
// 取消功能
_debounce.cancel = function () {
if (timer) clearTimeout(timer)
timer = null
isInvoke = false
}
return _debounce
}
/**
* 节流
* @param {Function} fn
* @param {Number} wait
* @returns
*/
export function throttle(fn, wait) {
let timer = null
return function () {
if (!timer) {
fn.apply(this, Array.from(arguments))
timer = setTimeout(() => {
timer = null
}, wait)
}
}
}
/**
* 使用代理创建单例
* @param {Function | class} className
* @returns
*/
export function singleton(className) {
let instance = null;
const proxy = new Proxy(className, {
construct(_target, args) {
if (!instance) {
instance = new className(...args)
} else {
console.warn(`${className}只有一个实例.`)
}
return instance;
}
})
className.prototype.constructor = proxy;
return proxy;
}
/**
* 自动转化柯里化过程
* @param {Function} fn
* @returns
*/
export function ftCurrying(fn) {
function curryingFn(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args)
} else {
return function (...nextArgs) {
return curryingFn.apply(this, [...args, ...nextArgs])
}
}
}
return curryingFn
}
/**
* 创建数组迭代器
* @param {Array | string} arr
* @returns
*/
export function createArrayIterator(arr) {
let index = 0
return {
next() {
if (index < arr.length) {
return { done: false, value: arr[index++] }
} else {
return { done: true, value: undefined }
}
}
}
}
/**
* 创建数组生成器
* @param {Array | string} arr
*/
export function* createArrayGenerator(arr) {
yield* arr
}
/**
* 生成uuid
* @returns
*/
export function getUuid() {
const temp_url = URL.createObjectURL(new Blob())
const uuid = temp_url.toString()
URL.revokeObjectURL(temp_url) //释放这个url
return uuid.substring(uuid.lastIndexOf('/') + 1)
}
/**
* 生成uuid
* @returns
*/
export function generateUUID() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => (c ^ window.crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));
}
/**
* 生成随机字符串(长度通常在5-11个字符之间)
* @returns
*/
export function getRandomStr() {
return Math.random().toString(36).slice(2)
}
/**
* base64编码转Blob
* @param {string} base64Data
* @returns
*/
export function base64ToBlob(base64Data) {
let arr = base64Data.split(','),
fileType = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
l = bstr.length,
u8Arr = new Uint8Array(l);
while (l--) {
u8Arr[l] = bstr.charCodeAt(l);
}
return new Blob([u8Arr], {
type: fileType
});
}
/**
* 根据身份证号获取年龄
* @param {String} idCard
*/
export function getAgeFromID(idCard) {
// 检查身份证号是否合法
if (!/^\d{17}[\dXx]$/.test(idCard)) {
return;
}
// 获取出生日期
let year = parseInt(idCard.substring(6, 10), 10);
let month = parseInt(idCard.substring(10, 12), 10);
let day = parseInt(idCard.substring(12, 14), 10);
// 创建出生日期对象
let birthDate = new Date(year, month - 1, day);
// 获取当前日期
let today = new Date();
// 计算年龄
let age = today.getFullYear() - birthDate.getFullYear();
let monthDiff = today.getMonth() - birthDate.getMonth();
// 如果月份或日期还未到达,则年龄减一
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
}
/**
* 根据身份证号获取性别
* @param {String} idCard
*/
export function getGenderFromID(idCard) {
// 验证身份证号是否合法
if (!/^\d{17}[\dXx]$/.test(idCard)) {
return null;
}
// 提取第17位数字
const genderDigit = parseInt(idCard.charAt(16), 10);
if (isNaN(genderDigit)) {
return null;
}
// 判断性别
if (genderDigit % 2 === 0) {
return "女";
} else {
return "男";
}
}
/**
* 解析路径中的参数
* @param {string} url
* @returns
*/
export function getQueryParamsFromUrl(url) {
if (!url || typeof url !== 'string') {
return {}
}
const urlParse = queryString => {
const urlObj = new URL(queryString);
const params = new URLSearchParams(urlObj.search);
const result = {};
for (const [key, value] of params.entries()) {
result[key] = value;
}
return result;
}
const regexParse = queryString => {
const result = {};
const regex = /([^?&=]+)=([^&]*)/g; // 匹配 key=value 形式的键值对
let match;
while ((match = regex.exec(queryString)) !== null) {
const key = decodeURIComponent(match[1]);
const value = decodeURIComponent(match[2]);
result[key] = value;
}
return result;
}
if (window.URL && window.URLSearchParams) {
return urlParse(url)
}
return regexParse(url)
}
/**
* 判断手机是Andoird还是IOS
* @returns
*/
export function getOSType() {
let u = navigator.userAgent;
let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1;
let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
if (isIOS) return 1;
if (isAndroid) return 2;
return 3;
}
/**
* 手机号脱敏
* @param {string} mobile
* @returns
*/
export function hideMobile(mobile) {
return mobile.replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2")
}
/**
* 驼峰转下划线
* @param {string} name
* @returns
*/
export function hump2Line(name) {
if (name.indexOf('.') < 0) {
return name.replace(/([A-Z])/g, '_$1').toLowerCase()
} else {
return name
}
}
/**
* 下划线转驼峰
* @param {string} str
* @returns
*/
export function line2Hump(str) {
return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase())
}
/**
* 创建html字符串
* @param {HTMLElement} node
* @returns
*/
export function createHtmlStr(node) {
const div = document.createElement('div')
div.appendChild(node)
return div.innerHTML
}
/**
* html转文本
* @param {string} val
* @returns
*/
export function html2Text(val) {
const div = document.createElement('div')
div.innerHTML = val
return div.textContent || div.innerText
}
/**
* 复制文本到剪切板
* @param {string} text
* @returns
*/
export function copyText(text) {
return new Promise((resolve, reject) => {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(resolve, reject)
} else {
const textArea = document.createElement('textarea')
textArea.value = text
textArea.style.opacity = '0'
document.body.appendChild(textArea)
textArea.select()
// 复制文本
try {
document.execCommand('copy');
resolve(undefined)
} catch (err) {
reject(err)
}
document.body.removeChild(textArea)
}
})
}
/**
* 检验数据类型
* @param {any} obj
* @returns
*/
export function getObjType(obj) {
let toString = Object.prototype.toString
let map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object AsyncFunction]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object'
}
if (obj instanceof Element) {
return 'element'
}
return map[toString.call(obj)]
}
/**
* 转树形数据
* @param {*} data list数据
* @param {*} id 主键ID
* @param {*} pid 上级ID
* @param childrenKey 子list数据的key
*/
export function treeDataTranslate(data, id = 'id', pid = 'parentId', childrenKey = 'children') {
let res = []
let temp = {}
for (let i = 0; i < data.length; i++) {
temp[data[i][id]] = data[i]
}
for (let k = 0; k < data.length; k++) {
if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) {
if (!temp[data[k][pid]][childrenKey]) {
temp[data[k][pid]][childrenKey] = []
}
if (!temp[data[k][pid]]['_level']) {
temp[data[k][pid]]['_level'] = 1
}
data[k]['_level'] = temp[data[k][pid]]._level + 1
temp[data[k][pid]][childrenKey].push(data[k])
} else {
res.push(data[k])
}
}
return res
}
/**
* 转树形结构
* @param {Array} list list数据
* @param {string} id 主键ID
* @param {string} pid 上级ID
* @param {string} childrenKey 标识子节点的key
* @returns
*/
export function toTree(list, id = 'id', pid = 'parentId', childrenKey = 'children') {
const target = []
const map = {}
list.forEach(item => {
map[item[id]] = item
})
list.forEach(item => {
let parent = map[item[pid]]
if (parent) {
(parent[childrenKey] || (parent[childrenKey] = [])).push(item)
} else {
target.push(item)
}
})
return target
}
/**
* 遍历树形数据
* @param {*} treeData 树形数据
* @param {*} callback 每次遍历的回调函数
* @param {*} childrenKey 树形数据中标识子节点的字段
* @param {*} level 级别
*/
export function mapTree(treeData, callback, childrenKey = 'children', level = 0) {
treeData.forEach(item => {
const curItem = JSON.parse(JSON.stringify(item))
curItem.level = level
callback(curItem, treeData)
if (item?.[childrenKey]?.length) {
mapTree(item[childrenKey], callback, childrenKey, level + 1)
}
})
}
/**
* 扁平树形(广度优先遍历)
* @param {*} root 根节点<Object>
* @returns
*/
export function bfsFlatTree(root) {
const target = []
const arr = [root]
for (let item of arr) {
const o = JSON.parse(JSON.stringify(item))
if (o.children) delete o.children
target.push(o)
if (item.children && item.children.length) {
item.children.forEach(ele => {
arr.push(ele)
})
}
}
return target
}
/**
* 树形结构根据里层ID找出所属父级集合
*/
export class FindParents {
constructor({
tree,
id,
idKey = 'id',
parentIdKey = 'parentId',
childrenKey = 'children',
rootId = '0'
}) {
this.idKey = idKey
this.parentIdKey = parentIdKey
this.childrenKey = childrenKey
this.rootId = rootId
this.flatList = this.flatTree(tree)
this.parents = this.getParents(id, this.flatList)
this.getParentValuesByKey.bind(this)
}
flatTree(tree) { // 扁平树型(深度遍历)
const target = []
tree.forEach(item => {
const cloneItem = JSON.parse(JSON.stringify(item))
if (cloneItem[this.childrenKey]) delete cloneItem[this.childrenKey]
target.push(cloneItem)
if (item[this.childrenKey] && item[this.childrenKey].length) {
target.push(...this.flatTree(item[this.childrenKey]))
}
})
return target
}
getParents(id, list) {
const target = []
const data = list.find(item => item[this.idKey] === id)
if (data === undefined) return target
target.push(data)
if (data[this.parentIdKey] !== this.rootId) {
target.push(...this.getParents(data[this.parentIdKey], list))
}
return target
}
getParentValuesByKey(key) {
return this.parents.map(item => item[key]).reverse()
}
}
/**
* 树形结构根据里层ID找出所属父级集合
*/
export class FindArrsById {
constructor(id, tree) {
this.id = id
this.flatList = this.flatTreeAndSetLevel.call(this, tree)
this.parents = this.getParents.call(this, id, this.flatList)
this.getParentValuesByKey.bind(this)
}
flatTreeAndSetLevel(tree, level = 0) { // 扁平树型(深度遍历)
const target = []
tree.forEach(item => {
const o = JSON.parse(JSON.stringify(item))
if (o.children) delete o.children
o.level = level
target.push(o)
if (item.children && item.children.length) {
target.push(...this.flatTreeAndSetLevel(item.children, level + 1))
}
})
return target
}
getParents(id, list) {
const target = []
const o = list.find(item => item.id === id) || {}
if (JSON.stringify(o) !== '{}') target.push(o)
if (o.parentId) target.push(...this.getParents(o.parentId, list))
return target
}
getParentValuesByKey(key) {
return this.parents.map(item => item[key]).reverse()
}
}
/**
* 对象去重
* @param {*} obj
* @returns
*/
export function noRepeatObj(obj) {
if (obj.constructor !== Object) return {}
const target = {}
for (let key in obj) {
let flag = true
for (let k in target) {
if (JSON.stringify(target[k]) === JSON.stringify(obj[key])) {
flag = false
}
}
if (flag) {
target[key] = obj[key]
}
}
return target
}
/**
* 数组去重
* @param {*} list
* @returns
*/
export function noRepeatArr(list) {
if (list.every(item => typeof item !== 'object')) {
return [...new Set(list)]
}
return [...new Set(list.map(item => JSON.stringify(item)))].map(item => JSON.parse(item))
}
/**
* 对象数组,根据某个字段去重 例如: [ { id: 0, name: 'wft1' }, { id: 0, name: 'wft_1' }, { id: 1, name: 'wft2' } ]
* @param {*} list
* @param {*} field
*/
export function noRepeatArrByField(list, field = 'id') {
const map = {}
list.forEach(item => {
if (!map[item[field]]) {
map[item[field]] = item
}
})
return Object.values(map)
}
/**
* 数组去重
* 两个属性相同的对象 也认为是重复的
* 这里采用原始的处理方式去重(当然如果是引用类型的话可以通过JSON.stringify转为json字符串 然后通过ES6中的Set去重)
* @param {Array} arr
*/
export function uniqueArray(arr) {
const target = []
for (let i = 0; i < arr.length; i++) {
let flag = true
for (let j = 0; j < target.length; j++) {
if (equals(arr[i], target[j])) {
flag = false
break
}
}
if (flag) {
target.push(arr[i])
}
}
return target
}
/**
* 比较两个值是否相等(两个属性相同的对象 也认为是重复的)
* @param {*} value1
* @param {*} value2
*/
function equals(value1, value2) {
if (isBasicType(value1) || isBasicType(value2)) {
return Object.is(value1, value2)
}
if (Object.keys(value1).length !== Object.keys(value2).length) {
return false
}
for (let key in value1) {
if (!equals(value1[key], value2[key])) {
return false
}
}
return true
}
/**
* 判断是否是基本数据类型(这里null虽然是object类型,但也可以直接通过Object.is判断相等,所以暂认为基本类型)
* @param {*} value
* @returns
*/
function isBasicType(value) {
const referenceType = ['object', 'function']
return !referenceType.includes(typeof value) || value === null
}
/**
* 千分位数字转换
* @param {*} value
* @returns
*/
export function numberToCurrencyNo(value) {
if (!value) return '0'
if (value === '--') return '--'
value = value - 0
// 将数值截取,保留0位小数
value = value.toFixed(0)
// 获取整数部分
const intPart = Math.trunc(value)
// 整数部分处理,增加,
const intPartFormat = intPart.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
// 预定义小数部分
let floatPart = ''
// 将数值截取为小数部分和整数部分
const valueArray = value.toString().split('.')
if (valueArray.length === 2) { // 有小数部分
floatPart = valueArray[1].toString() // 取得小数部分
return intPartFormat + '.' + floatPart
}
return intPartFormat + floatPart
}
/**
* 表单对象赋值:
* 对目标对象存在且源对象同样存在的属性,全部覆盖;
* 目标对象不存在但是源对象存在的属性, 全部丢弃;
* 目标对象存在但是源对象不存在的属性,如果是字符串赋值为空串,其余类型赋值为undefined
* @param {Object} target
* @param {Object} source
* @returns
*/
export function recover(target, source) {
if (target === undefined || target === null) { throw new TypeError('Cannot convert first argument to object') }
var to = Object(target)
if (source === undefined || source === null) { return to }
var keysArray = Object.keys(Object(target))
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex]
var desc = Object.getOwnPropertyDescriptor(target, nextKey)
if (desc !== undefined && desc.enumerable) {
if (to.hasOwnProperty(nextKey)) {
if (to[nextKey] instanceof Array) {
to[nextKey] = source[nextKey]
} else if (to[nextKey] instanceof Object) {
recover(to[nextKey], source[nextKey])
} else if (source[nextKey] !== undefined) {
to[nextKey] = source[nextKey]
} else if (typeof (to[nextKey]) === 'string') {
to[nextKey] = ''
} else {
to[nextKey] = undefined
}
}
}
}
return to
}
/**
* 表单对象赋值:
* 对目标对象存在且源对象同样存在的属性,全部覆盖;
* 目标对象不存在但是源对象存在的属性, 全部丢弃;
* 目标对象存在但是源对象不存在的属性,保留目标对象的属性不做处理
* @param {Object} target
* @param {Object} source
* @returns
*/
export function recoverNotNull(target, source) {
if (target === undefined || target === null) { throw new TypeError('Cannot convert first argument to object') }
var to = Object(target)
if (source === undefined || source === null) { return to }
var keysArray = Object.keys(Object(target))
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex]
var desc = Object.getOwnPropertyDescriptor(target, nextKey)
if (desc !== undefined && desc.enumerable) {
if (to.hasOwnProperty(nextKey)) {
if (to[nextKey] instanceof Array) {
to[nextKey] = source[nextKey]
} else if (to[nextKey] instanceof Object) {
recover(to[nextKey], source[nextKey])
} else if (source[nextKey] !== undefined) {
to[nextKey] = source[nextKey]
}
}
}
}
return to
}
/**
* 判断一个指定的位置点坐标是否落在一个多边形区域内
* @param {number} aLon 经度
* @param {number} aLat 维度
* @param {Array<{ longitude: number, latitude: number }>} pointList 多边形坐标集合(多边形点的顺序需根据顺时针或逆时针,不能乱)
* @returns
*/
export function isPtInPoly(aLon, aLat, pointList) {
let iSum = 0
let iCount = pointList.length
if (iCount < 3) {
return false
}
// 待判断的点(x, y) 为已知值
let y = aLat
let x = aLon
for (let i = 0; i < iCount; i++) {
let x1 = pointList[i].longitude
let y1 = pointList[i].latitude
let x2, y2
if (i == iCount - 1) {
x2 = pointList[0].longitude
y2 = pointList[0].latitude
} else {
y2 = pointList[i + 1].latitude
x2 = pointList[i + 1].longitude
}
// 当前边的 2 个端点分别为 已知值(x1, y1), (x2, y2)
if (((y >= y1) && (y < y2)) || ((y >= y2) && (y < y1))) {
// y 界于 y1 和 y2 之间
// 假设过待判断点(x, y)的水平直线和当前边的交点为(x_intersect, y_intersect),有y_intersect = y
// 则有(2个相似三角形,公用顶角,宽/宽 = 高/高):|x1 - x2| / |x1 - x_intersect| = |y1 - y2| / |y1 - y|
if (Math.abs(y1 - y2) > 0) {
let x_intersect = x1 - ((x1 - x2) * (y1 - y)) / (y1 - y2);
if (x_intersect < x) {
iSum += 1
}
}
}
}
if (iSum % 2 != 0) {
return true
} else {
return false
}
}
/**
* 字符串大小写转换
* 1: 转大写;2:转小写;3:首字母大写
* @param {string} str
* @param {number} type
* @returns
*/
export function turnCase(str, type) {
switch (type) {
case 1:
return str.toUpperCase();
case 2:
return str.toLowerCase();
case 3:
return str[0].toUpperCase() + str.slice(1).toLowerCase();
default:
return str;
}
}
/**
* echarts字体自适应方法,px -> rem
* @param {*} res
* @returns
*/
export function fontChart(res) {
let docEl = document.documentElement,
clientWidth =
window.innerWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth;
if (!clientWidth) return;
// 此处的3840 为设计稿的宽度,记得修改!
let fontSize = clientWidth / 1920;
return res * fontSize;
}
/**
* 序列化(JSON.stringify对象序列化,解决undefined、函数和NaN 丢失问题)
* @param {*} option
* @returns
*/
export function JSONStringify(option) {
return JSON.stringify(option, (key, val) => {
// 处理函数丢失问题
if (typeof val === 'function') {
return `${val}`;
}
// 处理undefined丢失问题
if (typeof val === 'undefined') {
return 'undefined';
}
// 处理NaN转为null的情况(注意: 这里如果使用isNaN的话,那么对象也会走进去)
if (val !== val) {
return `${val}`
}
return val;
}, 2)
}
/**
* 反序列化(重写扩展JSON.parse方法)
* @param {*} jsonStr
* @returns
*/
export function JSONParse(jsonStr) {
return JSON.parse(jsonStr, (_key, val) => {
let isRetain = false
if (typeof val === 'string') {
isRetain = val.indexOf('function') >= 0 || val === 'undefined' || val === 'NaN'
}
if (isRetain) {
// eslint-disable-next-line
return eval(`(function(){return ${val}})()`);
}
return val
})
}
/**
* 打开小窗口
* @param {string} url
* @param {string} title
* @param {number} w
* @param {number} h
* @returns
*/
export function openWindow(url, title, w, h) {
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;
const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
const left = width / 2 - w / 2 + dualScreenLeft;
const top = height / 2 - h / 2 + dualScreenTop;
return window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left);
}
/**
* Storage存储
*/
export class StorageCache {
#storage = null
constructor(isLocal = true) {
this.#storage = isLocal ? window.localStorage : window.sessionStorage
}
setCache(key, value) {
const dataType = typeof value
const dateTime = new Date().getTime()
const obj = { value, dataType, dateTime }
this.#storage.setItem(key, JSON.stringify(obj))
}
getCache(key) {
const result = this.#storage.getItem(key)
return result ? JSON.parse(result).value : result
}
getAllCache() {
const list = []
for (let i = 0; i < this.#storage.length; i++) {
const curKey = this.#storage.key(i)
list.push({
key: curKey,
value: this.getCache(curKey)
})
}
return list
}
removeCache(key) {
this.#storage.removeItem(key)
}
clear() {
this.#storage.clear()
}
}
/**
* sessionStorage工具方法
*/
export class SessionUtils {
static get(key) {
return JSON.parse(window.sessionStorage.getItem(key))
}
static set(key, value) {
window.sessionStorage.setItem(key, JSON.stringify(value))
}
static remove(key) {
window.sessionStorage.removeItem(key)
}
static clear() {
window.sessionStorage.clear()
}
static getAll() {
const list = []
for (let i = 0; i < window.sessionStorage.length; i++) {
const curKey = window.sessionStorage.key(i)
list.push({
key: curKey,
value: SessionUtils.get(curKey)
})
}
return list
}
}
/**
* localStorage工具方法
*/
export class LocalUtils {
static get(key) {
return JSON.parse(window.localStorage.getItem(key))
}
static set(key, value) {
window.localStorage.setItem(key, JSON.stringify(value))
}
static remove(key) {
window.localStorage.removeItem(key)
}
static clear() {
window.localStorage.clear()
}
static getAll() {
const list = []
for (let i = 0; i < window.localStorage.length; i++) {
const curKey = window.localStorage.key(i)
list.push({
key: curKey,
value: LocalUtils.get(curKey)
})
}
return list
}
}
/**
* 使用JS完成一个LRU缓存
* 超出内存将淘汰掉最近最少使用的页面
*/
export class LRUCache {
#length = null
#map = null
constructor(length) {
this.#length = length // 存储长度
this.#map = new Map() // 存储数据
}
// 存储数据,通过键值对的方式
set(key, value) {
if (this.#map.has(key)) {
this.#map.delete(key)
}
this.#map.set(key, value)
// 如果超出了容量,则删除最久的数据(map中即第一个)
if (this.#map.size > this.#length) {
this.#map.delete(this.#map.keys().next().value)
}
}
// 获取数据
get(key) {
if (!this.#map.has(key)) {
return null
}
const val = this.#map.get(key) // 获取元素
this.#map.delete(key) // 删除元素
this.#map.set(key, val) // 重新插入元素,更新存储顺序
}
}