From 872d3febe98c1a7ee9299d0db0bb44d46ad96303 Mon Sep 17 00:00:00 2001 From: xiebing Date: Wed, 24 Dec 2025 11:46:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/globalFunction.js | 1410 +++++++++++++++++++------------------- 1 file changed, 706 insertions(+), 704 deletions(-) diff --git a/common/globalFunction.js b/common/globalFunction.js index 9febf80..caca696 100644 --- a/common/globalFunction.js +++ b/common/globalFunction.js @@ -1,705 +1,707 @@ -import '@/lib/encryption/sm4.min.js' -import useUserStore from "../stores/useUserStore"; -import { - createRequestWithCache, - createRequest, - uploadFile -} from "../utils/request"; -import streamRequest, { - chatRequest -} from "../utils/streamRequest.js"; - -export const CloneDeep = (props) => { - if (typeof props !== 'object' || props === null) { - return props - } - - let result - if (props) { - result = [] - } else { - result = {} - } - - for (let key in props) { - if (props.hasOwnProperty(key)) { - result[key] = CloneDeep(props[key]) - } - } - - return result -} - - -export const msg = (title, duration = 1500, mask = false, icon = 'none', image) => { - if (Boolean(title) === false) { - return; - } - uni.showToast({ - title, - duration, - mask, - icon, - image - }); -} - -const prePage = () => { - let pages = getCurrentPages(); - let prePage = pages[pages.length - 2]; - return prePage.$vm; -} - - -export function safeReLaunch(url) { - const pages = getCurrentPages(); - const currentPage = pages[pages.length - 1]; - - // 移除传入url开头的斜杠用于对比 - const cleanUrl = url.startsWith('/') ? url.slice(1) : url; - - if (currentPage && currentPage.route === cleanUrl) { - console.log('已在当前页'); - return; - } - - uni.reLaunch({ - url - }); -} - - - -/** - * 页面跳转封装,支持 query 参数传递和返回回调 - * @param {string} url - 跳转路径 - * @param {object} options - * @param {boolean} options.needLogin - 是否需要登录 - * @param {object} options.query - 携带参数 - * @param {function} options.onBack - 页面返回时的回调(目标页调用 uni.navigateBack 时传递数据) - */ -let isJumping = false -export const navTo = function(url, { - needLogin = false, - query = {}, - onBack = null -} = {}) { - const userStore = useUserStore(); - if (isJumping) return - isJumping = true - if (needLogin && !userStore.hasLogin) { - setTimeout(() => { - uni.navigateTo({ - url: '/pages/login/login' - }); - isJumping = false - }, 170); - return; - } - - const queryStr = Object.entries(query) - .map(([key, val]) => `${key}=${encodeURIComponent(val)}`) - .join('&'); - const finalUrl = queryStr ? `${url}?${queryStr}` : url; - - if (onBack) { - const pages = getCurrentPages(); - const currentPage = pages[pages.length - 1]; - currentPage.__onBackCallback__ = onBack; - } - - setTimeout(() => { - uni.navigateTo({ - url: finalUrl - }); - isJumping = false - }, 170); -}; - -export const navBack = function({ - delta = 1, - data = null, - fallbackUrl = '/pages/index/index' -} = {}) { - const pages = getCurrentPages(); - - if (pages.length > 1) { - const prevPage = pages[pages.length - 1 - delta]; - - // 如果上一页存在回调函数,调用 - if (data && prevPage?.__onBackCallback__) { - prevPage.__onBackCallback__(data); - } - - uni.navigateBack({ - delta - }); - } else { - // 没有可返回的页面,直接跳转 fallback 页面 - uni.reLaunch({ - url: fallbackUrl - }); - } -}; -// // 默认返回上一页 -// navBack(); - -// // 返回上两层 -// navBack(2); - -// // 没有历史页面时跳转首页 -// navBack(1, '/pages/home/home'); - -function getdeviceInfo() { - const globalData = { - statusBarHeight: 0, // 状态导航栏高度 - topHeight: 0, // 距离顶部高度 - navHeight: 0, // 总体高度 - windowHeight: 0, // 可使用窗口高度 - tabBarHight: 0, //底部导航栏高度 - }; - let systemInfo = uni.getSystemInfoSync() - globalData.windowHeight = systemInfo.screenHeight - // 底部导航栏 - globalData.tabBarHight = systemInfo.screenHeight - systemInfo.safeArea.bottom - // 状态栏高度 - globalData.statusBarHeight = systemInfo.statusBarHeight - // #ifdef MP-MP-WEIXIN - let menuButtonInfo = uni.getMenuButtonBoundingClientRect() - // 胶囊距离顶部高度 - globalData.topHeight = menuButtonInfo.top - // 胶囊高度 - globalData.navHeight = menuButtonInfo.height​ - // #endif - return { - ...globalData - } -} - -function sleep(time) { - return new Promise((resolve) => setTimeout(resolve, time)) -} -const cloneDeep = (obj) => { - // 1.1 判断是否是对象 - const isObject = (obj) => (typeof obj === 'object' || typeof obj === 'function') && obj !== 'null' - - if (!isObject(obj)) { - throw new Error('参数不是对象') - } - // 1.3 如果参数为数组,则复制数组各元素,否则复制对象属性 - const newObject = Array.isArray(obj) ? [...obj] : { - ...obj - } - // 1.4 迭代 - Object.keys(newObject).forEach((key) => { - // 1.5 判断如果遍历到的属性值为对象,则继续递归cloneDeep - if (isObject(newObject[key])) { - newObject[key] = cloneDeep(newObject[key]) - } - }) - return newObject -} - -const CopyText = (text) => { - let input = document.createElement('textarea'); - input.value = text; - document.body.appendChild(input); - input.select(); - let flag = document.execCommand('copy') - if (flag) { - message.success('成功复制到剪贴板') - } else { - message.success('复制失败') - } - document.body.removeChild(input) -} - -// 柯里化 降低使用范围,提高适用性 -function Exp(regExp) { - return (str) => { - return regExp.test(str) - } -} - -const checkingPhoneRegExp = Exp(/^1[3-9]{1}\d{9}/) -// 手机号校验 checkingPhoneRegExp(phone) - -const checkingEmailRegExp = Exp(/^[a-z0-9_\.-]+@[a-z0-9_\.-]+[a-z0-9]{2,6}$/i) -// 邮箱校验 checkingEmailRegExp(email) - - -function throttle(fn, delay = 300) { - let valid = true - let savedArgs = null // 参数存储器 - let savedContext = null // 上下文存储器 - - return function(...args) { - // 保存当前参数和上下文 - savedArgs = args - savedContext = this - - if (!valid) return false - valid = false - setTimeout(() => { - fn.apply(savedContext, savedArgs) - valid = true - savedArgs = null // 清空存储 - savedContext = null - }, delay) - } -} - -function debounce(fun, delay) { - return function(args) { - let that = this - let _args = args - clearTimeout(fun.id) - fun.id = setTimeout(function() { - fun.call(that, _args) - }, delay) - } -} - - -function toRad(degree) { - return degree * Math.PI / 180; -} - -function haversine(lat1, lon1, lat2, lon2) { - const R = 6371; // 地球半径,单位为公里 - const a1 = toRad(lat1); - const a2 = toRad(lat2); - const b1 = toRad(lat2 - lat1); - const b2 = toRad(lon2 - lon1); - - const a = Math.sin(b1 / 2) * Math.sin(b1 / 2) + - Math.cos(a1) * Math.cos(a2) * Math.sin(b2 / 2) * Math.sin(b2 / 2); - const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - const distance = R * c; // 计算得到的距离,单位为公里 - - return distance; -} - -export function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) { - const R = 6371; // 地球平均半径,单位为公里 - const dLat = deg2rad(lat2 - lat1); - const dLon = deg2rad(lon2 - lon1); - const a = - Math.sin(dLat / 2) * Math.sin(dLat / 2) + - Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * - Math.sin(dLon / 2) * Math.sin(dLon / 2); - const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - const d = R * c; - return { - km: d, - m: d * 1000 - }; -} - -// 将角度转换为弧度 -function deg2rad(deg) { - return deg * (Math.PI / 180); -} - -function vacanciesTo(vacancies) { - if (vacancies === null) { - return '-人' - } else if (vacancies >= 0) { - return vacancies + "人" - } else { - return '不限人数' - } -} - -function salaryGlobal(type = 'min') { - const salay = [2, 5, 10, 15, 20, 25, 30, 50, 80]; - const salaymax = [2, 5, 10, 15, 20, 25, 30, 50, 80, 100]; - const salarys = salay.map((item, index) => ({ - label: item + 'k', - value: item * 1000, - children: CloneDeep(salaymax).splice(index).map((vItem) => ({ - label: vItem + 'k', - value: vItem * 1000, - })) - })) - - return salarys -} - -class CustomSystem { - constructor() { - const systemInfo = uni.getSystemInfoSync(); - this.systemInfo = systemInfo - } -} -const customSystem = new CustomSystem() - -function setCheckedNodes(nodes, ids) { - const isClear = ids.length === 0; - - nodes.forEach((firstLayer) => { - // 每次处理都先重置 - firstLayer.checkednumber = 0; - - const traverse = (node) => { - if (isClear) { - node.checked = false; - } else { - node.checked = ids.includes(node.id); - } - - if (node !== firstLayer && node.checked) { - firstLayer.checkednumber++; - } - - if (node.children && node.children.length) { - node.children.forEach(child => traverse(child)); - } - }; - - traverse(firstLayer); - }); - - return nodes; -} -const formatTotal = (total) => { - if (total < 10) return total.toString(); // 直接返回小于 10 的数 - - const magnitude = Math.pow(10, Math.floor(Math.log10(total))); // 计算数量级 - const roundedTotal = Math.floor(total / magnitude) * magnitude; // 去掉零头 - - return `${roundedTotal}+`; -}; - -export function formatDate(isoString) { - const date = new Date(isoString); - const year = date.getFullYear(); - const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需要 +1 - const day = String(date.getDate()).padStart(2, '0'); - const hours = String(date.getHours()).padStart(2, '0'); - const minutes = String(date.getMinutes()).padStart(2, '0'); - const seconds = String(date.getSeconds()).padStart(2, '0'); - - return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; -} - -export function insertSortData(data, attribute = 'createTime') { - const sortedData = data.sort((a, b) => new Date(b[attribute]) - new Date(a[attribute])); // 按时间降序排序 - const result = []; - let lastDate = ''; - let lastTitle = '' - - - const now = new Date(); - const todayStr = now.toISOString().split('T')[0]; // 获取今天的日期字符串 - const yesterday = new Date(now.setDate(now.getDate() - 1)).toISOString().split('T')[0]; // 获取昨天的日期字符串 - const twoDaysAgo = new Date(now.setDate(now.getDate() - 1)).toISOString().split('T')[0]; // 获取前天的日期字符串 - - sortedData.forEach(item => { - const itemAttribute = item[attribute].replace('T', ' ') - const itemDate = itemAttribute.split(' ')[0]; // 提取日期部分 - - let title = itemDate; - if (itemDate === todayStr) { - title = '今天'; - } else if (itemDate === yesterday) { - title = '昨天'; - } else if (itemDate === twoDaysAgo) { - title = '前天'; - } - - if (lastDate !== itemDate) { - result.push({ - title, - isTitle: true - }); - lastDate = itemDate; - lastTitle = title; - } - - result.push({ - ...item, - isTitle: false - }); - }); - - return [result, lastTitle]; -} - -function getWeeksOfMonth(year, month) { - const firstDay = new Date(year, month - 1, 1); // 当月第一天 - const lastDay = new Date(year, month, 0); // 当月最后一天 - const weeks = []; - let week = []; - - for (let d = new Date(firstDay); d <= lastDay; d.setDate(d.getDate() + 1)) { - // 补充第一周的上个月日期 - if (week.length === 0 && d.getDay() !== 1) { - let prevMonday = new Date(d); - prevMonday.setDate(d.getDate() - (d.getDay() === 0 ? 6 : d.getDay() - 1)); - while (prevMonday < d) { - week.push({ - year: prevMonday.getFullYear(), - month: prevMonday.getMonth() + 1, - day: prevMonday.getDate(), - fullDate: getLocalYYYYMMDD(prevMonday), // 修正 - weekday: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][prevMonday.getDay()], - isCurrent: false // 上个月日期 - }); - prevMonday.setDate(prevMonday.getDate() + 1); - } - } - - // 添加当前月份的日期 - week.push({ - year: d.getFullYear(), - month: d.getMonth() + 1, - day: d.getDate(), - fullDate: getLocalYYYYMMDD(d), // 修正 - weekday: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][d.getDay()], - isCurrent: true // 当前月的日期 - }); - - // 如果到了月末但当前周未满7天,需要补足到周日 - if (d.getTime() === lastDay.getTime() && week.length < 7) { - let nextDay = new Date(d); - nextDay.setDate(d.getDate() + 1); - while (week.length < 7) { - week.push({ - year: nextDay.getFullYear(), - month: nextDay.getMonth() + 1, - day: nextDay.getDate(), - fullDate: getLocalYYYYMMDD(nextDay), // 修正 - weekday: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][nextDay.getDay()], - isCurrent: false // 下个月日期 - }); - nextDay.setDate(nextDay.getDate() + 1); - } - } - - // 如果本周满了(7天)或者到了月末 - if (week.length === 7 || d.getTime() === lastDay.getTime()) { - weeks.push([...week]); // 存入当前周 - week = []; // 清空,准备下一周 - } - } - - return weeks; -} - -// 新增工具函数:将日期格式化为本地 YYYY-MM-DD 字符串 -function getLocalYYYYMMDD(date) { - const y = date.getFullYear(); - const m = String(date.getMonth() + 1).padStart(2, '0'); - const d = String(date.getDate()).padStart(2, '0'); - return `${y}-${m}-${d}`; -} - -function isFutureDate(dateStr) { - const inputDate = new Date(dateStr); - const today = new Date(); - - // 只比较年月日,不考虑具体时间 - today.setHours(0, 0, 0, 0); - inputDate.setHours(0, 0, 0, 0); - - return inputDate > today; -} - -function parseQueryParams(url = window.location.href) { - const queryString = url.split('?')[1]?.split('#')[0]; - const params = {}; - if (!queryString) return params; - - queryString.split('&').forEach(param => { - const [key, value] = param.split('='); - if (key) { - params[decodeURIComponent(key)] = decodeURIComponent(value || ''); - } - }); - - return params; -} - -function formatFileSize(bytes) { - if (bytes < 1024) return bytes + ' B' - else if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB' - else if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(2) + ' MB' - else return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB' -} - - -function sendingMiniProgramMessage(data = { - text: 'hello' -}, action = 'defalut') { - jWeixin.miniProgram.postMessage({ - data, - action - }); -} - -function copyText(text) { - uni.setClipboardData({ - data: text, - showToast: false, - success(res) { - msg('复制成功') - }, - }); -} - -function appendScriptTagElement(src) { - if (!src) return null; - return new Promise((resolve, reject) => { - const script = document.createElement('script'); - script.src = src; - script.onload = () => { - resolve() - }; - script.onerror = () => { - reject() - }; - document.body.appendChild(script); - }) -} - -function isInWechatMiniProgramWebview() { - const ua = navigator.userAgent.toLowerCase() - return ua.includes('miniprogram') || window.__wxjs_environment === 'miniprogram' -} - -function isEmptyObject(obj) { - return obj && typeof obj === 'object' && !Array.isArray(obj) && Object.keys(obj).length === 0; -} - -function aes_Decrypt(word, key) { - var key = CryptoJS.enc.Utf8.parse(key) //转为128bit - var srcs = CryptoJS.enc.Hex.parse(word) //转为16进制 - var str = CryptoJS.enc.Base64.stringify(srcs) //变为Base64编码的字符串 - var decrypt = CryptoJS.AES.decrypt(str, key, { - mode: CryptoJS.mode.ECB, - spadding: CryptoJS.pad.Pkcs7 - }) - return decrypt.toString(CryptoJS.enc.Utf8) -} -export function sm2_Decrypt(word, key) { - return SM.decrypt(word, key); -} - -export function sm2_Encrypt(word, key) { - return SM.encrypt(word, key); -} - -export function sm4Decrypt(key, value, mode = "hex") { - try { - if (key.length !== 32) { - alert('密钥必须是32位16进制字符串(128位)'); - return; - } - const decrypted = sm4.decrypt(value, key, { - mode: 'ecb', - cipherType: mode === 'hex' ? 'hex' : 'base64', - padding: 'pkcs#5' - }); - - return decrypted - - } catch (e) { - console.log('解密失败', e) - } -} - -export function sm4Encrypt(key, value, mode = "hex") { - try { - - if (key.length !== 32) { - alert('密钥必须是32位16进制字符串(128位)'); - return; - } - - const encrypted = sm4.encrypt(value, key, { - mode: 'ecb', - cipherType: mode === 'hex' ? 'hex' : 'base64', - padding: 'pkcs#5' - }); - - return encrypted - - } catch (e) { - console.log('加密失败') - } -} - -export function reloadBrowser() { - window.location.reload() -} - -// 一体机环境判断 -export function isY9MachineType() { - const ua = navigator.userAgent; - const isY9Machine = /Y9-ZYYH/i.test(ua); // 匹配机器型号 - return isY9Machine; -} - -// 一体机环境判断 -export function isAsdMachineType() { - // const ua = navigator.userAgent; - // const isY9Machine = /asd_hanweb/i.test(ua); // 匹配机器型号 - // return isY9Machine; - return !!lightAppJssdk.user -} - - -export const $api = { - msg, - prePage, - sleep, - createRequest, - streamRequest, - chatRequest, - insertSortData, - uploadFile, - formatFileSize, - sendingMiniProgramMessage, - copyText, - aes_Decrypt, - createRequestWithCache, - safeReLaunch -} - - - -export default { - $api, - navTo, - navBack, - cloneDeep, - formatDate, - getdeviceInfo, - checkingPhoneRegExp, - checkingEmailRegExp, - throttle, - debounce, - haversine, - getDistanceFromLatLonInKm, - vacanciesTo, - salaryGlobal, - customSystem, - setCheckedNodes, - formatTotal, - getWeeksOfMonth, - isFutureDate, - parseQueryParams, - appendScriptTagElement, - insertSortData, - isInWechatMiniProgramWebview, - isEmptyObject, - sm4Decrypt, - aes_Decrypt, - sm2_Decrypt, - sm2_Encrypt, - safeReLaunch, - reloadBrowser, - isAsdMachineType, - isY9MachineType +import '@/lib/encryption/sm4.min.js' +import useUserStore from "../stores/useUserStore"; +import { + createRequestWithCache, + createRequest, + uploadFile +} from "../utils/request"; +import streamRequest, { + chatRequest +} from "../utils/streamRequest.js"; + +export const CloneDeep = (props) => { + if (typeof props !== 'object' || props === null) { + return props + } + + let result + if (props) { + result = [] + } else { + result = {} + } + + for (let key in props) { + if (props.hasOwnProperty(key)) { + result[key] = CloneDeep(props[key]) + } + } + + return result +} + + +export const msg = (title, duration = 1500, mask = false, icon = 'none', image) => { + if (Boolean(title) === false) { + return; + } + uni.showToast({ + title, + duration, + mask, + icon, + image + }); +} + +const prePage = () => { + let pages = getCurrentPages(); + let prePage = pages[pages.length - 2]; + return prePage.$vm; +} + + +export function safeReLaunch(url) { + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + + // 移除传入url开头的斜杠用于对比 + const cleanUrl = url.startsWith('/') ? url.slice(1) : url; + + if (currentPage && currentPage.route === cleanUrl) { + console.log('已在当前页'); + return; + } + + uni.reLaunch({ + url + }); +} + + + +/** + * 页面跳转封装,支持 query 参数传递和返回回调 + * @param {string} url - 跳转路径 + * @param {object} options + * @param {boolean} options.needLogin - 是否需要登录 + * @param {object} options.query - 携带参数 + * @param {function} options.onBack - 页面返回时的回调(目标页调用 uni.navigateBack 时传递数据) + */ +let isJumping = false +export const navTo = function(url, { + needLogin = false, + query = {}, + onBack = null +} = {}) { + const userStore = useUserStore(); + if (isJumping) return + isJumping = true + if (needLogin && !userStore.hasLogin) { + setTimeout(() => { + uni.navigateTo({ + url: '/pages/login/login' + }); + isJumping = false + }, 170); + return; + } + + const queryStr = Object.entries(query) + .map(([key, val]) => `${key}=${encodeURIComponent(val)}`) + .join('&'); + const finalUrl = queryStr ? `${url}?${queryStr}` : url; + + if (onBack) { + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + currentPage.__onBackCallback__ = onBack; + } + + setTimeout(() => { + uni.navigateTo({ + url: finalUrl + }); + isJumping = false + }, 170); +}; + +export const navBack = function({ + delta = 1, + data = null, + fallbackUrl = '/pages/index/index' +} = {}) { + const pages = getCurrentPages(); + + if (pages.length > 1) { + const prevPage = pages[pages.length - 1 - delta]; + + // 如果上一页存在回调函数,调用 + if (data && prevPage?.__onBackCallback__) { + prevPage.__onBackCallback__(data); + } + + uni.navigateBack({ + delta + }); + } else { + // 没有可返回的页面,直接跳转 fallback 页面 + uni.reLaunch({ + url: fallbackUrl + }); + } +}; +// // 默认返回上一页 +// navBack(); + +// // 返回上两层 +// navBack(2); + +// // 没有历史页面时跳转首页 +// navBack(1, '/pages/home/home'); + +function getdeviceInfo() { + const globalData = { + statusBarHeight: 0, // 状态导航栏高度 + topHeight: 0, // 距离顶部高度 + navHeight: 0, // 总体高度 + windowHeight: 0, // 可使用窗口高度 + tabBarHight: 0, //底部导航栏高度 + }; + let systemInfo = uni.getSystemInfoSync() + globalData.windowHeight = systemInfo.screenHeight + // 底部导航栏 + globalData.tabBarHight = systemInfo.screenHeight - systemInfo.safeArea.bottom + // 状态栏高度 + globalData.statusBarHeight = systemInfo.statusBarHeight + // #ifdef MP-MP-WEIXIN + let menuButtonInfo = uni.getMenuButtonBoundingClientRect() + // 胶囊距离顶部高度 + globalData.topHeight = menuButtonInfo.top + // 胶囊高度 + globalData.navHeight = menuButtonInfo.height​ + // #endif + return { + ...globalData + } +} + +function sleep(time) { + return new Promise((resolve) => setTimeout(resolve, time)) +} +const cloneDeep = (obj) => { + // 1.1 判断是否是对象 + const isObject = (obj) => (typeof obj === 'object' || typeof obj === 'function') && obj !== 'null' + + if (!isObject(obj)) { + throw new Error('参数不是对象') + } + // 1.3 如果参数为数组,则复制数组各元素,否则复制对象属性 + const newObject = Array.isArray(obj) ? [...obj] : { + ...obj + } + // 1.4 迭代 + Object.keys(newObject).forEach((key) => { + // 1.5 判断如果遍历到的属性值为对象,则继续递归cloneDeep + if (isObject(newObject[key])) { + newObject[key] = cloneDeep(newObject[key]) + } + }) + return newObject +} + +const CopyText = (text) => { + let input = document.createElement('textarea'); + input.value = text; + document.body.appendChild(input); + input.select(); + let flag = document.execCommand('copy') + if (flag) { + message.success('成功复制到剪贴板') + } else { + message.success('复制失败') + } + document.body.removeChild(input) +} + +// 柯里化 降低使用范围,提高适用性 +function Exp(regExp) { + return (str) => { + return regExp.test(str) + } +} + +const checkingPhoneRegExp = Exp(/^1[3-9]{1}\d{9}/) +// 手机号校验 checkingPhoneRegExp(phone) + +const checkingEmailRegExp = Exp(/^[a-z0-9_\.-]+@[a-z0-9_\.-]+[a-z0-9]{2,6}$/i) +// 邮箱校验 checkingEmailRegExp(email) + + +function throttle(fn, delay = 300) { + let valid = true + let savedArgs = null // 参数存储器 + let savedContext = null // 上下文存储器 + + return function(...args) { + // 保存当前参数和上下文 + savedArgs = args + savedContext = this + + if (!valid) return false + valid = false + setTimeout(() => { + fn.apply(savedContext, savedArgs) + valid = true + savedArgs = null // 清空存储 + savedContext = null + }, delay) + } +} + +function debounce(fun, delay) { + return function(args) { + let that = this + let _args = args + clearTimeout(fun.id) + fun.id = setTimeout(function() { + fun.call(that, _args) + }, delay) + } +} + + +function toRad(degree) { + return degree * Math.PI / 180; +} + +function haversine(lat1, lon1, lat2, lon2) { + const R = 6371; // 地球半径,单位为公里 + const a1 = toRad(lat1); + const a2 = toRad(lat2); + const b1 = toRad(lat2 - lat1); + const b2 = toRad(lon2 - lon1); + + const a = Math.sin(b1 / 2) * Math.sin(b1 / 2) + + Math.cos(a1) * Math.cos(a2) * Math.sin(b2 / 2) * Math.sin(b2 / 2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + const distance = R * c; // 计算得到的距离,单位为公里 + + return distance; +} + +export function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) { + const R = 6371; // 地球平均半径,单位为公里 + const dLat = deg2rad(lat2 - lat1); + const dLon = deg2rad(lon2 - lon1); + const a = + Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * + Math.sin(dLon / 2) * Math.sin(dLon / 2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + const d = R * c; + return { + km: d, + m: d * 1000 + }; +} + +// 将角度转换为弧度 +function deg2rad(deg) { + return deg * (Math.PI / 180); +} + +function vacanciesTo(vacancies) { + if (vacancies === null) { + return '-人' + } else if (vacancies >= 0) { + return vacancies + "人" + } else { + return '不限人数' + } +} + +function salaryGlobal(type = 'min') { + const salay = [2, 5, 10, 15, 20, 25, 30, 50, 80]; + const salaymax = [2, 5, 10, 15, 20, 25, 30, 50, 80, 100]; + const salarys = salay.map((item, index) => ({ + label: item + 'k', + value: item * 1000, + children: CloneDeep(salaymax).splice(index).map((vItem) => ({ + label: vItem + 'k', + value: vItem * 1000, + })) + })) + + return salarys +} + +class CustomSystem { + constructor() { + const systemInfo = uni.getSystemInfoSync(); + this.systemInfo = systemInfo + } +} +const customSystem = new CustomSystem() + +function setCheckedNodes(nodes, ids) { + const isClear = ids.length === 0; + + nodes.forEach((firstLayer) => { + // 每次处理都先重置 + firstLayer.checkednumber = 0; + + const traverse = (node) => { + if (isClear) { + node.checked = false; + } else { + node.checked = ids.includes(node.id); + } + + if (node !== firstLayer && node.checked) { + firstLayer.checkednumber++; + } + + if (node.children && node.children.length) { + node.children.forEach(child => traverse(child)); + } + }; + + traverse(firstLayer); + }); + + return nodes; +} +const formatTotal = (total) => { + if (total < 10) return total.toString(); // 直接返回小于 10 的数 + + const magnitude = Math.pow(10, Math.floor(Math.log10(total))); // 计算数量级 + const roundedTotal = Math.floor(total / magnitude) * magnitude; // 去掉零头 + + return `${roundedTotal}+`; +}; + +export function formatDate(isoString) { + const date = new Date(isoString); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需要 +1 + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +} + +export function insertSortData(data, attribute = 'createTime') { + const sortedData = data.sort((a, b) => new Date(b[attribute]) - new Date(a[attribute])); // 按时间降序排序 + const result = []; + let lastDate = ''; + let lastTitle = '' + + + const now = new Date(); + const todayStr = now.toISOString().split('T')[0]; // 获取今天的日期字符串 + const yesterday = new Date(now.setDate(now.getDate() - 1)).toISOString().split('T')[0]; // 获取昨天的日期字符串 + const twoDaysAgo = new Date(now.setDate(now.getDate() - 1)).toISOString().split('T')[0]; // 获取前天的日期字符串 + + sortedData.forEach(item => { + const itemAttribute = item[attribute].replace('T', ' ') + const itemDate = itemAttribute.split(' ')[0]; // 提取日期部分 + + let title = itemDate; + if (itemDate === todayStr) { + title = '今天'; + } else if (itemDate === yesterday) { + title = '昨天'; + } else if (itemDate === twoDaysAgo) { + title = '前天'; + } + + if (lastDate !== itemDate) { + result.push({ + title, + isTitle: true + }); + lastDate = itemDate; + lastTitle = title; + } + + result.push({ + ...item, + isTitle: false + }); + }); + + return [result, lastTitle]; +} + +function getWeeksOfMonth(year, month) { + const firstDay = new Date(year, month - 1, 1); // 当月第一天 + const lastDay = new Date(year, month, 0); // 当月最后一天 + const weeks = []; + let week = []; + + for (let d = new Date(firstDay); d <= lastDay; d.setDate(d.getDate() + 1)) { + // 补充第一周的上个月日期 + if (week.length === 0 && d.getDay() !== 1) { + let prevMonday = new Date(d); + prevMonday.setDate(d.getDate() - (d.getDay() === 0 ? 6 : d.getDay() - 1)); + while (prevMonday < d) { + week.push({ + year: prevMonday.getFullYear(), + month: prevMonday.getMonth() + 1, + day: prevMonday.getDate(), + fullDate: getLocalYYYYMMDD(prevMonday), // 修正 + weekday: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][prevMonday.getDay()], + isCurrent: false // 上个月日期 + }); + prevMonday.setDate(prevMonday.getDate() + 1); + } + } + + // 添加当前月份的日期 + week.push({ + year: d.getFullYear(), + month: d.getMonth() + 1, + day: d.getDate(), + fullDate: getLocalYYYYMMDD(d), // 修正 + weekday: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][d.getDay()], + isCurrent: true // 当前月的日期 + }); + + // 如果到了月末但当前周未满7天,需要补足到周日 + if (d.getTime() === lastDay.getTime() && week.length < 7) { + let nextDay = new Date(d); + nextDay.setDate(d.getDate() + 1); + while (week.length < 7) { + week.push({ + year: nextDay.getFullYear(), + month: nextDay.getMonth() + 1, + day: nextDay.getDate(), + fullDate: getLocalYYYYMMDD(nextDay), // 修正 + weekday: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][nextDay.getDay()], + isCurrent: false // 下个月日期 + }); + nextDay.setDate(nextDay.getDate() + 1); + } + } + + // 如果本周满了(7天)或者到了月末 + if (week.length === 7 || d.getTime() === lastDay.getTime()) { + weeks.push([...week]); // 存入当前周 + week = []; // 清空,准备下一周 + } + } + + return weeks; +} + +// 新增工具函数:将日期格式化为本地 YYYY-MM-DD 字符串 +function getLocalYYYYMMDD(date) { + const y = date.getFullYear(); + const m = String(date.getMonth() + 1).padStart(2, '0'); + const d = String(date.getDate()).padStart(2, '0'); + return `${y}-${m}-${d}`; +} + +function isFutureDate(dateStr) { + const inputDate = new Date(dateStr); + const today = new Date(); + + // 只比较年月日,不考虑具体时间 + today.setHours(0, 0, 0, 0); + inputDate.setHours(0, 0, 0, 0); + + return inputDate > today; +} + +function parseQueryParams(url = window.location.href) { + const queryString = url.split('?')[1]?.split('#')[0]; + const params = {}; + if (!queryString) return params; + + queryString.split('&').forEach(param => { + const [key, value] = param.split('='); + if (key) { + params[decodeURIComponent(key)] = decodeURIComponent(value || ''); + } + }); + + return params; +} + +function formatFileSize(bytes) { + if (bytes < 1024) return bytes + ' B' + else if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB' + else if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(2) + ' MB' + else return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB' +} + + +function sendingMiniProgramMessage(data = { + text: 'hello' +}, action = 'defalut') { + jWeixin.miniProgram.postMessage({ + data, + action + }); +} + +function copyText(text) { + uni.setClipboardData({ + data: text, + showToast: false, + success(res) { + msg('复制成功') + }, + }); +} + +function appendScriptTagElement(src) { + if (!src) return null; + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = src; + script.onload = () => { + resolve() + }; + script.onerror = () => { + reject() + }; + document.body.appendChild(script); + }) +} + +function isInWechatMiniProgramWebview() { + const ua = navigator.userAgent.toLowerCase() + return ua.includes('miniprogram') || window.__wxjs_environment === 'miniprogram' +} + +function isEmptyObject(obj) { + return obj && typeof obj === 'object' && !Array.isArray(obj) && Object.keys(obj).length === 0; +} + +function aes_Decrypt(word, key) { + var key = CryptoJS.enc.Utf8.parse(key) //转为128bit + var srcs = CryptoJS.enc.Hex.parse(word) //转为16进制 + var str = CryptoJS.enc.Base64.stringify(srcs) //变为Base64编码的字符串 + var decrypt = CryptoJS.AES.decrypt(str, key, { + mode: CryptoJS.mode.ECB, + spadding: CryptoJS.pad.Pkcs7 + }) + return decrypt.toString(CryptoJS.enc.Utf8) +} +export function sm2_Decrypt(word, key) { + return SM.decrypt(word, key); +} + +export function sm2_Encrypt(word, key) { + return SM.encrypt(word, key); +} + +export function sm4Decrypt(key, value, mode = "hex") { + try { + if (key.length !== 32) { + alert('密钥必须是32位16进制字符串(128位)'); + return; + } + const decrypted = sm4.decrypt(value, key, { + mode: 'ecb', + cipherType: mode === 'hex' ? 'hex' : 'base64', + padding: 'pkcs#5' + }); + + return decrypted + + } catch (e) { + console.log('解密失败', e) + } +} + +export function sm4Encrypt(key, value, mode = "hex") { + try { + + if (key.length !== 32) { + alert('密钥必须是32位16进制字符串(128位)'); + return; + } + + const encrypted = sm4.encrypt(value, key, { + mode: 'ecb', + cipherType: mode === 'hex' ? 'hex' : 'base64', + padding: 'pkcs#5' + }); + + return encrypted + + } catch (e) { + console.log('加密失败') + } +} + +export function reloadBrowser() { + window.location.reload() +} + +// 一体机环境判断 +export function isY9MachineType() { + + try { + const ua = navigator.userAgent; + const isY9Machine = /Y9-ZYYH/i.test(ua); // 匹配机器型号 + return isY9Machine; +} + +// 一体机环境判断 +export function isAsdMachineType() { + // const ua = navigator.userAgent; + // const isY9Machine = /asd_hanweb/i.test(ua); // 匹配机器型号 + // return isY9Machine; + return !!lightAppJssdk.user +} + + +export const $api = { + msg, + prePage, + sleep, + createRequest, + streamRequest, + chatRequest, + insertSortData, + uploadFile, + formatFileSize, + sendingMiniProgramMessage, + copyText, + aes_Decrypt, + createRequestWithCache, + safeReLaunch +} + + + +export default { + $api, + navTo, + navBack, + cloneDeep, + formatDate, + getdeviceInfo, + checkingPhoneRegExp, + checkingEmailRegExp, + throttle, + debounce, + haversine, + getDistanceFromLatLonInKm, + vacanciesTo, + salaryGlobal, + customSystem, + setCheckedNodes, + formatTotal, + getWeeksOfMonth, + isFutureDate, + parseQueryParams, + appendScriptTagElement, + insertSortData, + isInWechatMiniProgramWebview, + isEmptyObject, + sm4Decrypt, + aes_Decrypt, + sm2_Decrypt, + sm2_Encrypt, + safeReLaunch, + reloadBrowser, + isAsdMachineType, + isY9MachineType } \ No newline at end of file