UNPKG

@cataract6545/tmui

Version:

tm-vuetify是一个新势力由主题驱动的UI组件库,相比其它优势大,组件全,设计趋势紧跟未来。具有主题生成,主题实时切换,暗黑实时切换,lottie动画,图表等新颖功能,tmui TMUI

901 lines (844 loc) 24.3 kB
import { ComponentInternalInstance } from 'vue' interface Data { [key: string]: any; } /** * 预览图片。 @param {Object} url 必填 当前预览的图片链接。 @param {Object} list 可以是url数组,也可以是对象,数据比如:["http:url"] or [{url:"https:url",...}] @param {Object} rangKey 如果list是对象数组,需要提供url字段。 */ import { preview } from "./preview.js" export default preview; /** * 检测是否是数字 * @param arg 待检测的字符 * @param defaultNum 0,如果不符合值时设置默认值 * @returns number类型数值 */ export function isNumber(arg: string | number | undefined | null, defaultNum = 0): number { const p = Number(arg); return p || defaultNum; } /** * 检测是否是字符串 * @param arg 待检测的字符 * @param defaultNum 默认"",如果不符合值是设置默认值 * @returns 字符串 */ export function isString(arg: string | number | undefined | null, defaultStr = ""): string { let p = ""; if (typeof arg === "string" && arg != null) { p = String(arg); } else p = defaultStr; return p; } /** * 把一个数字进行分页返回数字数组 * @param total 总数 * @param pageSize 分页大小 * @returns 数字数组 */ export function paginate(total: number, pageSize: number): number[] { const pages = Math.ceil(total / pageSize); const pageArr: number[] = []; for (let i = 0; i < pages; i++) { pageArr.push(i + 1); } return pageArr; } /** * 取对象数据值(可深层次取值) * @example getValue(data,"a.b.c") * @param data 对象数据 * @param keys 键值 * @returns 返回值 * @description 注意不会去改变原来的数据 */ export function getValue(data: Data, keys: string): any { const keyArr = keys.split("."); let result = { ...data }; for (const key of keyArr) { result = result[key]; if (result === undefined || result === null) { return result; } } return result; } /** * 设置对象键值(可深层次设置值) * @example setValue(data,"a.b.c","haha") * @param data 对象数据 * @param keys 键值 * @returns 修改后的对象数据。 * @description 改变原来的数据 */ export function setValue(data: Data, keys: string, value: any): void { const keyArr = keys.split("."); let obj = data; for (let i = 0; i < keyArr.length - 1; i++) { const key = keyArr[i]; if (!(key in obj)) { obj[key] = {}; } obj = obj[key]; } obj[keyArr[keyArr.length - 1]] = value; } /** * 计算并返回一个对象中最大的层级数 * @param data 待检测对象数据 * @returns 最大层级数 */ export function getMaxDepth(data: Data): number { let maxDepth = 0; function traverse(obj: any, depth: number): void { if (typeof obj !== "object" || obj === null) { maxDepth = Math.max(maxDepth, depth); return; } for (const key in obj) { // deno-lint-ignore no-prototype-builtins if (obj.hasOwnProperty(key)) { traverse(obj[key], depth + 1); } } } traverse(data, 0); return maxDepth; } /** * 深度合并对象 * @param FirstOBJ 需要合并的对象 * @param SecondOBJ 被合并的对象 * @returns 返回合并后的对象 */ export function deepObjectMerge(FirstOBJ : Data, SecondOBJ : Data) : Data { // 深度合并对象 for (var key in SecondOBJ) { FirstOBJ[key] = FirstOBJ[key] && FirstOBJ[key]?.toString() === "[object Object]" ? deepObjectMerge(FirstOBJ[key], SecondOBJ[key]) : FirstOBJ[key] = SecondOBJ[key]; } return FirstOBJ; } /** * 数据分组 * @param {Array} oArr - 原数组列表 * @param {Number} length - 单个数组长度 * @return {Array} arr - 分组后的新数组 */ export function splitData<T>(arr: Array<T> = [], size = 1): Array<T[]> { const result = []; for (let i = 0; i < arr.length; i += size) { result.push(arr.slice(i, i + size)); } return result as T[][]; } /** * 深度克隆 * @param {T} data 待大克隆复制的数据 * @return {T} any */ export function deepClone<T>(data: T): T { // 对常见的“非”值,直接返回原来值 if (data === null || typeof data !== "object") { return data; } if (Array.isArray(data)) { const clone: any[] = []; for (const item of data) { clone.push(deepClone(item)); } return clone as any; } if (data instanceof Date) { return new Date(data.getTime()) as unknown as T; } if (data instanceof RegExp) { const flags = data.flags; return new RegExp(data.source, flags) as unknown as T; } if (typeof data === "function") { return data as unknown as T; } const clone = {} as T; for (const key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { clone[key] = deepClone(data[key]); } } return clone; } /** * 剩余时间格式化 * @param {Number} t - 剩余多少秒 * @return {Object} format - 格式后的天时分秒对象 */ export function timeMuch(t: number) { let format: any = { d: '00', h: '00', m: '00', s: '00' }; if (t > 0) { let d = Math.floor(t / 86400); let h = Math.floor((t / 3600) % 24); let m = Math.floor((t / 60) % 60); let s = Math.floor(t % 60); format.d = d < 10 ? '0' + d : d; format.h = h < 10 ? '0' + h : h; format.m = m < 10 ? '0' + m : m; format.s = s < 10 ? '0' + s : s; } return format; } //获取时间距离当前时间 export function getDateToNewData(timestamp: number | string | Date = new Date().getTime()) { if (typeof timestamp == 'string') { timestamp = new Date(timestamp).getTime(); } // 补全为13位 var arrTimestamp: Array<string> = (timestamp + '').split(''); for (var start = 0; start < 13; start++) { if (!arrTimestamp[start]) { arrTimestamp[start] = '0'; } } timestamp = Number(arrTimestamp.join('')) * 1; var minute = 1000 * 60; var hour = minute * 60; var day = hour * 24; var halfamonth = day * 15; var month = day * 30; var now = new Date().getTime(); var diffValue = now - timestamp; // 如果本地时间反而小于变量时间 if (diffValue < 0) { return '不久前'; } // 计算差异时间的量级 var monthC = diffValue / month; var weekC = diffValue / (7 * day); var dayC = diffValue / day; var hourC = diffValue / hour; var minC = diffValue / minute; // 数值补0方法 var zero = function (value: number) { if (value < 10) { return '0' + value; } return value; }; // 使用 if (monthC > 12) { // 超过1年,直接显示年月日 return (function () { var date = new Date(timestamp); return date.getFullYear() + '年' + zero(date.getMonth() + 1) + '月' + zero(date.getDate()) + '日'; })(); } else if (monthC >= 1) { return parseInt(monthC + '') + "月前"; } else if (weekC >= 1) { return parseInt(weekC + '') + "周前"; } else if (dayC >= 1) { return parseInt(dayC + '') + "天前"; } else if (hourC >= 1) { return parseInt(hourC + '') + "小时前"; } else if (minC >= 1) { return parseInt(minC + '') + "分钟前"; } return '刚刚'; } /** * 打电话 * @param {String<Number>} phoneNumber - 数字字符串 * @return {Promise} */ export function callPhone(phoneNumber = '') { let num = phoneNumber.toString() return new Promise((rs, rj) => { uni.makePhoneCall({ phoneNumber: num, success: () => rs(true), fail: (err) => rj(err) }); }) } /** * 调起客户端相机扫码。 * @param {Boolean} onlyFromCamera true 是否只允许相机扫码识别 * @param {Array<string>} scanType ['barCode', 'qrCode', 'datamatrix','datamatrix'] * @returns Promise 成功返回相关数据结构 */ export function scanCode(onlyFromCamera = true, scanType = ['barCode', 'qrCode', 'datamatrix', 'datamatrix']): Promise<string | UniApp.ScanCodeSuccessRes> { return new Promise((rs, rj) => { // #ifdef H5 rj('不支持H5'); // #endif // #ifndef H5 uni.scanCode({ onlyFromCamera: onlyFromCamera, scanType: scanType, success: (res) => rs(res), fail: (error) => rj(error) }); // #endif }) } /** * 设置剪切板内容。 * @param {String} data * @returns Promise true/false */ export function setClipboardData(data: string): Promise<string | boolean> { return new Promise((rs, rj) => { // #ifndef H5 uni.setClipboardData({ data: data, success: () => rs(true), fail: (error) => rj(error) }); // #endif // #ifdef H5 if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(data).then(() => rs(true)).catch((error => rs(error))) } else { const textArea = document.createElement('textarea') textArea.style.opacity = "0" textArea.style.position = "fixed" textArea.style.top = "0px" textArea.value = data document.body.appendChild(textArea) textArea.focus() textArea.select() document.execCommand('copy') ? rs(true) : rj("错误") textArea.remove() } // #endif }) } /** * 获取剪切板内容 * @returns Promise 剪切板内容 */ export function getClipboardData(): Promise<boolean | string> { return new Promise((rs, rj) => { // #ifndef H5 uni.getClipboardData({ success: (res) => rs(res.data), fail: (error) => rj(error) }); // #endif // #ifdef H5 console.error('H5无法获取剪切板内容') rj('H5无法获取剪切板内容') // #endif }) } /** * 设置cookie数据 * @param {String} key 键值 * @param {String} data 值 * @returns Boolean */ export function setCookie(key: string, data: any) { try { uni.setStorageSync(key, data); return true; } catch (e) { return false; } } /** * 删除一个本地cookie * @param {String} key 键值 * @returns Boolean */ export function delCookie(key: string) { try { uni.removeStorageSync(key); return true; } catch (e) { return false; } } /** * 获取一个cookie数据 * 如果存入的是对象,返回的也是对象。如果是string返回的也是字符串。 * @param {String} key 键 * @returns json/string */ export function getCookie(key: string) { try { const value = uni.getStorageSync(key); try { let val = JSON.parse(value) return val; } catch (e) { return value; } } catch (e) { return undefined; } } /** * 向地址连接追加参数。 * @param {string} uri 网址 * @param {string} key 字段 * @param {string} value 字段值 * @returns */ export function httpUrlAddKey(uri: string, key: string, value: string) { if (!value) { return uri; } var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i"); var separator = uri.indexOf("?") !== -1 ? "&" : "?"; if (uri.match(re)) { return uri.replace(re, "$1" + key + "=" + value + "$2"); } else { return uri + separator + key + "=" + value; } } /** * 取url参数 * @param {string} uri 网址 * @param {string} key 字段 * @returns string */ export function getQueryString(url: string, key: string): string { var query_string = url.substring(url.indexOf("?")); if (!query_string) return ""; var re = /[?&]?([^=]+)=([^&]*)/g; var tokens: any; while (tokens = re.exec(query_string)) { if (decodeURIComponent(tokens[1]) === key) { return decodeURIComponent(tokens[2]); break; } } return ""; } /** * * @param rdix 随机因子 * @param length 取的长度 * @param isAddStr 是否限制随机结果中的长度,不允许输出长度 * @returns String */ export function getUid(rdix = 1, length = 12, isAddStr = false) { return Math.floor(Math.random() * rdix * Math.floor(Math.random() * Date.now())).toString(isAddStr ? 16 : 10).substring(0, length); } /* 防抖 防抖原理:在一定时间内,只有最后一次操作,再过wait毫秒后才执行函数 @param {Function} func 要执行的回调函数 @param {Number} wait 延迟的时间 @param{Boolean} immediate 是否要立即执行 */ var timeout: any = getUid(1) export function debounce(func: Function, wait = 500, immediate = false) { // 清除定时器 if (timeout !== null) clearTimeout(timeout); // 立即执行,此类情况一般用不到 if (immediate) { timeout = setTimeout(() => { timeout = null; }, wait); typeof func === "function" && func(); } else { // 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法 timeout = getUid(1); timeout = setTimeout(() => { typeof func === "function" && func(); }, wait); } } /** * 节流 节流原理:在一定时间内,只能触发一次 * @param {Function} func 要执行的回调函数 * @param {Number} wait 延时的时间 * @param {Boolean} immediate 是否立即执行 * @return null */ var timesr: any = NaN, throttleFlag: boolean; export function throttle(func: Function, wait = 500, immediate = true) { if (immediate) { if (!throttleFlag) { throttleFlag = true; // 如果是立即执行,则在wait毫秒内开始时执行 typeof func === 'function' && func(); timesr = setTimeout(() => { throttleFlag = false; }, wait); } } else { if (!throttleFlag) { throttleFlag = true // 如果是非立即执行,则在wait毫秒内的结束处执行 timesr = setTimeout(() => { throttleFlag = false typeof func === 'function' && func(); }, wait); } } }; /**等同:queryDom */ export function quereyDom(t: ComponentInternalInstance, node: string): Promise<UniApp.NodeInfo | UniApp.NodeInfo[]> { return new Promise((res, rej) => { // #ifdef APP-NVUE const dom: any = uni.requireNativePlugin('dom') setTimeout(function () { node = node.replace(/^[#\.]/g, '') dom.getComponentRect(t.refs[node], function (el: any) { res(el?.size); }) }, 60) // #endif // #ifndef APP-NVUE const query = uni.createSelectorQuery().in(t); query.select(node).boundingClientRect(el => { res(el); }).exec(); // #endif }) } /** * 查询文档节点信息 * @param t Vue上下文对象 * @param node 提供带#的id比如:'#id',在nvue中应该是元素上写明ref='id' * @returns vue页面返回查询的节点信息,nvue返回weex的节点信息。 */ export const queryDom = quereyDom /** * 是否是手机号码 * @param phone 号码 * @returns Boolean */ export function isPhone(phone: string | number) { let val = String(phone); let reg = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/ return !!val.match(reg); } /** * 是否含有中文 * @param s 字符串 * @returns Boolean */ export function isChina(s: string) { var patrn = /[\u4E00-\u9FA5]|[\uFE30-\uFFA0]/gi; return !!patrn.exec(s); } /** * 是否为空 * @description 判断是否是null,对象是否为空,数组是否为空。是否为 undefaind,是否为 “”空字符串。 * @param s 任意 */ export function isEmpty(s: any) { if (typeof s === 'string') { s = s.trim(); } if (s == "") return true if (s == null) return true; if (typeof s === 'undefined') return true; if (Array.isArray(s)) { if (s.length == 0) return true; } if (typeof s === 'object') { if (Object.keys(s).length == 0) return true; } return false; } /** * 是否邮箱 * @param s 字符串 * @returns Boolean */ export function isEmail(s: string) { let reg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/; return !!s.match(reg); } /** * 是否身份证号 * @param val 字符号或者数字 * @returns Boolean * @author https://cloud.tencent.com/developer/article/1114323 */ export function isIdCard(val: string | number) { val = String(val) var p = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/; var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; var parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2]; var code = val.substring(17); if (p.test(val)) { var sum: number = 0; for (var i = 0; i < 17; i++) { let id: number | string | any = val[i] sum += id * factor[i]; } if (parity[sum % 11] == code.toUpperCase()) { return true; } } return false; } /** * 是否车牌 * @description 蓝牌5位,绿牌6位。 * @param s 字符串 * @returns Boolean */ export function isIdCar(s: string) { let reg = /^[京|沪|津|渝|鲁|冀|晋|蒙|辽|吉|黑|苏|浙|皖|闽|赣|豫|湘|鄂|粤|桂|琼|川|贵|云|藏|陕|甘|青|宁|新|港|澳|台|新|使]{1}[A-Z]{1}[A-Z_0-9]{5,6}$/ return !!s.match(reg); } /** * 纯数字密码验证 * @param s 字符串或者数字 * @param len 最小长度,默认6 * @param maxLen 最大长度,默认20 * @returns Boolean */ export function isPasswordOfNumber(s: number | string, len = 6, maxLen = 20) { s = String(s); let reg = new RegExp(`^[0-9]{${len},${maxLen}}$`) return !!s.match(reg) } /** * 密码验证 * @param s 字符串或者数字 * @param len 最小长度,默认6 * @param maxLen 最大长度,默认20 * @param model 0数字和英文,1数字,英文必须包含,不允许有特殊字符,2数字和字母必须包含,可以有特殊字符。 * @returns Boolean */ export function isPasswordOfOther(s: string | number, len = 6, maxLen = 20, model = 0) { s = String(s); //密码至少包含 数字和英文,长度6-20 let reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/ //密码包含 数字,英文,字符中的两种以上,长度6-20 if (model === 1) { reg = /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)])+$).{6,20}$/ } //至少包含数字跟字母,可以有字符 if (model === 2) { reg = /(?=.*([a-zA-Z].*))(?=.*[0-9].*)[a-zA-Z0-9-*/+.~!@#$%^&*()]{6,20}$/ } return !!s.match(reg) } /** * 是否是一个有效的日期 * @param s 字符串,数字,日期对象 * @returns Boolean */ export function isDate(s: string | number | Date) { if (s == null || typeof s === 'undefined' || !s) return false; if (typeof s === 'string') { //兼容ios,mac s = s.replace('-', '/'); } let d = new Date(s); if (d.toString() == 'Invalid Date') return false; return true; } /** * 显示信息 * @param word 标题 * @param mask 不允许穿透 * @param icon 图标 */ export function toast(word: string, mask: boolean = true, icon: any = 'none') { // #ifndef MP-ALIPAY uni.showToast({ mask: mask, title: word, icon: icon }) // #endif // #ifdef MP-ALIPAY uni.showToast({ title: word, icon: icon }) // #endif } /** * 获取屏幕窗口安全高度和宽度 * 注意是针对种屏幕的统一计算,统一高度,不再让uni获取有效高度而烦恼。 * 请一定要在onMounted或者onLoad中调用,否则不准确在h5端。 * @return {height,width,top,isCustomHeader,statusBarHeight,sysinfo} */ export function getWindow(): { width: number, height: number, top: number, bottom: number, statusBarHeight: number, isCustomHeader: Boolean, sysinfo: UniApp.GetSystemInfoResult } { // let getsysinfoSync = getCookie("tmui_sysinfo") // if(getsysinfoSync){ // return getsysinfoSync // } const sysinfo = uni.getSystemInfoSync(); let top = 0; let height = sysinfo.windowHeight; let nowPage = getCurrentPages().pop() let isCustomHeader = false; let pages = uni.$tm?.pages ?? [] let bottom = sysinfo.safeAreaInsets?.bottom ?? 0; if (uni.$tm?.globalNavStyle == 'custom') { isCustomHeader = true; } else { for (let i = 0; i < uni.$tm.pages.length; i++) { if (nowPage?.route == uni.$tm.pages[i].path && uni.$tm.pages[i].custom == 'custom') { isCustomHeader = true; break; } } } // #ifdef H5 // 兼容说明:h5端第一次获取的高度和第二次获取的高度是有差异 的。 if (isCustomHeader) { height = sysinfo.windowHeight + sysinfo.windowTop } else { top = 44 if (sysinfo.windowTop > 0) { height = sysinfo.windowHeight; } else { height = sysinfo.windowHeight + sysinfo.windowTop } } // #endif let results = { bottom: bottom, height: height, width: sysinfo.windowWidth, top: top, isCustomHeader: isCustomHeader, statusBarHeight: sysinfo.statusBarHeight || 0, sysinfo: sysinfo }; return results; } type openUrlType = "navigate" | "redirect" | "reLaunch" | "switchTab" | "navigateBack" /** * * @param url string 打开的页面路径 * @param type openUrlType "navigate"|"redirect"|"reLaunch"|"switchTab"|"navigateBack" */ export function routerTo(url: string, type: openUrlType = 'navigate') { type openUrlTypeFun = "navigateTo" | "redirectTo" | "reLaunch" | "switchTab" | "navigateBack" let funType = { navigate: "navigateTo", redirect: "redirectTo", switchTab: "switchTab", reLaunch: "reLaunch", navigateBack: "navigateBack", } let fun = funType[type]; if (fun == 'navigateBack') { uni.navigateBack({ fail(error) { console.error(error) } }) } else if (fun == 'reLaunch') { uni.reLaunch({ url: url, fail(error) { console.error(error) } }) } else if (fun == 'switchTab') { uni.switchTab({ url: url, fail(error) { console.error(error) } }) } else if (fun == 'redirectTo') { uni.redirectTo({ url: url, fail(error) { console.error(error) } }) } else if (fun == 'navigateTo') { uni.navigateTo({ url: url, fail(error) { console.error(error) } }) } } /** * 将rpx转换为px * @param v 待转换的数字 * @param screenWidth 屏幕的宽度,如果不提供默认自动获取 * @return number */ export function torpx(v: number, screenWidth: number = 0) { if (typeof screenWidth === 'undefined' || !screenWidth) { screenWidth = uni.getSystemInfoSync().screenWidth; } let pixelRatio = 750 / screenWidth; return Math.ceil(v * pixelRatio) } /** * 将rpx转换为px * @param v 待转换的数字 * @return number */ export function topx(v: number) { return Math.ceil(uni.upx2px(Number(v))) } var lastTime = 0; /** * 在下一次前执行回调函数 * @param callback 回调函数 * @returns 一个id值,取消时cancelAnimationFrame(id)来取消 */ export function requestAnimationFrame(callback: Function): number { const currentTime = new Date().getTime(); const timeToCall = Math.max(0, 16 - (currentTime - lastTime)); const id = <any>setTimeout(() => { callback(currentTime + timeToCall); }, timeToCall); lastTime = currentTime + timeToCall; return id; } /** * 取消回调执行 * @param id requestAnimationFrame产生的id */ export function cancelAnimationFrame(id: number): void { clearTimeout(id) } /** * 给定一个值,来填充边距所需要的数组值 * @param val any * @returns [左,上,右,下] */ export function valToMarginAr(val:any):number[]{ let ar:number[] = []; if(typeof val==='string'&&val){ ar = [Number(val)] }else if(typeof val=== 'number'&&isNaN(Number(val))){ ar = [val] }else if(typeof val ==='undefined'||typeof val === null || val==="" || val === undefined){ val = [0] }else if(Array.isArray(val)){ ar = val.map(el=>Number(el)) }; if(ar.length==1){ ar = new Array(4).fill(ar[0]) }else if(ar.length==2){ ar = [...ar,...ar] }else if(ar.length==3){ ar = [...ar,0] } return ar; } /** * 给定一个值,来填充边距所需要的数组值 * @param val any * @returns [左,上,右,下] */ export function valToRoundStrClass(val:number|number[]){ let dstr = "" if(typeof val == 'number') return 'round-'+val if (val.length == 1) return 'round-'+val if (val.length == 2) return `round-tl-${val[0]} round-tr-${val[1]}` if (val.length == 3) return `round-tl-${val[0]} round-tr-${val[1]} round-br-${val[2]} ` if (val.length == 4) return `round-tl-${val[0]} round-tr-${val[1]} round-br-${val[2]} round-bl-${val[2]}` return dstr }