import { cloneDeep, intersectionWith, isEqual, mergeWith, unionWith } from 'lodash-es'; import type { App, Component } from 'vue'; import { unref } from 'vue'; import { isArray, isFunction, isObject } from './inference'; type Recordable = Record; type LabelValueOptions = { label: string; value: any; [key: string]: string | number | boolean; }[]; const noop = () => {}; function openWindow( url: string, opt?: { target?: string; noopener?: boolean; noreferrer?: boolean; }, ) { const { target = '_blank', noopener = true, noreferrer = true } = opt || {}; const feature: string[] = []; noopener && feature.push('noopener=yes'); noreferrer && feature.push('noreferrer=yes'); window.open(url, target, feature.join(',')); } /** * @description: Set ui mount node */ function getPopupContainer(node?: HTMLElement): HTMLElement { return (node?.parentNode as HTMLElement) ?? document.body; } /** * Add the object as a parameter to the URL * @param baseUrl url * @param obj * @returns {string} * eg: * let obj = {a: '3', b: '4'} * setObjToUrlParams('www.baidu.com', obj) * ==>www.baidu.com?a=3&b=4 */ function setObjToUrlParams(baseUrl: string, obj: any): string { let parameters = ''; for (const key in obj) { parameters += key + '=' + encodeURIComponent(obj[key]) + '&'; } parameters = parameters.replace(/&$/, ''); return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters; } /** * Recursively merge two objects. * 递归合并两个对象。 * * @param source The source object to merge from. 要合并的源对象。 * @param target The target object to merge into. 目标对象,合并后结果存放于此。 * @param mergeArrays How to merge arrays. Default is "replace". * 如何合并数组。默认为replace。 * - "union": Union the arrays. 对数组执行并集操作。 * - "intersection": Intersect the arrays. 对数组执行交集操作。 * - "concat": Concatenate the arrays. 连接数组。 * - "replace": Replace the source array with the target array. 用目标数组替换源数组。 * @returns The merged object. 合并后的对象。 */ function deepMerge( source: U, target: T, mergeArrays: 'union' | 'intersection' | 'concat' | 'replace' = 'replace', ): T & U { if (!target) { return source as T & U; } if (!source) { return target as T & U; } return mergeWith({}, cloneDeep(source), target, (sourceValue, targetValue) => { if (isArray(targetValue) && isArray(sourceValue)) { switch (mergeArrays) { case 'union': return unionWith(sourceValue, targetValue, isEqual); case 'intersection': return intersectionWith(sourceValue, targetValue, isEqual); case 'concat': return sourceValue.concat(targetValue); case 'replace': return targetValue; default: throw new Error(`Unknown merge array strategy: ${mergeArrays as string}`); } } if (isObject(targetValue) && isObject(sourceValue)) { return deepMerge(cloneDeep(sourceValue), targetValue, mergeArrays); } return undefined; }); } // dynamic use hook props export function getDynamicProps, U>(props: T): Partial { const ret: Recordable = {}; Object.keys(props).map((key) => { ret[key] = unref((props as Recordable)[key]); }); return ret as Partial; } // https://github.com/vant-ui/vant/issues/8302 interface EventShim { new (...args: any[]): { $props: { onClick?: (...args: any[]) => void; }; }; } type WithInstall = T & { install(app: App): void; } & EventShim; type CustomComponent = Component & { displayName?: string }; const withInstall = (component: T, alias?: string) => { (component as Record).install = (app: App) => { const compName = component.name || component.displayName; if (!compName) return; app.component(compName, component); if (alias) { app.config.globalProperties[alias] = component; } }; return component as WithInstall; }; // 替换{}内的变量 function replaceVariables(str: string, variables: Recordable): string { const pattern = /\{([^}]+)\}/g; // 匹配 {} 中的内容 return str.replace(pattern, (match, variable) => { if (variable in variables) { return variables[variable]; } return match; }); } // 转换接口options为label,value形式 const resolveOptions = ( options: T[] = [], config?: { labelField?: string; valueField?: string; deleteItem?: boolean; disabled?: (T) => boolean; }, ): LabelValueOptions => { const { labelField = 'value', valueField = 'key', deleteItem = true, disabled } = config ?? {}; return options.map((option) => ({ ...(deleteItem ? {} : option), label: option[labelField], value: option[valueField], disabled: isFunction(disabled) ? disabled(option) : false, })); }; /** * 休眠(setTimeout的promise版) * @param ms 要休眠的时间,单位:毫秒 * @param fn callback,可空 * @return Promise */ function sleep(ms: number, fn?: () => void) { return new Promise((resolve) => setTimeout(() => { fn && fn(); resolve(); }, ms), ); } /** * 数字转大写 * @param value * @returns {*} */ function numToUpper(value) { if (value != '') { const unit = ['仟', '佰', '拾', '', '仟', '佰', '拾', '', '角', '分']; const toDx = (n) => { switch (n) { case '0': return '零'; case '1': return '壹'; case '2': return '贰'; case '3': return '叁'; case '4': return '肆'; case '5': return '伍'; case '6': return '陆'; case '7': return '柒'; case '8': return '捌'; case '9': return '玖'; } }; const lth = value.toString().length; value *= 100; value += ''; const length = value.length; if (lth <= 8) { let result = ''; for (let i = 0; i < length; i++) { if (i == 2) { result = '元' + result; } else if (i == 6) { result = '万' + result; } if (value.charAt(length - i - 1) == 0) { if (i != 0 && i != 1) { if (result.charAt(0) != '零' && result.charAt(0) != '元' && result.charAt(0) != '万') { result = '零' + result; } } continue; } result = toDx(value.charAt(length - i - 1)) + unit[unit.length - i - 1] + result; } result += result.charAt(result.length - 1) == '元' ? '整' : ''; return result; } else { return null; } } return null; } export { deepMerge, type EventShim, getPopupContainer, noop, numToUpper, openWindow, replaceVariables, resolveOptions, setObjToUrlParams, sleep, withInstall, };