359 lines
8.6 KiB
JavaScript
359 lines
8.6 KiB
JavaScript
|
|
import { prefix } from './config';
|
|||
|
|
import { isString, isNumeric, isDef, isBoolean, isObject } from './validator';
|
|||
|
|
import { getWindowInfo, getAppBaseInfo, getDeviceInfo } from './wechat';
|
|||
|
|
|
|||
|
|
export { getWindowInfo };
|
|||
|
|
|
|||
|
|
export const systemInfo = getWindowInfo();
|
|||
|
|
|
|||
|
|
export const appBaseInfo = getAppBaseInfo();
|
|||
|
|
|
|||
|
|
export const deviceInfo = getDeviceInfo();
|
|||
|
|
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 多参数空值合并函数
|
|||
|
|
* @param {...any} args - 任意数量的参数
|
|||
|
|
* @returns {any} 第一个非null/undefined的参数值
|
|||
|
|
*/
|
|||
|
|
export function coalesce(...args) {
|
|||
|
|
// 遍历所有参数
|
|||
|
|
for (let i = 0; i < args.length; i += 1) {
|
|||
|
|
// 返回第一个非null且非undefined的值
|
|||
|
|
if (args[i] !== null && args[i] !== undefined) {
|
|||
|
|
return args[i];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 如果所有参数都是null/undefined,返回最后一个参数
|
|||
|
|
return args[args.length - 1];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
export const debounce = function (func, wait = 500) {
|
|||
|
|
let timerId;
|
|||
|
|
return function (...rest) {
|
|||
|
|
if (timerId) {
|
|||
|
|
clearTimeout(timerId);
|
|||
|
|
}
|
|||
|
|
timerId = setTimeout(() => {
|
|||
|
|
func.apply(this, rest);
|
|||
|
|
}, wait);
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const throttle = (func, wait = 100, options = null) => {
|
|||
|
|
let previous = 0;
|
|||
|
|
let timerid = null;
|
|||
|
|
|
|||
|
|
if (!options) {
|
|||
|
|
options = {
|
|||
|
|
leading: true,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return function (...args) {
|
|||
|
|
const now = Date.now();
|
|||
|
|
|
|||
|
|
if (!previous && !options.leading) previous = now;
|
|||
|
|
|
|||
|
|
const remaining = wait - (now - previous);
|
|||
|
|
const context = this;
|
|||
|
|
|
|||
|
|
if (remaining <= 0) {
|
|||
|
|
if (timerid) {
|
|||
|
|
clearTimeout(timerid);
|
|||
|
|
timerid = null;
|
|||
|
|
}
|
|||
|
|
previous = now;
|
|||
|
|
func.apply(context, args);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const classNames = function (...args) {
|
|||
|
|
const hasOwn = {}.hasOwnProperty;
|
|||
|
|
const classes = [];
|
|||
|
|
|
|||
|
|
args.forEach((arg) => {
|
|||
|
|
// for (let i = 0; i < args.length; i++) {
|
|||
|
|
// eslint-disable-next-line
|
|||
|
|
// const arg = args[i]
|
|||
|
|
if (!arg) return;
|
|||
|
|
|
|||
|
|
const argType = typeof arg;
|
|||
|
|
|
|||
|
|
if (argType === 'string' || argType === 'number') {
|
|||
|
|
classes.push(arg);
|
|||
|
|
} else if (Array.isArray(arg) && arg.length) {
|
|||
|
|
const inner = classNames(...arg);
|
|||
|
|
if (inner) {
|
|||
|
|
classes.push(inner);
|
|||
|
|
}
|
|||
|
|
} else if (argType === 'object') {
|
|||
|
|
// eslint-disable-next-line
|
|||
|
|
for (const key in arg) {
|
|||
|
|
if (hasOwn.call(arg, key) && arg[key]) {
|
|||
|
|
classes.push(key);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return classes.join(' ');
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const styles = function (styleObj) {
|
|||
|
|
return Object.keys(styleObj)
|
|||
|
|
.map(styleKey => `${styleKey}: ${styleObj[styleKey]}`)
|
|||
|
|
.join('; ');
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const getAnimationFrame = function (context, cb) {
|
|||
|
|
return uni
|
|||
|
|
.createSelectorQuery()
|
|||
|
|
.in(context)
|
|||
|
|
.selectViewport()
|
|||
|
|
.boundingClientRect()
|
|||
|
|
.exec(() => {
|
|||
|
|
cb();
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const getRect = function (context, selector, needAll = false, useH5Origin = false) {
|
|||
|
|
let result;
|
|||
|
|
// #ifdef H5
|
|||
|
|
if (useH5Origin) {
|
|||
|
|
result = document[needAll ? 'querySelectorAll' : 'querySelector'](selector)?.getBoundingClientRect();
|
|||
|
|
}
|
|||
|
|
// #endif
|
|||
|
|
if (result) {
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
uni
|
|||
|
|
.createSelectorQuery()
|
|||
|
|
.in(context)
|
|||
|
|
// eslint-disable-next-line no-unexpected-multiline
|
|||
|
|
[needAll ? 'selectAll' : 'select'](selector)
|
|||
|
|
.boundingClientRect((rect) => {
|
|||
|
|
if (rect) {
|
|||
|
|
resolve(rect);
|
|||
|
|
} else {
|
|||
|
|
reject(rect);
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.exec();
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
export const getTreeDepth = (tree, key) => tree.reduce((maxDepth, node) => {
|
|||
|
|
const keyName = coalesce(key, 'children');
|
|||
|
|
if (node[keyName] && node[keyName].length > 0) {
|
|||
|
|
return Math.max(maxDepth, getTreeDepth(node[keyName], key) + 1);
|
|||
|
|
}
|
|||
|
|
return Math.max(maxDepth, 1);
|
|||
|
|
}, 0);
|
|||
|
|
|
|||
|
|
export const isIOS = function () {
|
|||
|
|
return !!(deviceInfo?.system?.toLowerCase().search('ios') + 1);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 判断是否是为企微环境
|
|||
|
|
* 企微环境 wx.getSystemInfoSync() 接口会额外返回 environment 字段(微信中不返回)
|
|||
|
|
* https://developer.work.weixin.qq.com/document/path/91511
|
|||
|
|
*/
|
|||
|
|
export const isWxWork = deviceInfo?.environment === 'wxwork';
|
|||
|
|
|
|||
|
|
export const isPC = ['mac', 'windows'].includes(deviceInfo?.platform);
|
|||
|
|
|
|||
|
|
export const addUnit = function (value) {
|
|||
|
|
if (!isDef(value)) {
|
|||
|
|
return undefined;
|
|||
|
|
}
|
|||
|
|
value = String(value);
|
|||
|
|
return isNumeric(value) ? `${value}px` : value;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 计算字符串字符的长度并可以截取字符串。
|
|||
|
|
* @param char 传入字符串(maxcharacter条件下,一个汉字表示两个字符)
|
|||
|
|
* @param max 规定最大字符串长度
|
|||
|
|
* @returns 当没有传入maxCharacter/maxLength 时返回字符串字符长度,当传入maxCharacter/maxLength时返回截取之后的字符串和长度。
|
|||
|
|
*/
|
|||
|
|
export const getCharacterLength = (type, char, max) => {
|
|||
|
|
const str = String(coalesce(char, ''));
|
|||
|
|
|
|||
|
|
if (str.length === 0) {
|
|||
|
|
return {
|
|||
|
|
length: 0,
|
|||
|
|
characters: '',
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (type === 'maxcharacter') {
|
|||
|
|
let len = 0;
|
|||
|
|
for (let i = 0; i < str.length; i += 1) {
|
|||
|
|
let currentStringLength = 0;
|
|||
|
|
if (str.charCodeAt(i) > 127 || str.charCodeAt(i) === 94) {
|
|||
|
|
currentStringLength = 2;
|
|||
|
|
} else {
|
|||
|
|
currentStringLength = 1;
|
|||
|
|
}
|
|||
|
|
if (len + currentStringLength > max) {
|
|||
|
|
return {
|
|||
|
|
length: len,
|
|||
|
|
characters: str.slice(0, i),
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
len += currentStringLength;
|
|||
|
|
}
|
|||
|
|
return {
|
|||
|
|
length: len,
|
|||
|
|
characters: str,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (type === 'maxlength') {
|
|||
|
|
const length = str.length > max ? max : str.length;
|
|||
|
|
return {
|
|||
|
|
length,
|
|||
|
|
characters: str.slice(0, length),
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
length: str.length,
|
|||
|
|
characters: str,
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const chunk = (arr, size) => Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => arr.slice(i * size, i * size + size));
|
|||
|
|
|
|||
|
|
|
|||
|
|
const getPageContext = () => {
|
|||
|
|
const pages = getCurrentPages();
|
|||
|
|
const page = pages[pages.length - 1];
|
|||
|
|
return (page).$$basePage || page;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
const findInstance = (context, pageContext, pureSelector) => {
|
|||
|
|
if (context && context.$refs[pureSelector]) {
|
|||
|
|
return context.$refs[pureSelector];
|
|||
|
|
}
|
|||
|
|
if (pageContext && pageContext.$refs[pureSelector]) {
|
|||
|
|
return pageContext.$refs[pureSelector];
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const getInstance = function (context, selector) {
|
|||
|
|
const pageContext = getPageContext();
|
|||
|
|
const pureSelector = /^[.#]/.test(selector) ? selector.slice(1) : selector;
|
|||
|
|
const instance = findInstance(context, pageContext, pureSelector);
|
|||
|
|
|
|||
|
|
if (!instance) {
|
|||
|
|
console.warn('未找到组件,请检查 selector 是否正确');
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
return instance;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const unitConvert = (value) => {
|
|||
|
|
if (typeof value === 'string') {
|
|||
|
|
if (value.includes('rpx')) {
|
|||
|
|
return (parseInt(value, 10) * (coalesce(systemInfo?.screenWidth, 750))) / 750;
|
|||
|
|
}
|
|||
|
|
return parseInt(value, 10);
|
|||
|
|
}
|
|||
|
|
return coalesce(value, 0);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const setIcon = (iconName, icon, defaultIcon) => {
|
|||
|
|
if (icon) {
|
|||
|
|
if (typeof icon === 'string') {
|
|||
|
|
return {
|
|||
|
|
[`${iconName}Name`]: icon,
|
|||
|
|
[`${iconName}Data`]: {},
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
if (typeof icon === 'object') {
|
|||
|
|
return {
|
|||
|
|
[`${iconName}Name`]: '',
|
|||
|
|
[`${iconName}Data`]: icon,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
return {
|
|||
|
|
[`${iconName}Name`]: defaultIcon,
|
|||
|
|
[`${iconName}Data`]: {},
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
return {
|
|||
|
|
[`${iconName}Name`]: '',
|
|||
|
|
[`${iconName}Data`]: {},
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const toCamel = str => str.replace(/-(\w)/g, (match, m1) => m1.toUpperCase());
|
|||
|
|
export const toPascal = name => name
|
|||
|
|
.split('-')
|
|||
|
|
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|||
|
|
.join('');
|
|||
|
|
|
|||
|
|
export function hyphenate(str) {
|
|||
|
|
const hyphenateRE = /\B([A-Z])/g;
|
|||
|
|
return str.replace(hyphenateRE, '-$1').toLowerCase();
|
|||
|
|
}
|
|||
|
|
export const getCurrentPage = function () {
|
|||
|
|
const pages = getCurrentPages();
|
|||
|
|
return pages[pages.length - 1];
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const uniqueFactory = (compName) => {
|
|||
|
|
let number = 0;
|
|||
|
|
return () => {
|
|||
|
|
const uniqueId = `${prefix}_${compName}_${number}`;
|
|||
|
|
number += 1;
|
|||
|
|
return uniqueId;
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const calcIcon = (icon, defaultIcon) => {
|
|||
|
|
if (icon && ((isBoolean(icon) && defaultIcon) || isString(icon))) {
|
|||
|
|
return { name: isBoolean(icon) ? defaultIcon : icon };
|
|||
|
|
}
|
|||
|
|
if (isObject(icon)) {
|
|||
|
|
return icon;
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const isOverSize = (size, sizeLimit) => {
|
|||
|
|
if (!sizeLimit) return false;
|
|||
|
|
|
|||
|
|
const base = 1000;
|
|||
|
|
const unitMap = {
|
|||
|
|
B: 1,
|
|||
|
|
KB: base,
|
|||
|
|
MB: base * base,
|
|||
|
|
GB: base * base * base,
|
|||
|
|
};
|
|||
|
|
const computedSize = typeof sizeLimit === 'number'
|
|||
|
|
? sizeLimit * base
|
|||
|
|
: sizeLimit?.size * unitMap[coalesce(sizeLimit?.unit, 'KB')]; // 单位 KB
|
|||
|
|
|
|||
|
|
return size > computedSize;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const rpx2px = rpx => Math.floor((systemInfo.windowWidth * rpx) / 750);
|
|||
|
|
|
|||
|
|
export const nextTick = () => new Promise((resolve) => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
resolve();
|
|||
|
|
}, 33);
|
|||
|
|
});
|
|||
|
|
|