/** * 将常用的Uniapp提供的函数,存放到这里,按需引用 */ import lang from './lang.js'; /** * 判断是否为「微信剪贴板隐私声明缺失」导致的复制失败 * * 微信自 2023-09-15 起把剪贴板接口纳入《小程序用户隐私保护指引》, * 若小程序后台未声明「剪贴板」(或声明未审核通过),调用会返回: * { errno: 112, errMsg: 'setClipboardData:fail api scope is not declared in the privacy agreement' } * 这是平台侧配置问题,不改代码也能复现,因此不能把原始英文 errMsg 直接展示给用户。 * * @param {*} err uni.setClipboardData 的 fail 回调参数 * @returns {Boolean} true=剪贴板隐私声明缺失 */ export const isClipboardPrivacyError = (err) => { if (!err) return false; if (Number(err.errno) === 112) return true; const msg = `${err.errMsg || err.message || ''}`; return /privacy agreement/i.test(msg) || /api scope is not declared/i.test(msg); }; /** * 显示错误信息 * @param {Exception} err * @param {Boolean} useModal */ const showError = (err, useModal = false) => { const content = err?.message || err?.errMsg || err?.toString(); if (!useModal) { uni.showToast({ title: content, icon: 'none', duration: 3000 }); } else { uni.showModal({ title: lang.lang('uniappUtils.errorTip'), content, showCancel: false, }) } } /** * 打电话 * @param {string} mobile 电话号码 */ export const makePhoneCall = (mobile) => { try { uni.makePhoneCall({ phoneNumber: `${mobile}`, success(e) { } }); } catch (err) { showError(err); } } /** * 拷贝文本(返回 Promise) * @param {*} text * @param {*} options * @returns {Promise} 返回 Promise,成功时 resolve,失败时 reject */ export const copyTextAsync = (text, { copySuccess = '', copyFailed = '' } = {}) => { return new Promise((resolve, reject) => { // 输入验证 if (!text && text !== '') { const error = new Error(lang.lang('uniappUtils.copyTextEmpty')); showError(error); reject(error); return; } // 超时监测 const timeoutId = setTimeout(() => { let error = new Error(lang.lang('uniappUtils.copyTimeout')); // #ifdef MP-WEIXIN error = new Error([ lang.lang('uniappUtils.copyTimeoutTitle'), lang.lang('uniappUtils.copyTimeoutReason'), lang.lang('uniappUtils.wechatClipboardReason1'), lang.lang('uniappUtils.wechatClipboardReason2') ].join('\r\n')); // #endif showError(error, true); reject(error); }, 5000); try { uni.setClipboardData({ data: `${text}`, success: (res) => { clearTimeout(timeoutId); try { if (copySuccess) { uni.showToast({ title: copySuccess, icon: 'success', duration: 2000 }); } } catch (e) { showError(e); } resolve(res); }, fail: (err) => { clearTimeout(timeoutId); try { // 剪贴板隐私声明缺失(errno 112)属平台配置问题, 用可读文案替代微信原始英文错误 const tip = isClipboardPrivacyError(err) ? lang.lang('uniappUtils.clipboardPrivacyNotDeclared') : (err.message || err.errMsg || copyFailed || lang.lang('uniappUtils.copyFailed')); uni.showToast({ title: tip, icon: 'none', duration: 2000 }); } catch (e) { showError(e); } reject(err); } }); } catch (err) { clearTimeout(timeoutId); showError(err); reject(err); } }); } /** * 拷贝文本(回调形式,兼容旧代码) * @param {*} text * @param {*} options * @param {Function} callback 回调函数,接收 (success, error) 参数 */ export const copyText = (text, options = {}, callback) => { copyTextAsync(text, options) .then(res => { if (callback) callback(true, null); }) .catch(err => { if (callback) callback(false, err); }); } /** * 打开定位 * @param {Object} options */ export const openLocation = ({ latitude, longitude, name } = {}) => { try { uni.openLocation({ latitude, longitude, name, }); } catch (err) { showError(err); } }