utils.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. const time = function () {
  2. return parseInt(Math.round(new Date() / 1000));
  3. };
  4. /***
  5. * 时间戳转换时间字符串
  6. * @param format eg: Y-m-d h:i:s
  7. * @param timestamp
  8. * @returns {*}
  9. */
  10. const datetime = function (format, timestamp) {
  11. if (typeof format === 'undefined' || format === null) {
  12. format = 'Y-m-d h:i:s';
  13. }
  14. if (typeof timestamp === 'undefined' || timestamp === null) {
  15. timestamp = this.time();
  16. }
  17. const d = new Date();
  18. d.setTime(timestamp * 1000);
  19. // from https://www.cnblogs.com/yjf512/p/3796229.html
  20. const date = {
  21. "Y": d.getFullYear(),
  22. "m+": (d.getMonth() + 1) < 10 ? `0${d.getMonth() + 1}` : (d.getMonth() + 1),
  23. "d+": d.getDate() < 10 ? `0${d.getDate()}` : d.getDate(),
  24. "h+": d.getHours() < 10 ? `0${d.getHours()}` : d.getHours(),
  25. "i+": d.getMinutes() < 10 ? `0${d.getMinutes()}` : d.getMinutes(),
  26. "s+": d.getSeconds() < 10 ? `0${d.getSeconds()}` : d.getSeconds(),
  27. "q+": Math.floor((d.getMonth() + 3) / 3),
  28. "S+": d.getMilliseconds(),
  29. };
  30. for (let k in date) {
  31. if (new RegExp("(" + k + ")").test(format)) {
  32. format = format.replace(RegExp.$1, RegExp.$1.length === 1 ?
  33. date[k] : ("00" + date[k]).substr(("" + date[k]).length));
  34. }
  35. }
  36. return format;
  37. };
  38. const strtotime = function (str) {
  39. };
  40. const objectToUrlParams = function (obj, urlencode) {
  41. let str = "";
  42. for (let key in obj) {
  43. str += "&" + key + "=" + (urlencode ? encodeURIComponent(obj[key]) : obj[key]);
  44. }
  45. return str.substr(1);
  46. };
  47. const timeDifference = function (start_at, end_at) {
  48. let times = parseInt((end_at - start_at) / 1000);
  49. let day = 0,
  50. hour = 0,
  51. minute = 0,
  52. second = 0;
  53. if (times > 0) {
  54. day = Math.floor(times / (60 * 60 * 24));
  55. hour = Math.floor(times / (60 * 60)) - (day * 24);
  56. minute = Math.floor(times / 60) - (day * 24 * 60) - (hour * 60);
  57. second = Math.floor(times) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60);
  58. } else {
  59. return null;
  60. }
  61. return {
  62. d: day,
  63. h: hour < 10 ? ('0' + hour) : hour,
  64. m: minute < 10 ? ('0' + minute) : minute,
  65. s: second < 10 ? ('0' + second) : second,
  66. };
  67. };
  68. /**
  69. * 使用经纬度计算地球表面距离
  70. */
  71. const earthDistance = function (location1, location2) {
  72. const lat1 = parseFloat(location1.lat);
  73. const lng1 = parseFloat(location1.lng);
  74. const lat2 = parseFloat(location2.lat);
  75. const lng2 = parseFloat(location2.lng);
  76. const EARTH_RADIUS = 6378137.0; //单位M
  77. const PI = Math.PI;
  78. function getRad(d)
  79. {
  80. return d * PI / 180.0;
  81. }
  82. let f = getRad((lat1 + lat2) / 2);
  83. let g = getRad((lat1 - lat2) / 2);
  84. let l = getRad((lng1 - lng2) / 2);
  85. let sg = Math.sin(g);
  86. let sl = Math.sin(l);
  87. let sf = Math.sin(f);
  88. let s, c, w, r, d, h1, h2;
  89. let a = EARTH_RADIUS;
  90. let fl = 1 / 298.257;
  91. sg = sg * sg;
  92. sl = sl * sl;
  93. sf = sf * sf;
  94. s = sg * (1 - sl) + (1 - sf) * sl;
  95. c = (1 - sg) * (1 - sl) + sf * sl;
  96. w = Math.atan(Math.sqrt(s / c));
  97. r = Math.sqrt(s * c) / w;
  98. d = 2 * w * a;
  99. h1 = (3 * r - 1) / 2 / c;
  100. h2 = (3 * r + 1) / 2 / s;
  101. return d * (1 + fl * (h1 * sf * (1 - sg) - h2 * (1 - sf) * sg));
  102. };
  103. const objectValues = function (obj) {
  104. let arr = [];
  105. for (let i in obj) {
  106. arr.push(obj[i]);
  107. }
  108. return arr;
  109. };
  110. const randomString = function (length) {
  111. const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  112. let result = '';
  113. for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
  114. return result;
  115. };
  116. const urlParamsToObject = function (str) {
  117. let groups = str.split('&');
  118. let obj = {};
  119. for (let i in groups) {
  120. if (typeof groups[i] !== 'string') continue;
  121. if (!groups[i].length) continue;
  122. let kvs = groups[i].split('=');
  123. if (kvs.length !== 2) continue;
  124. obj[kvs[0]] = kvs[1];
  125. }
  126. return obj;
  127. };
  128. const showLoading = function () {
  129. uni.showLoading({
  130. title: '加载中',
  131. // #ifdef MP-WEIXIN || MP-BAIDU
  132. mask: true,
  133. // #endif
  134. })
  135. };
  136. const hideLoading = function () {
  137. uni.hideLoading();
  138. };
  139. const h5Address = function ({success, fail}) {
  140. // #ifdef H5
  141. async function geolocation() {
  142. let options = {
  143. enableHighAccuracy: true, //高精度 速度慢
  144. timeout: 5000, //5s
  145. maximumAge: 10000, //返回多长时间(即最长年龄,单位毫秒)内的可获取的缓存位置
  146. };
  147. function gsuccess(pos) {
  148. let crd = pos.coords;
  149. success({
  150. latitude: crd.latitude,
  151. longitude: crd.longitude,
  152. });
  153. }
  154. function gerror(err) {
  155. console.warn('ERROR(' + err.code + '): ' + err.message);
  156. fail({
  157. code: err.code,
  158. errMsg: err.message,
  159. })
  160. }
  161. await navigator.geolocation.getCurrentPosition(gsuccess, gerror, options);
  162. }
  163. geolocation();
  164. // #endif
  165. }
  166. //保存
  167. let batchStatus = false;
  168. /**
  169. *
  170. * too long
  171. * @param url
  172. * @param desc ['image', 'video']
  173. * @returns {Promise<unknown>}
  174. */
  175. // #ifdef H5
  176. const isWechat = function () {
  177. let mic = window.navigator.userAgent.toLowerCase().match(/micromessenger/i);
  178. return mic && mic[0] === 'micromessenger';
  179. };
  180. // #endif
  181. const batchSave = function (url, desc = 'image') {
  182. return new Promise((resolve_a, reject_a) => {
  183. if (!(url instanceof Array)) {
  184. url = [url];
  185. }
  186. const title = desc === "image" ? '图片' : '视频';
  187. // #ifdef H5
  188. if (isWechat()) {
  189. uni.previewImage({
  190. urls: url,
  191. current: 0
  192. });
  193. return
  194. }
  195. // #endif
  196. new Promise((resolve_b, reject_b) => {
  197. // #ifdef MP-ALIPAY || H5
  198. resolve_b('success');
  199. // #endif
  200. // #ifndef MP-ALIPAY
  201. let scope = null;
  202. // #ifdef MP-WEIXIN || MP-BAIDU
  203. scope = 'scope.writePhotosAlbum';
  204. // #endif
  205. // #ifdef MP-TOUTIAO
  206. scope = 'scope.album';
  207. // #endif
  208. uni.authorize({
  209. scope: scope,
  210. success(res) {
  211. resolve_b('success');
  212. },
  213. fail(e) {
  214. uni.showModal({
  215. title: '提示',
  216. content: '您好,请先授权保存到相册权限',
  217. showCancel: false,
  218. success(res) {
  219. if (res.confirm) {
  220. uni.openSetting({
  221. success(settingdata) {
  222. if (settingdata.authSetting[scope]) {
  223. resolve_b('success');
  224. } else {
  225. reject_b('fail');
  226. }
  227. }
  228. })
  229. }
  230. }
  231. })
  232. }
  233. });
  234. // #endif
  235. }).then(function (result) {
  236. if (batchStatus) {
  237. uni.showLoading({title: title + `保存中`, mask: true});
  238. return;
  239. }
  240. batchStatus = true;
  241. uni.showLoading({title: title + `保存中`, mask: true});
  242. }).then(e => {
  243. // #ifdef MP-ALIPAY
  244. let sentinel = 0;
  245. let func = function (url, sentinel) {
  246. my.saveImage({
  247. url: url[sentinel],
  248. showActionSheet: false,
  249. success: (res) => {
  250. if (sentinel < url.length - 1) {
  251. sentinel++;
  252. func(url, sentinel);
  253. } else {
  254. batchStatus = false;
  255. uni.hideLoading();
  256. resolve_a('success');
  257. }
  258. },
  259. fail(info) {
  260. batchStatus = false;
  261. uni.hideLoading();
  262. /* 提示暂放 */
  263. uni.showToast({title: '下载失败'});
  264. reject_a('fail');
  265. }
  266. });
  267. }
  268. url[sentinel] === undefined || func(url, sentinel);
  269. // #endif
  270. // #ifndef MP-ALIPAY
  271. Promise.all(url.map(item => {
  272. return new Promise((resolve_c, reject_c) => {
  273. try {
  274. // #ifdef H5
  275. const body = document.getElementsByTagName('body')[0];
  276. const aEle = document.createElement('a');
  277. if(desc === "image") {
  278. aEle.setAttribute('download', Math.random().toString(36).substr(2) + '.jpg');
  279. } else {
  280. aEle.setAttribute('download', Math.random().toString(36).substr(2) + '.mp4');
  281. }
  282. aEle.style.display = 'none';
  283. aEle.href = item;
  284. aEle.target = '_blank';
  285. body.appendChild(aEle);
  286. aEle.dispatchEvent(new MouseEvent('click')); // 模拟鼠标click点击事件
  287. document.body.removeChild(aEle);
  288. resolve_c('success')
  289. // #endif
  290. // #ifndef MP-ALIPAY || H5
  291. let downloadTask = uni.downloadFile({
  292. url: item,
  293. success: function (e) {
  294. if (desc === 'image') {
  295. uni.saveImageToPhotosAlbum({
  296. filePath: e.tempFilePath,
  297. success: function () {
  298. resolve_c('success')
  299. },
  300. fail: function (err) {
  301. reject_c('fail');
  302. }
  303. });
  304. }
  305. if (desc === 'video') {
  306. uni.saveVideoToPhotosAlbum({
  307. filePath: e.tempFilePath,
  308. success: function () {
  309. resolve_c('success')
  310. },
  311. fail: function (err) {
  312. reject_c('fail');
  313. }
  314. });
  315. }
  316. },
  317. fail: function (err) {
  318. reject_c('fail');
  319. }
  320. });
  321. // #endif
  322. // #ifdef MP-WEIXIN
  323. if (desc === 'video') {
  324. const ago = new Date().getTime();
  325. const limit = 1000 * 60; //max 1minute
  326. downloadTask.onProgressUpdate((res) => {
  327. let currency = new Date().getTime();
  328. if (currency - ago > limit) {
  329. downloadTask.abort();
  330. reject_c('fail');
  331. }
  332. });
  333. }
  334. // #endif
  335. } catch (e) {
  336. reject_c('fail');
  337. }
  338. })
  339. })).then(() => {
  340. batchStatus = false;
  341. uni.hideLoading();
  342. resolve_a('success');
  343. }).catch(() => {
  344. batchStatus = false;
  345. uni.hideLoading();
  346. /* 提示暂放 */
  347. uni.showToast({title: '下载失败'});
  348. reject_a('fail');
  349. })
  350. // #endif
  351. }).catch(() => {
  352. uni.showModal({title: '提示', content: '授权失败,请稍后重新获取', showCancel: false});
  353. reject_a('fail');
  354. })
  355. })
  356. }
  357. /**
  358. * 删除url中指定参数,并返回删除后的url
  359. * @param {string} url url或完整带参数的路由
  360. * @param {string|array} param 需要删除的参数,多个可使用数字如['key1', 'key2']
  361. * @param {boolean} allParams 是否删除所有参数,默认false
  362. * @returns {string|*}
  363. */
  364. const deleteUrlParam = function (url, param, allParams) {
  365. if (isNaN(url.indexOf('?')) || url.indexOf('?') < 0) {
  366. return url;
  367. }
  368. let query = url.substr(url.indexOf('?') + 1);
  369. let route = url.substr(0, url.indexOf('?'));
  370. if (allParams) {
  371. return route;
  372. }
  373. let params = [];
  374. if (typeof param === 'object') {
  375. params = param;
  376. } else {
  377. params = [param];
  378. }
  379. let queryObj = {};
  380. let queryList = query.split('&');
  381. for (let i in queryList) {
  382. queryList[i] = queryList[i].split('=');
  383. queryObj[queryList[i][0]] = queryList[i][1];
  384. }
  385. for (let i in params) {
  386. delete queryObj[params[i]];
  387. }
  388. query = JSON.stringify(queryObj).replace(/[\"\{\}]/g, '').replace(/\:/g, '=').replace(/\,/g, '&');
  389. if (!query.length) {
  390. return route;
  391. }
  392. return route + '?' + query;
  393. };
  394. const colorRgba = function (sHex, alpha = 1) {
  395. // 十六进制颜色值的正则表达式
  396. let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
  397. /* 16进制颜色转为RGB格式 */
  398. let sColor = sHex.toLowerCase()
  399. if (sColor && reg.test(sColor)) {
  400. if (sColor.length === 4) {
  401. let sColorNew = '#'
  402. for (let i = 1; i < 4; i += 1) {
  403. sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
  404. }
  405. sColor = sColorNew
  406. }
  407. // 处理六位的颜色值
  408. let sColorChange = []
  409. for (let i = 1; i < 7; i += 2) {
  410. sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2)))
  411. }
  412. // return sColorChange.join(',')
  413. // 或
  414. return 'rgba(' + sColorChange.join(',') + ',' + alpha + ')'
  415. } else {
  416. return sColor
  417. }
  418. };
  419. let timer, flag;
  420. /**
  421. * 节流原理:在一定时间内,只能触发一次
  422. *
  423. * @param {Function} func 要执行的回调函数
  424. * @param {Number} wait 延时的时间
  425. * @param {Boolean} immediate 是否立即执行
  426. * @return null
  427. */
  428. const throttle = function (func, wait = 500, immediate = true) {
  429. if (immediate) {
  430. if (!flag) {
  431. flag = true;
  432. typeof func === 'function' && func();
  433. timer = setTimeout(() => {
  434. flag = false;
  435. }, wait);
  436. }
  437. } else {
  438. if (!flag) {
  439. flag = true
  440. timer = setTimeout(() => {
  441. flag = false
  442. typeof func === 'function' && func();
  443. }, wait);
  444. }
  445. }
  446. }
  447. let timeout = null;
  448. /**
  449. * 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数
  450. *
  451. * @param {Function} func 要执行的回调函数
  452. * @param {Number} wait 延时的时间
  453. * @param {Boolean} immediate 是否立即执行
  454. * @return null
  455. */
  456. const debounce =function (func, wait = 500, immediate = false) {
  457. if (timeout !== null) clearTimeout(timeout);
  458. if (immediate) {
  459. let callNow = !timeout;
  460. timeout = setTimeout(function() {
  461. timeout = null;
  462. }, wait);
  463. if (callNow) typeof func === 'function' && func();
  464. } else {
  465. timeout = setTimeout(function() {
  466. typeof func === 'function' && func();
  467. }, wait);
  468. }
  469. }
  470. const deepClone = function(obj) {
  471. function isArray (arr) {
  472. return Object.prototype.toString.call(arr) === '[object Array]';
  473. }
  474. if([null, undefined, NaN, false].includes(obj)) return obj;
  475. if(typeof obj !== "object" && typeof obj !== 'function') {
  476. return obj;
  477. }
  478. let o = isArray(obj) ? [] : {};
  479. for(let i in obj) {
  480. if(obj.hasOwnProperty(i)){
  481. o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i];
  482. }
  483. }
  484. return o;
  485. }
  486. const uniCopy = function ({data, success, error}) {
  487. // #ifndef H5
  488. uni.setClipboardData({
  489. data: data,
  490. success() {
  491. success && success()
  492. }
  493. });
  494. // #endif
  495. // #ifdef H5
  496. if (!document.queryCommandSupported('copy')) { //为了兼容有些浏览器 queryCommandSupported 的判断
  497. // 不支持
  498. }
  499. let textarea = document.createElement("textarea");
  500. textarea.value = data;
  501. textarea.readOnly = "readOnly";
  502. document.body.appendChild(textarea);
  503. textarea.select(); // 选择对象
  504. textarea.setSelectionRange(0, data.length) //核心
  505. let result = document.execCommand("copy") // 执行浏览器复制命令
  506. if (result) {
  507. success && success()
  508. } else {
  509. }
  510. textarea.remove();
  511. // #endif
  512. }
  513. const getUrlParam = function(name) {
  514. // #ifdef H5
  515. let url = window.location.href.split('#')[0];
  516. let search = url.split('?')[1];
  517. if (search) {
  518. let r = search.substr(0).match(new RegExp('(^|&)' + name + '=([^&]*)(&|$)'))
  519. if (r !== null) return unescape(r[2])
  520. return null
  521. } else {
  522. return null
  523. }
  524. // #endif
  525. }
  526. // #ifdef H5
  527. const createWxOpenLaunchWeapp = function (id, username, path, text) {
  528. let script = document.createElement('script');
  529. script.type = 'text/wxtag-template';
  530. script.text = text;
  531. let html = `<wx-open-launch-weapp style="width:100%;display:block;height:100%;" username="${username}" path="${path}">${script.outerHTML}
  532. </wx-open-launch-weapp>`;
  533. setTimeout(() => {
  534. document.getElementById(id).innerHTML = html;
  535. }, 500);
  536. }
  537. function guid(str) {
  538. function S4() {
  539. return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
  540. }
  541. return str + "-" + (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
  542. }
  543. const getUrlParamApp = function(url,name) {
  544. let search = url.split('?')[1];
  545. if (search) {
  546. let r = search.substr(0).match(new RegExp('(^|&)' + name + '=([^&]*)(&|$)'))
  547. console.log(r);
  548. if (r !== null) {
  549. console.log(unescape(r[2]));
  550. return unescape(r[2])
  551. }
  552. return null
  553. } else {
  554. return null
  555. }
  556. }
  557. // #endif
  558. export {
  559. time,
  560. datetime,
  561. strtotime,
  562. objectToUrlParams,
  563. timeDifference,
  564. earthDistance,
  565. objectValues,
  566. randomString,
  567. urlParamsToObject,
  568. showLoading,
  569. hideLoading,
  570. batchSave,
  571. deleteUrlParam,
  572. colorRgba,
  573. throttle,
  574. debounce,
  575. deepClone,
  576. uniCopy,
  577. getUrlParam,
  578. h5Address,
  579. // #ifdef H5
  580. createWxOpenLaunchWeapp,
  581. guid,
  582. getUrlParamApp
  583. // #endif
  584. }