cloud-utils.common.js

  1. /*!
  2. * cloud-utils v1.6.3 (https://github.com/cklwblove/cloud-utils)
  3. * API https://cklwblove.github.io/cloud-utils/
  4. * Copyright 2017-2020 liwb. All Rights Reserved
  5. * Licensed under MIT (https://github.com/cklwblove/cloud-utils/blob/master/LICENSE)
  6. */
  7. 'use strict';
  8. Object.defineProperty(exports, '__esModule', { value: true });
  9. /**
  10. * 判断 iPhone X Series 机型,刘海屏
  11. *
  12. * @returns {boolean}
  13. * @example
  14. *
  15. * isPhoneX()
  16. * => true
  17. */
  18. function isPhoneX() {
  19. // X XS, XS Max, XR
  20. var xSeriesConfig = [
  21. {
  22. devicePixelRatio: 3,
  23. width: 375,
  24. height: 812
  25. },
  26. {
  27. devicePixelRatio: 3,
  28. width: 414,
  29. height: 896
  30. },
  31. {
  32. devicePixelRatio: 2,
  33. width: 414,
  34. height: 896
  35. }
  36. ];
  37. // h5
  38. if (typeof window !== 'undefined' && window) {
  39. var isIOS = /iphone/gi.test(window.navigator.userAgent);
  40. if (!isIOS) { return false; }
  41. var devicePixelRatio = window.devicePixelRatio;
  42. var screen = window.screen;
  43. var width = screen.width;
  44. var height = screen.height;
  45. return xSeriesConfig.some(function (item) { return item.devicePixelRatio === devicePixelRatio && item.width === width && item.height === height; });
  46. }
  47. return false;
  48. }
  49. /**
  50. * 加法函数,用来得到精确的加法结果<br>
  51. * javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
  52. *
  53. * @param {number} arg1
  54. * @param {number} arg2
  55. * @returns {number} arg1加上arg2的精确结果
  56. * @example
  57. *
  58. * accAdd(0.1, 0.2)
  59. * // => 0.3
  60. */
  61. function accAdd(arg1, arg2) {
  62. var r1;
  63. var r2;
  64. var m;
  65. var c;
  66. try {
  67. r1 = arg1.toString().split('.')[1].length;
  68. } catch (e) {
  69. r1 = 0;
  70. }
  71. try {
  72. r2 = arg2.toString().split('.')[1].length;
  73. } catch (e) {
  74. r2 = 0;
  75. }
  76. c = Math.abs(r1 - r2);
  77. m = Math.pow(10, Math.max(r1, r2));
  78. if (c > 0) {
  79. var cm = Math.pow(10, c);
  80. if (r1 > r2) {
  81. arg1 = Number(arg1.toString().replace('.', ''));
  82. arg2 = Number(arg2.toString().replace('.', '')) * cm;
  83. } else {
  84. arg1 = Number(arg1.toString().replace('.', '')) * cm;
  85. arg2 = Number(arg2.toString().replace('.', ''));
  86. }
  87. } else {
  88. arg1 = Number(arg1.toString().replace('.', ''));
  89. arg2 = Number(arg2.toString().replace('.', ''));
  90. }
  91. return (arg1 + arg2) / m;
  92. }
  93. /**
  94. * 除法函数,用来得到精确的除法结果<br>
  95. * javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
  96. *
  97. * @param {number} arg1
  98. * @param {number} arg2
  99. * @returns {number} arg1除以arg2的精确结果
  100. * @example
  101. *
  102. * accDiv(0.2, 0.3)
  103. * // => 0.6666666666666666
  104. */
  105. function accDiv(arg1, arg2) {
  106. var t1 = 0;
  107. var t2 = 0;
  108. var r1;
  109. var r2;
  110. try {
  111. t1 = arg1.toString().split('.')[1].length;
  112. } catch (e) {
  113. console.error(e);
  114. }
  115. try {
  116. t2 = arg2.toString().split('.')[1].length;
  117. } catch (e) {
  118. console.error(e);
  119. }
  120. r1 = Number(arg1.toString().replace('.', ''));
  121. r2 = Number(arg2.toString().replace('.', ''));
  122. return (r1 / r2) * Math.pow(10, t2 - t1);
  123. }
  124. /**
  125. * 乘法函数,用来得到精确的乘法结果<br>
  126. * javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
  127. *
  128. * @param {number} arg1
  129. * @param {number} arg2
  130. * @returns {number} arg1乘以arg2的精确结果
  131. * @example
  132. *
  133. * accMul(0.222, 0.3333)
  134. * // => 0.0739926
  135. */
  136. function accMul(arg1, arg2) {
  137. var m = 0;
  138. var s1 = arg1.toString();
  139. var s2 = arg2.toString();
  140. try {
  141. m += s1.split('.')[1].length;
  142. } catch (e) {
  143. console.error(e);
  144. }
  145. try {
  146. m += s2.split('.')[1].length;
  147. } catch (e) {
  148. console.error(e);
  149. }
  150. return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m);
  151. }
  152. /**
  153. * 减法函数,用来得到精确的减法结果<br>
  154. * javascript的减法结果会有误差,在两个浮点数相减的时候会比较明显。这个函数返回较为精确的减法结果。
  155. *
  156. * @param {number} arg1
  157. * @param {number} arg2
  158. * @returns {number} arg1减去arg2的精确结果
  159. * @example
  160. *
  161. * accSub(0.3, 0.2)
  162. * // => 0.1
  163. */
  164. function accSub(arg1, arg2) {
  165. var r1;
  166. var r2;
  167. var m;
  168. var n;
  169. try {
  170. r1 = arg1.toString().split('.')[1].length;
  171. } catch (e) {
  172. r1 = 0;
  173. }
  174. try {
  175. r2 = arg2.toString().split('.')[1].length;
  176. } catch (e) {
  177. r2 = 0;
  178. }
  179. m = Math.pow(10, Math.max(r1, r2)); // last modify by deeka //动态控制精度长度
  180. n = (r1 >= r2) ? r1 : r2;
  181. return ((arg1 * m - arg2 * m) / m).toFixed(n);
  182. }
  183. /**
  184. * 为数字加上单位:万或亿
  185. *
  186. * @param {number} number 输入数字.
  187. * @param {number} decimalDigit 返回的小数点后最多的位数,默认为 2
  188. * @return {*} 加上单位后的数字(计算结果有时会有精度问题)
  189. * @example
  190. *
  191. * addChineseUnit(1000.01)
  192. * // => 1000.01
  193. *
  194. * addChineseUnit(10000)
  195. * // => 1万
  196. *
  197. * addChineseUnit(99000)
  198. * // => 9.9万
  199. *
  200. * addChineseUnit(566000)
  201. * // => 56.6万
  202. *
  203. * addChineseUnit(5660000)
  204. * // => 566万
  205. *
  206. * addChineseUnit(44440000)
  207. * // => 4444万
  208. *
  209. * addChineseUnit(11111000)
  210. * // => 1111.1万
  211. *
  212. * addChineseUnit(444400000)
  213. * // => 4.44亿
  214. *
  215. * addChineseUnit(400000000000000000000000)
  216. * // => 3999.99万亿亿
  217. *
  218. * addChineseUnit(4000000000000000000000000)
  219. * // => 4亿亿亿
  220. */
  221. function addChineseUnit(number, decimalDigit) {
  222. var addWan = function (integer, number, mutiple, decimalDigit) {
  223. var digit = getDigit(integer);
  224. if (digit > 3) {
  225. var remainder = digit % 8;
  226. if (remainder >= 5) { // ‘十万’、‘百万’、‘千万’显示为‘万’
  227. remainder = 4;
  228. }
  229. return Math.round(number / Math.pow(10, remainder + mutiple - decimalDigit)) / Math.pow(10, decimalDigit) + '万';
  230. } else {
  231. return Math.round(number / Math.pow(10, mutiple - decimalDigit)) / Math.pow(10, decimalDigit);
  232. }
  233. };
  234. var getDigit = function (integer) {
  235. // 当为负数时的会出现不转换的问题,因此取绝对值
  236. integer = Math.abs(integer);
  237. var digit = -1;
  238. while (integer >= 1) {
  239. digit++;
  240. integer = integer / 10;
  241. }
  242. return digit;
  243. };
  244. return function (number, decimalDigit) {
  245. decimalDigit = decimalDigit == null ? 2 : decimalDigit;
  246. var integer = Math.floor(number);
  247. var digit = getDigit(integer);
  248. // ['个', '十', '百', '千', '万', '十万', '百万', '千万'];
  249. var unit = [];
  250. if (digit > 3) {
  251. var multiple = Math.floor(digit / 8);
  252. if (multiple >= 1) {
  253. var tmp = Math.round(integer / Math.pow(10, 8 * multiple));
  254. unit.push(addWan(tmp, number, 8 * multiple, decimalDigit));
  255. for (var i = 0; i < multiple; i++) {
  256. unit.push('亿');
  257. }
  258. return unit.join('');
  259. } else {
  260. return addWan(integer, number, 0, decimalDigit);
  261. }
  262. } else {
  263. return parseFloat(number).toFixed(decimalDigit);
  264. }
  265. }(number, decimalDigit);
  266. }
  267. /**
  268. * 2020-3-18 23:11:54
  269. * 支持负值格式转换
  270. * eg:
  271. * -123456.33 -> "-123.46万"
  272. */
  273. /**
  274. * 识别股票代码添加市场后缀
  275. *
  276. * @param {string} stock 股票代码
  277. * @returns {string}
  278. * @example
  279. *
  280. * appendStockSuffix('600570')
  281. * // => '600570.SS'
  282. */
  283. function appendStockSuffix(stock) {
  284. var regSS = /^(((700|730|900|600|601|580)[\d]{3})|60[\d]{4})$/;
  285. var regSZ = /^(((000|002|200|300|080|031)[\d]{3}|00[\d]{4}))$/;
  286. if (regSS.test(stock)) {
  287. stock = stock + '.SS';
  288. } else if (regSZ.test(stock)) {
  289. stock = stock + '.SZ';
  290. }
  291. return stock;
  292. }
  293. /**
  294. * 加密算法
  295. * 1.所有入参加入集合M,参数名做key, 值做value
  296. * 2.提供的密钥1(字段名appid)与密钥2(字段名secret)两项,以及当前时间戳(字段名time)也加入集合M,
  297. * 3.将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序)
  298. * 4.集合M所有值拼接成字符串,转化成UTF-8编码格式的字节数组, 最后需要取MD5码(signature摘要值)
  299. *
  300. * @param {object} params
  301. * @example
  302. *
  303. * const params = { mobile: '15858264900', nickname: 'liwb', appkey: 'ertfgdf345435568123454rtoiko5=' };
  304. *
  305. * md5(encrypt(params).toUpperCase());
  306. * // => md5('APPKEY=ERTFGDF34543545=&MOBILE=15858264903&NICKNAME=LIWB')
  307. */
  308. function encrypt(params) {
  309. // 顺序排列key
  310. var keys = Object.keys(params).sort();
  311. var str = [];
  312. keys.forEach(function(p) {
  313. str.push(p + '=' + params[p]);
  314. });
  315. return str.join('&');
  316. }
  317. /**
  318. * 将from所有的键值对都添加到to上面去,返回to
  319. *
  320. * @param {Object} to
  321. * @param {Object} from
  322. * @returns {*}
  323. * @example
  324. *
  325. * const from = {mobile: '15858264903', nickname: 'liwb'};
  326. * const to = {nickname: 'cklwb'};
  327. *
  328. * extend(to, from);
  329. * // => {nickname: "liwb", mobile: "15858264903"}
  330. */
  331. function extend(to, from) {
  332. var keys = Object.keys(from);
  333. var i = keys.length;
  334. while (i--) {
  335. to[keys[i]] = from[keys[i]];
  336. }
  337. return to;
  338. }
  339. /**
  340. * 格式化银行卡<br>
  341. * 用户在输入银行卡号时,需要以4位4位的形式显示,就是每隔4位加个空格,方便用户校对输入的银行卡是否正确<br>
  342. * **注:**一般数据库里面存的都是不带格式的原始数据,所以提交的时候记得过滤下空格再提交哦。毕竟格式化这种算是表现层,前端展示的时候处理下就好,业务逻辑什么用到的卡号可不是格式化后的呢。<br>
  343. * 还原`val.replace(/\s/g, '');`
  344. *
  345. * @param {string} val
  346. * @returns {*}
  347. * @example
  348. *
  349. * formatBankCard('6225365271562822');
  350. * // => 6225 3652 7156 2822
  351. */
  352. function formatBankCard(val) {
  353. if (typeof val !== 'string') { throw new Error('输入值必须为字符串'); }
  354. var len = val.length;
  355. var reg = /(\d{4})(?=\d)/g;
  356. if (len < 4) {
  357. return val;
  358. } else {
  359. return val.replace(reg, '$1 ');
  360. }
  361. }
  362. /**
  363. * Date 转化为指定格式的String<br>
  364. * 月(M)、日(d)、12小时(h)、24小时(H)、分(m)、秒(s)、周(E)、季度(q)可以用 1-2 个占位符<br>
  365. * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
  366. *
  367. * @param {string | number} date string支持形式:20160126 12:00:00,2016-01-26 12:00:00,2016.01.26 12:00:00,20160126,2016-01-26 12:00:00.0
  368. * @param {string} fmt
  369. * @returns {string}
  370. * @example
  371. *
  372. * formatDate(Date.now(), 'yyyy-MM-dd hh:mm:ss.S');
  373. * // => 2006-07-02 08:09:04.423
  374. *
  375. * formatDate(Date.now(), 'yyyy-MM-dd E HH:mm:ss');
  376. * // => 2009-03-10 二 20:09:04
  377. *
  378. * formatDate(Date.now(), 'yyyy-MM-dd EE hh:mm:ss');
  379. * // => 2009-03-10 周二 08:09:04
  380. *
  381. * formatDate(Date.now(), 'yyyy-MM-dd EEE hh:mm:ss');
  382. * // => 2009-03-10 星期二 08:09:04
  383. *
  384. * formatDate(Date.now(), 'yyyy-M-d h:m:s.S')
  385. * // => 2006-7-2 8:9:4.18
  386. */
  387. function formatDate(date, fmt) {
  388. if ( date === void 0 ) date = new Date();
  389. if ( fmt === void 0 ) fmt = 'yyyy-MM-dd HH:mm:ss';
  390. if (typeof date === 'string') {
  391. date = new Date(formatTimeByPattern(date));
  392. } else if (typeof date === 'number') {
  393. date = new Date(date);
  394. }
  395. var o = {
  396. 'M+': date.getMonth() + 1, // 月份
  397. 'd+': date.getDate(), // 日
  398. 'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, // 小时
  399. 'H+': date.getHours(), // 小时
  400. 'm+': date.getMinutes(), // 分
  401. 's+': date.getSeconds(), // 秒
  402. 'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
  403. 'S': date.getMilliseconds() // 毫秒
  404. };
  405. var week = {
  406. '0': '\u65e5',
  407. '1': '\u4e00',
  408. '2': '\u4e8c',
  409. '3': '\u4e09',
  410. '4': '\u56db',
  411. '5': '\u4e94',
  412. '6': '\u516d'
  413. };
  414. if (/(y+)/.test(fmt)) {
  415. fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
  416. }
  417. if (/(E+)/.test(fmt)) {
  418. fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '\u661f\u671f' : '\u5468') : '') + week[date.getDay() + '']);
  419. }
  420. for (var k in o) {
  421. if (new RegExp('(' + k + ')').test(fmt)) {
  422. fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
  423. }
  424. }
  425. return fmt;
  426. }
  427. // val 字符串转换成 / 连接
  428. // 20160126 12:00:00
  429. // 2016-01-26 12:00:00
  430. // 2016.01.26 12:00:00
  431. // 20160126
  432. // 2016-01-26 12:00:00.0
  433. function formatTimeByPattern(val) {
  434. // 2016-05-23 13:58:02.0
  435. if (val.length > 19) {
  436. val = val.substring(0, 19);
  437. }
  438. var pattern = /-|\./g;
  439. var year;
  440. var month;
  441. var day;
  442. var reset;
  443. if (pattern.test(val)) {
  444. return val.replace(pattern, '/');
  445. } else {
  446. // 若无’-‘,则不处理
  447. if (!~val.indexOf('-')) {
  448. year = val.slice(0, 4);
  449. month = val.slice(4, 6);
  450. day = val.slice(6, 8);
  451. reset = val.slice(8);
  452. return year + '/' + month + '/' + day + reset;
  453. }
  454. }
  455. }
  456. /**
  457. * 将时间转化为几天前,几小时前,几分钟前
  458. *
  459. * @param {number} ms
  460. * @returns {*}
  461. * @example
  462. *
  463. * formatTimeAgo(1505232000000);
  464. * // => 1天前
  465. */
  466. function formatTimeAgo(ms) {
  467. ms = parseInt(ms);
  468. var timeNow = Date.now();
  469. var diff = (timeNow - ms) / 1000;
  470. var date = new Date();
  471. var days = Math.round(diff / (24 * 60 * 60));
  472. var hours = Math.round(diff / (60 * 60));
  473. var minutes = Math.round(diff / 60);
  474. var second = Math.round(diff);
  475. if (days > 0 && days < 2) {
  476. return days + '天前';
  477. } else if (days <= 0 && hours > 0) {
  478. return hours + '小时前';
  479. } else if (hours <= 0 && minutes > 0) {
  480. return minutes + '分钟前';
  481. } else if (minutes <= 0 && second >= 0) {
  482. return '刚刚';
  483. } else {
  484. date.setTime(ms);
  485. return (date.getFullYear() + '-' + f(date.getMonth() + 1) + '-' + f(date.getDate()) + ' ' + f(date.getHours()) + ':' + f(date.getMinutes()));
  486. }
  487. function f(n) {
  488. return (n < 10) ? '0' + n : n;
  489. }
  490. }
  491. /**
  492. * 获取指定时间unix时间戳
  493. *
  494. * @param {string} time
  495. * @returns {number}
  496. * @example
  497. *
  498. * formatDateToTimeStamp('20160126 12:00:00');
  499. * // => 1453780800000
  500. *
  501. * formatDateToTimeStamp('2016-01-26 12:00:00');
  502. * // => 1453780800000
  503. *
  504. * formatDateToTimeStamp('2016.01.26 12:00:00');
  505. * // => 1453780800000
  506. *
  507. * formatDateToTimeStamp('20160126');
  508. * // => 1453737600000
  509. *
  510. * formatDateToTimeStamp('2016-01-26 12:00:00.0');
  511. * // => 1453780800000
  512. */
  513. function formatDateToTimeStamp(time) {
  514. if (typeof time !== 'string') { throw new TypeError('数据类型必须是 string'); }
  515. // 2016-05-23 13:58:02.0
  516. if (time.length > 19) {
  517. time = time.substring(0, 19);
  518. }
  519. var unixTime;
  520. var pattern = /-|\./g;
  521. var year;
  522. var month;
  523. var day;
  524. var reset;
  525. if (pattern.test(time)) {
  526. unixTime = time.replace(pattern, '/');
  527. } else {
  528. // 若无’-‘,则不处理
  529. if (!~time.indexOf('-')) {
  530. year = time.slice(0, 4);
  531. month = time.slice(4, 6);
  532. day = time.slice(6, 8);
  533. reset = time.slice(8);
  534. unixTime = year + '/' + month + '/' + day + reset;
  535. }
  536. }
  537. return Math.round(new Date(unixTime).getTime());
  538. }
  539. /**
  540. * 用符号(默认为逗号)格式化金钱
  541. *
  542. * @param {string} val
  543. * @param {string} symbol 默认`,`
  544. * @returns {string|*|XML|void}
  545. * @example
  546. *
  547. * formatMoney('1234567890');
  548. * // => 1,234,567,890
  549. */
  550. function formatMoney(val, symbol) {
  551. if ( symbol === void 0 ) symbol = ',';
  552. if (typeof val !== 'string') { throw new TypeError('数据类型必须是 string'); }
  553. return val.replace(/\B(?=(\d{3})+(?!\d))/g, symbol);
  554. }
  555. /**
  556. * 手机号码中间部分替换成指定符号
  557. *
  558. * @param {string} phone
  559. * @param {string} symbol 默认为`*`
  560. * @returns {string|*|XML|void}
  561. * @example
  562. *
  563. * formatPhone('15858264903');
  564. * // => 158****4903
  565. */
  566. function formatPhone(phone, symbol) {
  567. if ( symbol === void 0 ) symbol = '****';
  568. if (typeof phone !== 'string') { throw new TypeError('数据类型必须是 string'); }
  569. return phone.replace(/(\d{3})\d{4}(\d{4})/, ("$1" + symbol + "$2"));
  570. }
  571. /**
  572. * 获取location.href参数
  573. *
  574. * @param {string} name
  575. * @returns {*}
  576. * @example
  577. *
  578. * window.location.href = 'http://www.baidu.com/?a=1&b=2';
  579. *
  580. * getLocationHrefParam('a');
  581. * // => 1
  582. */
  583. function getLocationHrefParam(name) {
  584. // 构造一个含有目标参数的正则表达式对象
  585. var r = new RegExp('(\\?|#|&)' + name + '=([^&#]*)(&|#|$)');
  586. var m = window.location.href.match(r);
  587. if (r !== null) { return decodeURIComponent(!m ? '' : m[2]); }
  588. return null;
  589. }
  590. /**
  591. * 获取location.search的参数
  592. *
  593. * @param {string} name
  594. * @returns {*}
  595. * @example
  596. *
  597. * window.location.href = 'http://www.baidu.com/?a=1&b=2';
  598. *
  599. * getLocationSearchParam('a');
  600. * // => 1
  601. */
  602. function getLocationSearchParam(name) {
  603. // 构造一个含有目标参数的正则表达式对象
  604. var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
  605. // 匹配目标参数
  606. var r = window.location.search.substr(1).match(reg);
  607. if (r !== null) { return decodeURIComponent(r[2]); }
  608. return null;
  609. }
  610. /**
  611. * 是否为空对象
  612. *
  613. * @param val
  614. * @returns {boolean}
  615. * @example
  616. *
  617. * isEmptyObject({});
  618. * // => true
  619. */
  620. function isEmptyObject(val) {
  621. if (val !== Object(val)) { return false; }
  622. return Object.keys(val).length === 0;
  623. }
  624. /**
  625. * 根据参数获取对应的值
  626. *
  627. * @param {string} name
  628. * @returns {*}
  629. * @example
  630. *
  631. * // window.location.href: 'http://www.baidu.com/?a=1&b=2&state=broker:aaaa1111ccc;tenant:asdfasdf;view_tag:2;
  632. * getUrlNames('state');
  633. * // => {broker: "aaaa1111ccc", tenant: "asdfasdf", view_tag: "2"}
  634. */
  635. function getUrlNames(name) {
  636. var urlParam = getLocationHrefParam(name);
  637. var o = {};
  638. if (urlParam) {
  639. var str = urlParam.split(';');
  640. for (var i = 0, len = str.length; i < len; i++) {
  641. if (str[i].indexOf(':') !== -1) {
  642. var str1 = str[i].split(':');
  643. o[str1[0]] = str1[1];
  644. }
  645. }
  646. }
  647. return !isEmptyObject(o) ? o : '';
  648. }
  649. /**
  650. * 生成guid
  651. *
  652. * @returns {string}
  653. * @example
  654. *
  655. * generateGUID();
  656. * // => 1e508ed6-6177-498d-c2a3-467f546db6ab
  657. */
  658. function generateGUID() {
  659. var d = Date.now();
  660. var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
  661. var r = (d + Math.random() * 16) % 16 | 0;
  662. d = Math.floor(d / 16);
  663. return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16);
  664. });
  665. return uuid;
  666. }
  667. /**
  668. * 获取max与min之间的随机数
  669. *
  670. * @param {number} min
  671. * @param {number} max
  672. * @returns {*}
  673. * @example
  674. *
  675. * getRandomInt(1, 9);
  676. * // => 2
  677. */
  678. function getRandomInt(min, max) {
  679. return Math.floor(Math.random() * (max - min + 1)) + min;
  680. }
  681. /**
  682. * html字符解码
  683. *
  684. * @param {string} str
  685. * @returns {string}
  686. * @example
  687. *
  688. * htmlDecode('&lt;script&gt;');
  689. * // => <script>
  690. */
  691. function htmlDecode(str) {
  692. if (typeof str === 'string' && str.length === 0) { return; }
  693. var s = str.replace(/&amp;/g, '&');
  694. return s.replace(/&lt;/g, '<')
  695. .replace(/&gt;/g, '>')
  696. .replace(/&#39;/g, '\'')
  697. .replace(/&nbsp;/g, ' ')
  698. .replace(/&quot;/g, '"')
  699. .replace(/<br>/g, '\\n');
  700. }
  701. /**
  702. * html字符编码
  703. *
  704. * @param {string} str
  705. * @returns {string}
  706. * @example
  707. *
  708. * htmlEncode('<script>');
  709. * // => &lt;script&gt;
  710. */
  711. function htmlEncode(str) {
  712. if (typeof str === 'string' && str.length === 0) { return; }
  713. var s = str.replace(/&/g, '&amp;');
  714. return s.replace(/</g, '&lt;')
  715. .replace(/>/g, '&gt;')
  716. .replace(/\'/g, '&#39;')
  717. .replace(/ /g, '&nbsp;')
  718. .replace(/\"/g, '&quot;')
  719. .replace(/\n/g, '<br>');
  720. }
  721. /**
  722. * 是否是支付宝内核
  723. *
  724. * @returns {boolean}
  725. * @example
  726. *
  727. * inAlipay();
  728. * // => false
  729. */
  730. function inAlipay() {
  731. if (typeof navigator === 'undefined') { return; }
  732. var ua = navigator.userAgent.toLowerCase();
  733. return ua.indexOf('alipayclient') !== -1;
  734. }
  735. /**
  736. * 是否是微信内核
  737. *
  738. * @returns {boolean}
  739. * @example
  740. *
  741. * inWeixin();
  742. * // => false
  743. */
  744. function inWeixin() {
  745. if (typeof navigator === 'undefined') { return; }
  746. var ua = navigator.userAgent.toLowerCase();
  747. return ua.indexOf('micromessenger') !== -1;
  748. }
  749. /**
  750. * 是否为有效的身份证号,支持1/2代(15位/18位数字)
  751. *
  752. * @param {string} val
  753. * @returns {boolean}
  754. * @example
  755. *
  756. * isCardId('411423198807127834');
  757. * // => true
  758. */
  759. function isCardId(val) {
  760. var reg = /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/;
  761. return reg.test(val);
  762. }
  763. /**
  764. * 是否为数字
  765. *
  766. * @param {string} val
  767. * @returns {boolean}
  768. * @example
  769. *
  770. * isDigit('abc');
  771. * // => false
  772. */
  773. function isDigit(val) {
  774. var reg = /^-?\d+\.?\d*$/;
  775. return reg.test(val);
  776. }
  777. /**
  778. * 是否为闰年
  779. *
  780. * @param {number} val
  781. * @returns {boolean}
  782. * @example
  783. *
  784. * isLeapYear(2000);
  785. * // => true
  786. */
  787. function isLeapYear(val) {
  788. if (typeof val !== 'number') { throw new TypeError('数据类型必须是 number'); }
  789. if (val % 4 === 0 && val % 100 !== 0) {
  790. return true;
  791. } else {
  792. return val % 400 === 0;
  793. }
  794. }
  795. /**
  796. * 是否为字母
  797. *
  798. * @param {string} val
  799. * @returns {boolean}
  800. * @example
  801. *
  802. * isLetters('1234');
  803. * // => false
  804. */
  805. function isLetters(val) {
  806. var reg = /^[a-z]+$/i;
  807. return reg.test(val);
  808. }
  809. /**
  810. * 是否为有效的车牌号码
  811. *
  812. * 1.常规车牌号:仅允许以汉字开头,后面可录入六个字符,由大写英文字母和阿拉伯数字组成。如:粤B12345;<br>
  813. * 2.武警车牌:允许前两位为大写英文字母,后面可录入七个字符,由大写英文字母和阿拉伯数字组成,其中第三位可录汉字也可录大写英文字母及阿拉伯数字,如:WJ01警0081、WJ0112345。<br>
  814. * 3.最后一个为汉字的车牌:允许以汉字开头,后面可录入六个字符,前五位字符,由大写英文字母和阿拉伯数字组成,而最后一个字符为汉字,汉字包括“挂”、“学”、“警”、“军”、“港”、“澳”。<br>如:粤Z1234港。
  815. * 4.新军车牌:以两位为大写英文字母开头,后面以5位阿拉伯数字组成。如:BA12345。<br>
  816. * 5.黑龙江车牌存在08或38开头的情况。<br>
  817. *
  818. * @param {string} val
  819. * @returns {boolean}
  820. * @example
  821. *
  822. * isLicenseNo('浙A12345');
  823. * // => true
  824. */
  825. function isLicenseNo(val) {
  826. var reg = /(^[\u4E00-\u9FA5]{1}[A-Z0-9]{6}$)|(^[A-Z]{2}[A-Z0-9]{2}[A-Z0-9\u4E00-\u9FA5]{1}[A-Z0-9]{4}$)|(^[\u4E00-\u9FA5]{1}[A-Z0-9]{5}[挂学警军港澳]{1}$)|(^[A-Z]{2}[0-9]{5}$)|(^(08|38){1}[A-Z0-9]{4}[A-Z0-9挂学警军港澳]{1}$)/;
  827. return reg.test(val);
  828. }
  829. /**
  830. * 是否为有效的手机号,中国手机号(宽松), 只要是13,14,15,16,17,18,19开头即可
  831. *
  832. * @param {string} val
  833. * @returns {boolean}
  834. * @example
  835. *
  836. * isMobile('15898745678');
  837. * // => true
  838. */
  839. function isMobile(val) {
  840. var reg = /^[1][3456789]\d{9}$/;
  841. return reg.test(val);
  842. }
  843. /**
  844. * 是否为有效的日期格式<br>
  845. * 格式为 yyyy-mm-dd 或 yyyy-mm-dd HH:mm:ss
  846. *
  847. * @param {string} val
  848. * @returns {boolean}
  849. * @example
  850. *
  851. * isValidDate('2015-01-20');
  852. * // => true
  853. */
  854. function isValidDate(val) {
  855. var dateReg = /^\d{4}-\d{2}-\d{2}$/;
  856. var timeReg = /^(\d{4})\-(\d{2})\-(\d{2}) (\d{2})(?:\:\d{2}|:(\d{2}):(\d{2}))$/;
  857. return dateReg.test(val) || timeReg.test(val);
  858. }
  859. /**
  860. * 是否为有效的邮箱地址<br>
  861. * 名称允许汉字、字母、数字,域名只允许英文域名<br>
  862. * 中文如:杨元庆001Abc@lenovo.com.cn
  863. *
  864. * @param {string} val
  865. * @returns {boolean}
  866. * @example
  867. *
  868. * isValidEmail('123456@qq.com');
  869. * // => true
  870. */
  871. function isValidEmail(val) {
  872. var reg = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/i;
  873. return reg.test(val);
  874. }
  875. /**
  876. * 是否为有效的 qq
  877. *
  878. * @param {string} val
  879. * @returns {boolean}
  880. * @example
  881. *
  882. * isValidQQ('814563410');
  883. * // => true
  884. */
  885. function isValidQQ(val) {
  886. var reg = /^[1-9][0-9]{4,10}$/;
  887. return reg.test(val);
  888. }
  889. /**
  890. * 是否为有效的 url<br>
  891. *
  892. * 支持类型:<br>
  893. * http(s)://(username:password@)(www.)domain.(com/co.uk)(/...)<br>
  894. * (s)ftp://(username:password@)domain.com/...<br>
  895. * git://(username:password@)domain.com/...<br>
  896. * irc(6/s)://host:port/... //<br>
  897. * afp over TCP/IP: afp://[<user>@]<host>[:<port>][/[<path>]]<br>
  898. * telnet://<user>:<password>@<host>[:<port>/]<br>
  899. * smb://[<user>@]<host>[:<port>][/[<path>]][?<param1>=<value1>[;<param2>=<value2>]]<br>
  900. *
  901. * @param {string} url
  902. * @returns {*}
  903. * @example
  904. *
  905. * isValidURI('https://github.com/lodash');
  906. * // => true
  907. */
  908. function isValidURI(url) {
  909. var protocols = '((https?|s?ftp|irc[6s]?|git|afp|telnet|smb):\\/\\/)?';
  910. var userInfo = '([a-z0-9]\\w*(\\:[\\S]+)?\\@)?';
  911. var domain = '([a-z0-9]([\\w]*[a-z0-9])*\\.)?[a-z0-9]\\w*\\.[a-z]{2,}(\\.[a-z]{2,})?';
  912. var port = '(:\\d{1,5})?';
  913. var ip = '\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}';
  914. var address = '(\\/\\S*)?';
  915. var domainType = [protocols, userInfo, domain, port, address];
  916. var ipType = [protocols, userInfo, ip, port, address];
  917. var verify = function (type) {
  918. return new RegExp('^' + type.join('') + '$', 'i').test(url);
  919. };
  920. return verify(domainType) || verify(ipType);
  921. }
  922. /**
  923. * 是否为有效的中国邮政编码
  924. *
  925. * @param val
  926. * @returns {boolean}
  927. * @example
  928. *
  929. * isValidZipcode('330561');
  930. * // => true
  931. */
  932. function isValidZipcode(val) {
  933. var reg = /^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\d{4}$/;
  934. return reg.test(val);
  935. }
  936. /**
  937. * 对整数进行前置补0
  938. *
  939. * @param {number} num 数值
  940. * @param {number} size 要补0的位数
  941. * @returns {string}
  942. * @example
  943. *
  944. * preZeroFill(12, 3);
  945. * // => 012
  946. */
  947. function preZeroFill(num, size) {
  948. if (num >= Math.pow(10, size)) {
  949. return num.toString();
  950. } else {
  951. var _str = Array(size + 1).join('0') + num;
  952. return _str.slice(_str.length - size);
  953. }
  954. }
  955. /**
  956. * 将字节转换成友好格式,如Bytes,KB,MB
  957. *
  958. * @param {string} bytes
  959. * @returns {*}
  960. * @example
  961. *
  962. * bytesToSize(10000)
  963. * // => 9.8 KB
  964. */
  965. function bytesToSize(bytes) {
  966. var sizes = ['Bytes', 'KB', 'MB'];
  967. if (bytes === 0) { return 'n/a'; }
  968. var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
  969. return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
  970. }
  971. /**
  972. * base64转blob
  973. *
  974. * @param {string} dataURL
  975. * @returns {*}
  976. * @example
  977. *
  978. * const URI = 'data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48ZGVmcz48c3R5bGUvPjwvZGVmcz48cGF0aCBkPSJNOTU5LjQzNiAyNDMuNUwzNzcuNDEzIDg3MC4yNyA2NC4wMiA0NjcuMzQ0bDExNC43MjctOTcuOTMgMTk4LjY2NiAyMDcuMDYgNDkyLjQ4Mi00MjIuNTEgODkuNTQgODkuNTM3em0wIDAiIGZpbGw9IiM0M2E5ZjEiLz48L3N2Zz4=';
  979. *
  980. * dataURLToBlob(URI);
  981. * // => Blob {size: 248, type: "image/svg+xml"}
  982. */
  983. function dataURLToBlob(dataURL) {
  984. var BASE64_MARKER = ';base64,';
  985. var parts;
  986. var contentType;
  987. var raw;
  988. if (dataURL.indexOf(BASE64_MARKER) === -1) {
  989. parts = dataURL.split(',');
  990. contentType = parts[0].split(':')[1];
  991. raw = decodeURIComponent(parts[1]);
  992. return new Blob([raw], {type: contentType});
  993. }
  994. parts = dataURL.split(BASE64_MARKER);
  995. contentType = parts[0].split(':')[1];
  996. raw = window.atob(parts[1]);
  997. var rawLength = raw.length;
  998. var uInt8Array = new Uint8Array(rawLength);
  999. for (var i = 0; i < rawLength; ++i) {
  1000. uInt8Array[i] = raw.charCodeAt(i);
  1001. }
  1002. return new Blob([uInt8Array], {type: contentType});
  1003. }
  1004. /**
  1005. * 获取设备像素比
  1006. *
  1007. * @returns {number}
  1008. * @example
  1009. *
  1010. * // window.navigator.appVersion(5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1)
  1011. * getPixelRatio();
  1012. * // => 2
  1013. */
  1014. function getPixelRatio() {
  1015. var ctx = document.createElement('canvas').getContext('2d');
  1016. var dpr = window.devicePixelRatio || 1;
  1017. var bsr = ctx.webkitBackingStorePixelRatio ||
  1018. ctx.mozBackingStorePixelRatio ||
  1019. ctx.msBackingStorePixelRatio ||
  1020. ctx.oBackingStorePixelRatio ||
  1021. ctx.backingStorePixelRatio || 1;
  1022. return dpr / bsr;
  1023. }
  1024. /**
  1025. * 将文本插入到文本区域的光标位置<br>
  1026. * _应用场景:_如在评论框里,在光标位置里插入emoji等
  1027. *
  1028. * @param {object} dom对象
  1029. * @param {string} str
  1030. * @example
  1031. *
  1032. * <textarea name="textarea" rows="10" cols="50">你好世界~</textarea>
  1033. *
  1034. * const editText = document.querySelector('#editText');
  1035. *
  1036. * insertText(editText, 'hello world');
  1037. * // =>
  1038. */
  1039. function insertAtCaret(dom, str) {
  1040. if ( str === void 0 ) str = '';
  1041. if (document.selection) { // IE
  1042. var sel = document.selection.createRange();
  1043. sel.text = str;
  1044. } else if (typeof dom.selectionStart === 'number' && typeof dom.selectionEnd === 'number') {
  1045. var startPos = dom.selectionStart;
  1046. var endPos = dom.selectionEnd;
  1047. var cursorPos = startPos;
  1048. var tmpStr = dom.value;
  1049. dom.value = tmpStr.substring(0, startPos) + str + tmpStr.substring(endPos, tmpStr.length);
  1050. cursorPos += str.length;
  1051. dom.selectionStart = dom.selectionEnd = cursorPos;
  1052. } else {
  1053. dom.value += str;
  1054. }
  1055. }
  1056. /**
  1057. * 获取移动设备信息,如是否是iOS,android等
  1058. *
  1059. * @returns {{}}
  1060. * @example
  1061. *
  1062. * getDevice();
  1063. * // => {"androidChrome":false,"ipad":false,"iphone":true,"android":false,"ios":true,"os":"ios","osVersion":"9.1","webView":null}
  1064. */
  1065. function getDevice() {
  1066. var device = {};
  1067. var ua = navigator.userAgent;
  1068. var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
  1069. var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
  1070. var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
  1071. var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
  1072. device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false;
  1073. // Android
  1074. if (android) {
  1075. device.os = 'android';
  1076. device.osVersion = android[2];
  1077. device.android = true;
  1078. device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0;
  1079. }
  1080. if (ipad || iphone || ipod) {
  1081. device.os = 'ios';
  1082. device.ios = true;
  1083. }
  1084. // iOS
  1085. if (iphone && !ipod) {
  1086. device.osVersion = iphone[2].replace(/_/g, '.');
  1087. device.iphone = true;
  1088. }
  1089. if (ipad) {
  1090. device.osVersion = ipad[2].replace(/_/g, '.');
  1091. device.ipad = true;
  1092. }
  1093. if (ipod) {
  1094. device.osVersion = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
  1095. device.iphone = true;
  1096. }
  1097. // iOS 8+ changed UA
  1098. if (device.ios && device.osVersion && ua.indexOf('Version/') >= 0) {
  1099. if (device.osVersion.split('.')[0] === '10') {
  1100. device.osVersion = ua.toLowerCase().split('version/')[1].split(' ')[0];
  1101. }
  1102. }
  1103. // Webview
  1104. device.webView = (iphone || ipad || ipod) && ua.match(/.*AppleWebKit(?!.*Safari)/i);
  1105. return device;
  1106. }
  1107. /**
  1108. * 获取浏览器的类型和版本号
  1109. *
  1110. * @returns {{type: string, version: string}}
  1111. * @example
  1112. *
  1113. * getBrowser();
  1114. * // => {type: "chrome", version: "60.0.3112.101"}
  1115. */
  1116. function getBrowser() {
  1117. var ua = navigator.userAgent.toLowerCase();
  1118. var type = 'UNKNOW';
  1119. var v;
  1120. var check = function (regex) {
  1121. return regex.test(ua);
  1122. };
  1123. // IE
  1124. if (check(/msie/) && !check(/opera/)) {
  1125. type = 'ie';
  1126. v = /msie[\/|\s]*([\d+?\.?]+)/.exec(ua);
  1127. } else if ((!check(/webkit/) && check(/gecko/) && check(/firefox/)) && !check(/opera/)) { // firefox
  1128. type = 'firefox';
  1129. v = /firefox[\/|\s]*([\d+?\.?]+)/.exec(ua);
  1130. } else if (check(/\bchrome\b/)) { // chrome
  1131. type = 'chrome';
  1132. v = /chrome[\/|\s]*([\d+?\.?]+)/.exec(ua);
  1133. } else if (check(/applewebkit/) && check(/safari/)) { // safari (!check(/\bchrome\b/) is ensure by non-chrome above)
  1134. type = 'safari';
  1135. v = /version[\/|\s]*([\d+?\.?]+)/.exec(ua);
  1136. } else if (check(/opera/)) {
  1137. type = 'opera';
  1138. v = /version[\/|\s]*([\d+?.?]+)/.exec(ua) || /opera[\/|\s]*([\d+?.?]+)/.exec(ua);
  1139. }
  1140. return {
  1141. type: type,
  1142. version: (v && v[1]) ? v[1] : 'UNKNOW'
  1143. }
  1144. }
  1145. /**
  1146. * 得到两个时间的时间差(返回天数)
  1147. *
  1148. * @since 1.0.7
  1149. * @param {number} startDay 开始时间戳
  1150. * @param {number} endDay 结束时间戳
  1151. * @returns {number}
  1152. * @example
  1153. *
  1154. * getDiffDay(1501516800000, 1504195200000);
  1155. * // => 31
  1156. */
  1157. function getDiffDay(startDay, endDay) {
  1158. startDay = Number(startDay);
  1159. endDay = Number(endDay);
  1160. return Math.abs(endDay - startDay) / (24 * 1000 * 3600);
  1161. }
  1162. /**
  1163. * Dom 操作,元素是包含某个 class
  1164. *
  1165. * @since 1.1.5
  1166. * @param el HTML元素
  1167. * @param cls css类名
  1168. * @returns {boolean}
  1169. * @example
  1170. *
  1171. * <div class="box flex"></div>
  1172. * hasClass(document.querySelector('.box'), 'flex');
  1173. * // => true
  1174. */
  1175. function hasClass(el, cls) {
  1176. if (!el || !cls) { return false; }
  1177. if (cls.indexOf(' ') !== -1) { throw new Error('className should not contain space.'); }
  1178. if (el.classList) {
  1179. return el.classList.contains(cls);
  1180. } else {
  1181. return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
  1182. }
  1183. }
  1184. /**
  1185. * dom操作,元素添加某个class
  1186. *
  1187. * @since 1.1.5
  1188. * @param el HTML元素
  1189. * @param cls css类名
  1190. * @example
  1191. *
  1192. * <div class="box flex"></div>
  1193. * addClass(document.querySelector('.box'), 'flex1');
  1194. * // => <div class="box flex flex1"></div>
  1195. */
  1196. function addClass(el, cls) {
  1197. if (!el) { return; }
  1198. var curClass = el.className;
  1199. var classes = (cls || '').split(' ');
  1200. for (var i = 0, j = classes.length; i < j; i++) {
  1201. var clsName = classes[i];
  1202. if (!clsName) { continue; }
  1203. if (el.classList) {
  1204. el.classList.add(clsName);
  1205. } else {
  1206. if (!hasClass(el, clsName)) {
  1207. curClass += ' ' + clsName;
  1208. }
  1209. }
  1210. }
  1211. if (!el.classList) {
  1212. el.className = curClass;
  1213. }
  1214. }
  1215. var trim = function(string) {
  1216. return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
  1217. };
  1218. /**
  1219. * Dom 操作,元素删除某个 class
  1220. *
  1221. * @since 1.1.5
  1222. * @param el HTML元素
  1223. * @param cls css类名
  1224. * @example
  1225. *
  1226. * <div class="box flex"></div>
  1227. * removeClass(document.querySelector('.box'), 'flex');
  1228. * // => <div class="box"></div>
  1229. */
  1230. function removeClass(el, cls) {
  1231. if (!el || !cls) { return; }
  1232. var classes = cls.split(' ');
  1233. var curClass = ' ' + el.className + ' ';
  1234. for (var i = 0, j = classes.length; i < j; i++) {
  1235. var clsName = classes[i];
  1236. if (!clsName) { continue; }
  1237. if (el.classList) {
  1238. el.classList.remove(clsName);
  1239. } else {
  1240. if (hasClass(el, clsName)) {
  1241. curClass = curClass.replace(' ' + clsName + ' ', ' ');
  1242. }
  1243. }
  1244. }
  1245. if (!el.classList) {
  1246. el.className = trim(curClass);
  1247. }
  1248. }
  1249. /**
  1250. * 中划线转换小驼峰
  1251. *
  1252. * @since 1.1.7
  1253. * @param {string} variable
  1254. * @returns {string}
  1255. * @example
  1256. *
  1257. * toCamelCaseVar('get_account_list');
  1258. * // => getAccountList
  1259. */
  1260. function toCamelCaseVar (variable) {
  1261. return variable.replace(/_+[a-zA-Z]/g, function (str, index) { return index ? str.substr(-1).toUpperCase() : str; });
  1262. }
  1263. /**
  1264. * 格式化数字、金额、千分位、保留几位小数、舍入舍去
  1265. *
  1266. * @since 1.0.7
  1267. * @param number 要格式化的数字
  1268. * @param decimals 保留几位小数
  1269. * @param decPoint 小数点符号
  1270. * @param thousandsSep 千分位符号
  1271. * @param roundTag 舍入参数,默认 'ceil' 向上取,'floor'向下取,'round' 四舍五入
  1272. * @returns {XML|void|*|string}
  1273. * @example
  1274. *
  1275. * formatNumber(2, 2, '.', ',');
  1276. * // => 2.00
  1277. */
  1278. function formatNumber(number, decimals, decPoint, thousandsSep, roundTag) {
  1279. if ( decimals === void 0 ) decimals = 2;
  1280. if ( decPoint === void 0 ) decPoint = '.';
  1281. if ( thousandsSep === void 0 ) thousandsSep = ',';
  1282. if ( roundTag === void 0 ) roundTag = 'ceil';
  1283. number = (number + '').replace(/[^0-9+-Ee.]/g, '');
  1284. var n = !isFinite(+number) ? 0 : +number;
  1285. var prec = !isFinite(+decimals) ? 0 : Math.abs(decimals);
  1286. var sep = thousandsSep || ',';
  1287. var dec = decPoint || '.';
  1288. var re = /(-?\d+)(\d{3})/;
  1289. var s = '';
  1290. var toFixedFix = function (n, prec) {
  1291. var k = Math.pow(10, prec);
  1292. return '' + parseFloat(Math[roundTag](parseFloat((n * k).toFixed(prec * 2))).toFixed(prec * 2)) / k;
  1293. };
  1294. s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
  1295. while (re.test(s[0])) {
  1296. s[0] = s[0].replace(re, '$1' + sep + '$2');
  1297. }
  1298. if ((s[1] || '').length < prec) {
  1299. s[1] = s[1] || '';
  1300. s[1] += new Array(prec - s[1].length + 1).join('0');
  1301. }
  1302. return s.join(dec);
  1303. }
  1304. /**
  1305. * 版本比较
  1306. *
  1307. * {@link https://github.com/omichelsen/compare-versions}
  1308. * @param v1 老版本
  1309. * @param v2 新版本
  1310. * @returns {number} v1 > v2 => 1, v1 < v2 => -1, v1 === v2 => 0
  1311. * @example
  1312. *
  1313. * compareVersion('10.1.8', '10.0.4');
  1314. * // => 1
  1315. * compareVersion('10.0.1', '10.0.1');
  1316. * // => 0
  1317. * compareVersion('10.1.1', '10.2.2');
  1318. * // => -1
  1319. */
  1320. function compareVersion(v1, v2) {
  1321. var semver = /^v?(?:\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+))?(?:-[\da-z\-]+(?:\.[\da-z\-]+)*)?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;
  1322. function indexOrEnd(str, q) {
  1323. return str.indexOf(q) === -1 ? str.length : str.indexOf(q);
  1324. }
  1325. function split(v) {
  1326. var c = v.replace(/^v/, '').replace(/\+.*$/, '');
  1327. var patchIndex = indexOrEnd(c, '-');
  1328. var arr = c.substring(0, patchIndex).split('.');
  1329. arr.push(c.substring(patchIndex + 1));
  1330. return arr;
  1331. }
  1332. function tryParse(v) {
  1333. return isNaN(Number(v)) ? v : Number(v);
  1334. }
  1335. function validate(version) {
  1336. if (typeof version !== 'string') {
  1337. throw new TypeError('Invalid argument expected string');
  1338. }
  1339. if (!semver.test(version)) {
  1340. throw new Error('Invalid argument not valid semver (\''+version+'\' received)');
  1341. }
  1342. }
  1343. [v1, v2].forEach(validate);
  1344. var s1 = split(v1);
  1345. var s2 = split(v2);
  1346. for (var i = 0; i < Math.max(s1.length - 1, s2.length - 1); i++) {
  1347. var n1 = parseInt(s1[i] || 0, 10);
  1348. var n2 = parseInt(s2[i] || 0, 10);
  1349. if (n1 > n2) { return 1; }
  1350. if (n2 > n1) { return -1; }
  1351. }
  1352. var sp1 = s1[s1.length - 1];
  1353. var sp2 = s2[s2.length - 1];
  1354. if (sp1 && sp2) {
  1355. var p1 = sp1.split('.').map(tryParse);
  1356. var p2 = sp2.split('.').map(tryParse);
  1357. for (i = 0; i < Math.max(p1.length, p2.length); i++) {
  1358. if (p1[i] === undefined || typeof p2[i] === 'string' && typeof p1[i] === 'number') { return -1; }
  1359. if (p2[i] === undefined || typeof p1[i] === 'string' && typeof p2[i] === 'number') { return 1; }
  1360. if (p1[i] > p2[i]) { return 1; }
  1361. if (p2[i] > p1[i]) { return -1; }
  1362. }
  1363. } else if (sp1 || sp2) {
  1364. return sp1 ? -1 : 1;
  1365. }
  1366. return 0;
  1367. }
  1368. /**
  1369. * 主动防御
  1370. * 对于我们操作的数据,尤其是由 API 接口返回的,时常会有一个很复杂的深层嵌套的数据结构。为了代码的健壮性,很多时候需要对每一层访问都作空值判断,就像这样:
  1371. props.user &&
  1372. props.user.posts &&
  1373. props.user.posts[0] &&
  1374. props.user.posts[0].comments &&
  1375. props.user.posts[0].comments[0]
  1376. 代码看起来相当不美观,因此提供了一个非常简洁明了的原生的方式。
  1377. *
  1378. * @param p 属性列表
  1379. * @param o 对象
  1380. * @returns {*} 如果正常访问到,则返回对应的值,否则返回 null。
  1381. * @example
  1382. *
  1383. * var props = {
  1384. * user: {
  1385. * post: [{
  1386. * comments: 'test'
  1387. * }]
  1388. * }
  1389. * };
  1390. * getIn(['user', 'post', 0, 'comments'], props);
  1391. * // => test
  1392. */
  1393. function getIn(p, o) {
  1394. return p.reduce(function(xs, x) {
  1395. return (xs && xs[x]) ? xs[x] : null;
  1396. }, o);
  1397. }
  1398. /**
  1399. * RGB 转换为 Hex
  1400. *
  1401. * @since 1.2.0
  1402. * @param r r值
  1403. * @param g g值
  1404. * @param b b值
  1405. * @returns {string} Hex值
  1406. * @example
  1407. * rgbToHex(0,0,0);
  1408. * // => #000000
  1409. */
  1410. function rgbToHex(r, g, b) {
  1411. return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
  1412. }
  1413. /**
  1414. * Hex 转换为 Rgb
  1415. *
  1416. * @since 1.2.0
  1417. * @param hex
  1418. * @returns {*}
  1419. * @example
  1420. *
  1421. * hexToRgb("#0033ff").g;
  1422. * // => 51
  1423. */
  1424. function hexToRgb(hex) {
  1425. // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
  1426. var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  1427. hex = hex.replace(shorthandRegex, function (m, r, g, b) {
  1428. return r + r + g + g + b + b;
  1429. });
  1430. var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  1431. return result ? {
  1432. r: parseInt(result[1], 16),
  1433. g: parseInt(result[2], 16),
  1434. b: parseInt(result[3], 16)
  1435. } : null;
  1436. }
  1437. /**
  1438. * Anagrams of string(带有重复项)
  1439. * 使用递归。对于给定字符串中的每个字母,为字母创建字谜。使用map()将字母与每部分字谜组合,然后使用reduce()将所有字谜组合到一个数组中,最基本情况是字符串长度等于2或1。
  1440. *
  1441. * @since 1.2.1
  1442. * @param str
  1443. * @returns {*}
  1444. * @example
  1445. *
  1446. * anagrams('abc');
  1447. * // => ['abc','acb','bac','bca','cab','cba']
  1448. */
  1449. function anagrams(str) {
  1450. if (str.length <= 2) { return str.length === 2 ? [str, str[1] + str[0]] : [str]; }
  1451. return str.split('').reduce(function (acc, letter, i) { return acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(function (val) { return letter + val; })); }, []);
  1452. }
  1453. /**
  1454. * 大写每个单词的首字母
  1455. *
  1456. * @since 1.2.1
  1457. * @param str
  1458. * @returns {string|*|void|XML}
  1459. * @example
  1460. *
  1461. * capitalizeEveryWord('hello world!');
  1462. * // => 'Hello World!'
  1463. */
  1464. function capitalizeEveryWord(str) {
  1465. return str.replace(/\b[a-z]/g, function (char) { return char.toUpperCase(); });
  1466. }
  1467. /**
  1468. * 斐波那契数组生成器
  1469. * 创建一个特定长度的空数组,初始化前两个值(0和1)。使用Array.reduce()向数组中添加值,后面的一个数等于前面两个数相加之和(前两个除外)。
  1470. *
  1471. * @since 1.2.1
  1472. * @param num
  1473. * @returns {*}
  1474. * @example
  1475. *
  1476. * fibonacci(5);
  1477. * // => [0,1,1,2,3]
  1478. */
  1479. function fibonacci(num) {
  1480. return Array(num).fill(0).reduce(function (acc, val, i) { return acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i); }, []);
  1481. }
  1482. /**
  1483. * 获取滚动位置
  1484. * 如果已定义,请使用pageXOffset和pageYOffset,否则使用scrollLeft和scrollTop,可以省略el来使用window的默认值。
  1485. *
  1486. * @since 1.2.1
  1487. * @param el
  1488. * @returns {{x: Number, y: Number}}
  1489. * @example
  1490. *
  1491. * getScrollPos();
  1492. * // => {x: 0, y: 200}
  1493. */
  1494. function getScrollPos(el) {
  1495. if ( el === void 0 ) el = window;
  1496. return ({
  1497. x: (el.pageXOffset !== undefined) ? el.pageXOffset : el.scrollLeft,
  1498. y: (el.pageYOffset !== undefined) ? el.pageYOffset : el.scrollTop
  1499. });
  1500. }
  1501. /**
  1502. * 获取数组的最后一项
  1503. *
  1504. * @since 1.2.1
  1505. * @param array
  1506. * @returns {boolean}
  1507. * @example
  1508. *
  1509. * last(['1,2,3']);
  1510. * // => '3';
  1511. */
  1512. function last(array) {
  1513. return Array.isArray(array) && array.slice(-1)[0];
  1514. }
  1515. /**
  1516. * 测试函数所花费的时间
  1517. *
  1518. * @since 1.2.1
  1519. * @param callback
  1520. * @returns {*}
  1521. * @example
  1522. *
  1523. * timeTaken(() => Math.pow(2, 10));
  1524. * // => 1024
  1525. */
  1526. function timeTaken(callback) {
  1527. if (typeof callback !== 'function') { throw new Error('callback 必须为可执行的函数'); }
  1528. console.time('timeTaken');
  1529. var r = callback();
  1530. console.timeEnd('timeTaken');
  1531. return r;
  1532. }
  1533. /**
  1534. * 数组转换为键值对的对象
  1535. *
  1536. * @since 1.2.1
  1537. * @param array
  1538. * @returns {*}
  1539. * @example
  1540. *
  1541. * objectFromPairs([['a',1],['b',2]]);
  1542. * // => {a: 1, b: 2}
  1543. */
  1544. function objectFromPairs(array) {
  1545. return Array.isArray(array) && array.reduce(function (a, v) { return (a[v[0]] = v[1], a); }, {});
  1546. }
  1547. /**
  1548. * 对象转化为键值对
  1549. * 使用 Object.keys() 和 Array.map() 遍历对象的键并生成一个包含键值对的数组。
  1550. *
  1551. * @param obj
  1552. * @returns {any[][]}
  1553. * @example
  1554. *
  1555. * objectToPairs({ a: 1, b: 2 });
  1556. * // => [['a',1],['b',2]]
  1557. */
  1558. var objectToPairs = function (obj) { return Object.keys(obj).map(function (k) { return [k, obj[k]]; }); };
  1559. /**
  1560. * 滚动到顶部
  1561. * 使用document.documentElement.scrollTop或document.body.scrollTop获取到顶部的距离。从顶部滚动一小部分距离。
  1562. 使用window.requestAnimationFrame()来滚动。
  1563. *
  1564. * @since 1.2.1
  1565. * @example
  1566. *
  1567. * scrollToTop();
  1568. */
  1569. function scrollToTop() {
  1570. var c = document.documentElement.scrollTop || document.body.scrollTop;
  1571. if (c > 0) {
  1572. window.requestAnimationFrame(scrollToTop);
  1573. window.scrollTo(0, c - c / 8);
  1574. }
  1575. }
  1576. /**
  1577. * 是否为中文
  1578. *
  1579. * @since 1.4.7
  1580. * @param {string} str
  1581. * @returns {boolean}
  1582. * @example
  1583. *
  1584. * isChinese('中文');
  1585. * // => true
  1586. */
  1587. function isChinese(str) {
  1588. var reg = /^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/;
  1589. return reg.test(str);
  1590. }
  1591. /**
  1592. * 是否为 HTML 标签
  1593. *
  1594. * @since 1.2.4
  1595. * @param {string} str
  1596. * @returns {boolean}
  1597. * @example
  1598. *
  1599. * isHTML('<p>123</p>');
  1600. * // => true
  1601. */
  1602. function isHTML(str) {
  1603. var reg = /<("[^"]*"|'[^']*'|[^'">])*>/;
  1604. return reg.test(str);
  1605. }
  1606. /**
  1607. * 返回指定长度的月份集合
  1608. * 摘自:https://segmentfault.com/a/1190000013041329
  1609. *
  1610. * @param {time} 时间
  1611. * @param {len} 长度
  1612. * @param {direction} 方向: 1: 前几个月; 2: 后几个月; 3:前后几个月 默认 3
  1613. * @return {Array} 数组
  1614. * @example
  1615. *
  1616. * getMonths('2018-1-29', 6, 1)
  1617. * // => ["2018-1", "2017-12", "2017-11", "2017-10", "2017-9", "2017-8", "2017-7"]
  1618. */
  1619. function getMonths(time, len, direction) {
  1620. var mm = new Date(time).getMonth();
  1621. var yy = new Date(time).getFullYear();
  1622. var index = mm;
  1623. direction = isNaN(direction) ? 3 : direction;
  1624. var cutMonth = function (index) {
  1625. if (index <= len && index >= -len) {
  1626. return direction === 1 ? formatPre(index).concat(cutMonth(++index)) :
  1627. direction === 2 ? formatNext(index).concat(cutMonth(++index)) : formatCurr(index).concat(cutMonth(++index));
  1628. }
  1629. return [];
  1630. };
  1631. var formatNext = function (i) {
  1632. var y = Math.floor(i / 12);
  1633. var m = i % 12;
  1634. return [yy + y + '-' + (m + 1)];
  1635. };
  1636. var formatPre = function (i) {
  1637. var y = Math.ceil(i / 12);
  1638. var m = i % 12;
  1639. m = m === 0 ? 12 : m;
  1640. return [yy - y + '-' + (13 - m)];
  1641. };
  1642. var formatCurr = function (i) {
  1643. var y = Math.floor(i / 12);
  1644. var yNext = Math.ceil(i / 12);
  1645. var m = i % 12;
  1646. var mNext = m === 0 ? 12 : m;
  1647. return [yy - yNext + '-' + (13 - mNext), yy + y + '-' + (m + 1)];
  1648. };
  1649. // 数组去重
  1650. var unique = function (arr) {
  1651. if (Array.hasOwnProperty('from')) {
  1652. return Array.from(new Set(arr));
  1653. } else {
  1654. var n = {}, r = [];
  1655. for (var i = 0; i < arr.length; i++) {
  1656. if (!n[arr[i]]) {
  1657. n[arr[i]] = true;
  1658. r.push(arr[i]);
  1659. }
  1660. }
  1661. return r;
  1662. }
  1663. };
  1664. return direction !== 3 ? cutMonth(index) : unique(cutMonth(index).sort(function (t1, t2) {
  1665. return new Date(t1).getTime() - new Date(t2).getTime();
  1666. }));
  1667. }
  1668. /**
  1669. * 获取某月有多少天
  1670. * 摘自:https://segmentfault.com/a/1190000013041329
  1671. *
  1672. * @param {time} 时间
  1673. * @return {number} 天数
  1674. * @example
  1675. *
  1676. * getMonthOfDay('2018-1-29')
  1677. * // => 31
  1678. */
  1679. function getMonthOfDay(time) {
  1680. var date = new Date(time);
  1681. var year = date.getFullYear();
  1682. var mouth = date.getMonth() + 1;
  1683. var days;
  1684. //当月份为二月时,根据闰年还是非闰年判断天数
  1685. if (mouth == 2) {
  1686. days = (year % 4 == 0 && year % 100 == 0 && year % 400 == 0) || (year % 4 == 0 && year % 100 != 0) ? 28 : 29;
  1687. } else if (mouth == 1 || mouth == 3 || mouth == 5 || mouth == 7 || mouth == 8 || mouth == 10 || mouth == 12) {
  1688. //月份为:1,3,5,7,8,10,12 时,为大月.则天数为31;
  1689. days = 31;
  1690. } else {
  1691. //其他月份,天数为:30.
  1692. days = 30;
  1693. }
  1694. return days;
  1695. }
  1696. /**
  1697. * 返回指定长度的天数集合
  1698. * 摘自:https://segmentfault.com/a/1190000013041329
  1699. *
  1700. * @param {time} 时间
  1701. * @param {len} 长度
  1702. * @param {direction} 方向: 1: 前几天; 2: 后几天; 3:前后几天 默认 3
  1703. * @return {Array} 数组
  1704. * @example
  1705. *
  1706. * getDays('2018-1-29', 6, 1)
  1707. * // => ["2018-1-26", "2018-1-27", "2018-1-28", "2018-1-29", "2018-1-30", "2018-1-31", "2018-2-1"]
  1708. */
  1709. function getDays(time, len, direction) {
  1710. var tt = new Date(time);
  1711. var getDay = function (day) {
  1712. var t = new Date(time);
  1713. t.setDate(t.getDate() + day);
  1714. var m = t.getMonth() + 1;
  1715. return t.getFullYear() + '-' + m + '-' + t.getDate();
  1716. };
  1717. var arr = [];
  1718. if (direction === 1) {
  1719. for (var i = 1; i <= len; i++) {
  1720. arr.unshift(getDay(-i));
  1721. }
  1722. } else if (direction === 2) {
  1723. for (var i$1 = 1; i$1 <= len; i$1++) {
  1724. arr.push(getDay(i$1));
  1725. }
  1726. } else {
  1727. for (var i$2 = 1; i$2 <= len; i$2++) {
  1728. arr.unshift(getDay(-i$2));
  1729. }
  1730. arr.push(tt.getFullYear() + '-' + (tt.getMonth() + 1) + '-' + tt.getDate());
  1731. for (var i$3 = 1; i$3 <= len; i$3++) {
  1732. arr.push(getDay(i$3));
  1733. }
  1734. }
  1735. return direction === 1 ? arr.concat([tt.getFullYear() + '-' + (tt.getMonth() + 1) + '-' + tt.getDate()]) :
  1736. direction === 2 ? [tt.getFullYear() + '-' + (tt.getMonth() + 1) + '-' + tt.getDate()].concat(arr) : arr;
  1737. }
  1738. /**
  1739. * 获取某个日期是当年中的第几天
  1740. *
  1741. * @since 1.2.4
  1742. * @param time
  1743. * @returns {number}
  1744. * @example
  1745. *
  1746. * getDayOfYear('2014-01-10')
  1747. * => 10
  1748. */
  1749. function getDayOfYear(time) {
  1750. var firstDayYear = getFirstDayOfYear(time);
  1751. var numSecond = (new Date(time).getTime() - new Date(firstDayYear).getTime()) / 1000;
  1752. return Math.ceil(numSecond / (24 * 3600));
  1753. }
  1754. // 获取某年的第一天
  1755. function getFirstDayOfYear(time) {
  1756. var year = new Date(time).getFullYear();
  1757. return year + '-01-01 00:00:00';
  1758. }
  1759. /**
  1760. * 获取某个日期在这一年的第几周
  1761. *
  1762. * @since 1.2.4
  1763. * @param time
  1764. * @returns {number}
  1765. * @example
  1766. *
  1767. * getDayOfYearWeek('2014-01-10')
  1768. * => 2
  1769. */
  1770. function getDayOfYearWeek(time) {
  1771. var numDays = getDayOfYear(time);
  1772. return Math.ceil(numDays / 7);
  1773. }
  1774. /**
  1775. * 获取某年有多少天
  1776. *
  1777. * @since 1.2.4
  1778. * @param time
  1779. * @returns {number}
  1780. * @example
  1781. *
  1782. * getYearOfDay('2014')
  1783. * => 365
  1784. */
  1785. function getYearOfDay(time) {
  1786. var firstDayYear = getFirstDayOfYear$1(time);
  1787. var lastDayYear = getLastDayOfYear(time);
  1788. var numSecond = (new Date(lastDayYear).getTime() - new Date(firstDayYear).getTime()) / 1000;
  1789. return Math.ceil(numSecond / (24 * 3600));
  1790. }
  1791. // 获取某年的第一天
  1792. function getFirstDayOfYear$1(time) {
  1793. var year = new Date(time).getFullYear();
  1794. return year + '-01-01 00:00:00';
  1795. }
  1796. // 获取某年最后一天
  1797. function getLastDayOfYear(time) {
  1798. var year = new Date(time).getFullYear();
  1799. var dateString = year + '-12-01 00:00:00';
  1800. var endDay = getMonthOfDay(dateString);
  1801. return year + '-12-' + endDay + ' 23:59:59';
  1802. }
  1803. /**
  1804. * 图片压缩
  1805. * @param {string} file [压缩文件]
  1806. * @param {object} obj [压缩参数]
  1807. * @param {function} cb [回调函数]
  1808. * @return {string} [返回压缩前和压缩后的格式]
  1809. */
  1810. /* istanbul ignore next */
  1811. function photoCompress(file, obj, cb) {
  1812. // obj = {
  1813. // width: 图片宽,
  1814. // height: 图片高,
  1815. // quality: 图像质量,
  1816. // blob: 是否转换成Blob
  1817. // }
  1818. // 将以base64的图片url数据转换为Blob
  1819. function convertBase64UrlToBlob(urlData) {
  1820. var arr = urlData.split(',');
  1821. var mime = arr[0].match(/:(.*?);/)[1];
  1822. var bstr = atob(arr[1]);
  1823. var n = bstr.length, u8arr = new Uint8Array(n);
  1824. while (n--) {
  1825. u8arr[n] = bstr.charCodeAt(n);
  1826. }
  1827. return new Blob([u8arr], {type: mime});
  1828. }
  1829. // canvas 绘制图片
  1830. function canvasDataURL(oldBase64) {
  1831. var img = new Image();
  1832. img.src = oldBase64;
  1833. img.onload = function () {
  1834. var that = this;
  1835. // 默认按比例压缩
  1836. var w = that.width,
  1837. h = that.height,
  1838. scale = w / h;
  1839. w = obj.width || w;
  1840. h = obj.height || (w / scale);
  1841. var quality = 0.7; // 默认图片质量为0.7
  1842. //生成canvas
  1843. var canvas = document.createElement('canvas');
  1844. var ctx = canvas.getContext('2d');
  1845. // 创建属性节点
  1846. var anw = document.createAttribute('width');
  1847. anw.nodeValue = w;
  1848. var anh = document.createAttribute('height');
  1849. anh.nodeValue = h;
  1850. canvas.setAttributeNode(anw);
  1851. canvas.setAttributeNode(anh);
  1852. ctx.drawImage(that, 0, 0, w, h);
  1853. // 图像质量
  1854. if (obj.quality && obj.quality <= 1 && obj.quality > 0) {
  1855. quality = obj.quality;
  1856. }
  1857. // quality值越小,所绘制出的图像越模糊
  1858. var base64 = canvas.toDataURL('image/jpeg', quality);
  1859. // 回调函数返回base64的值
  1860. if (obj.blob) {
  1861. cb && cb(convertBase64UrlToBlob(base64), convertBase64UrlToBlob(oldBase64));
  1862. } else {
  1863. cb && cb(base64, oldBase64);
  1864. }
  1865. };
  1866. }
  1867. // 读取图片的base64格式
  1868. var ready = new FileReader();
  1869. ready.readAsDataURL(file);
  1870. ready.onload = function () {
  1871. var re = this.result;
  1872. canvasDataURL(re);
  1873. };
  1874. }
  1875. /**
  1876. * 数字金额大写转换
  1877. *
  1878. * @since 1.2.5
  1879. * @param n
  1880. * @returns {string}
  1881. * @example
  1882. *
  1883. * changeMoneyToChinese(100111);
  1884. * => "壹拾万零壹佰壹拾壹元整"
  1885. *
  1886. * changeMoneyToChinese(7.52);
  1887. * => "柒元伍角贰分"
  1888. *
  1889. * changeMoneyToChinese(951434677682.00);
  1890. * => "玖仟伍佰壹拾肆亿叁仟肆佰陆拾柒万柒仟陆佰捌拾贰元整"
  1891. */
  1892. function changeMoneyToChinese(n) {
  1893. if(isNaN(n)) {
  1894. console.warn("数据类型为 number");
  1895. return;
  1896. }
  1897. var fraction = ['角', '分'];
  1898. var digit = [
  1899. '零', '壹', '贰', '叁', '肆',
  1900. '伍', '陆', '柒', '捌', '玖'
  1901. ];
  1902. var unit = [
  1903. ['元', '万', '亿'],
  1904. ['', '拾', '佰', '仟']
  1905. ];
  1906. var head = n < 0 ? '欠' : '';
  1907. n = Math.abs(n);
  1908. var s = '';
  1909. for (var i = 0; i < fraction.length; i++) {
  1910. s += (digit[Math.floor(shiftRight(n, 1 + i)) % 10] + fraction[i]).replace(/零./, '');
  1911. }
  1912. s = s || '整';
  1913. n = Math.floor(n);
  1914. for (var i$1 = 0; i$1 < unit[0].length && n > 0; i$1++) {
  1915. var p = '';
  1916. for (var j = 0; j < unit[1].length && n > 0; j++) {
  1917. p = digit[n % 10] + unit[1][j] + p;
  1918. n = Math.floor(shiftLeft(n, 1));
  1919. }
  1920. s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i$1] + s;
  1921. }
  1922. return head + s.replace(/(零.)*零元/, '元')
  1923. .replace(/(零.)+/g, '零')
  1924. .replace(/^整$/, '零元整');
  1925. }
  1926. // 向右移位
  1927. function shiftRight(number, digit) {
  1928. digit = parseInt(digit, 10);
  1929. var value = number.toString().split('e');
  1930. return +(value[0] + 'e' + (value[1] ? (+value[1] + digit) : digit))
  1931. }
  1932. // 向左移位
  1933. function shiftLeft(number, digit) {
  1934. digit = parseInt(digit, 10);
  1935. var value = number.toString().split('e');
  1936. return +(value[0] + 'e' + (value[1] ? (+value[1] - digit) : -digit))
  1937. }
  1938. /**
  1939. * 数字转换成中文的大写数字
  1940. *
  1941. * @since 1.2.5
  1942. * @param num
  1943. * @returns {string}
  1944. * @example
  1945. *
  1946. * numberToChinese(10001010);
  1947. * => "一千万一千一十"
  1948. */
  1949. function numberToChinese(num) {
  1950. var AA = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
  1951. var BB = ['', '十', '百', '千', '万', '亿', '点', ''];
  1952. var a = ('' + num).replace(/(^0*)/g, '').split('.'),
  1953. k = 0,
  1954. re = '';
  1955. for (var i = a[0].length - 1; i >= 0; i--) {
  1956. switch (k) {
  1957. case 0:
  1958. re = BB[7] + re;
  1959. break;
  1960. case 4:
  1961. if (!new RegExp('0{4}//d{' + (a[0].length - i - 1) + '}$')
  1962. .test(a[0]))
  1963. { re = BB[4] + re; }
  1964. break;
  1965. case 8:
  1966. re = BB[5] + re;
  1967. BB[7] = BB[5];
  1968. k = 0;
  1969. break;
  1970. }
  1971. if (k % 4 == 2 && a[0].charAt(i + 2) != 0 && a[0].charAt(i + 1) == 0)
  1972. { re = AA[0] + re; }
  1973. if (a[0].charAt(i) != 0)
  1974. { re = AA[a[0].charAt(i)] + BB[k % 4] + re; }
  1975. k++;
  1976. }
  1977. if (a.length > 1) // 加上小数部分(如果有小数部分)
  1978. {
  1979. re += BB[6];
  1980. for (var i$1 = 0; i$1 < a[1].length; i$1++)
  1981. { re += AA[a[1].charAt(i$1)]; }
  1982. }
  1983. if (re == '一十')
  1984. { re = '十'; }
  1985. if (re.match(/^一/) && re.length == 3)
  1986. { re = re.replace('一', ''); }
  1987. return re;
  1988. }
  1989. /**
  1990. * 科学计数法转化为数值字符串形式
  1991. *
  1992. * @param {number} num
  1993. * @returns {string}
  1994. * @example
  1995. *
  1996. * toNonExponential(3.3e-7);
  1997. * => // "0.00000033"
  1998. *
  1999. * toNonExponential(3e-7);
  2000. * => // "0.0000003"
  2001. *
  2002. * toNonExponential(1.401e10);
  2003. * => // "14010000000"
  2004. *
  2005. * toNonExponential(0.0004);
  2006. * => // "0.0004"
  2007. */
  2008. function toNonExponential(num) {
  2009. if (typeof num !== 'number') { throw new TypeError('数据类型必须是 number'); }
  2010. var m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/);
  2011. return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));
  2012. }
  2013. /**
  2014. * 获取网址参数
  2015. * @param {string} url
  2016. * @returns {{}} 返回包含当前URL参数的对象。
  2017. * @example
  2018. *
  2019. * getURLParameters('http://url.com/page?name=Adam&surname=Smith');
  2020. * => // {name: 'Adam', surname: 'Smith'}
  2021. */
  2022. function getURLParameters(url) {
  2023. if ( url === void 0 ) url = window.location.href;
  2024. if (typeof url !== 'string') { throw new TypeError('数据类型必须是 string'); }
  2025. return url
  2026. .match(/([^?=&]+)(=([^&]*))/g)
  2027. .reduce(function (a, v) { return (a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1), a); }, {});
  2028. }
  2029. /**
  2030. * 根据提供函数返回的值映射一个新对象
  2031. *
  2032. * @param obj
  2033. * @param fn
  2034. * @returns {{}}
  2035. * @example
  2036. *
  2037. * const users = {
  2038. fred: { user: 'fred', age: 40 },
  2039. pebbles: { user: 'pebbles', age: 1 }
  2040. };
  2041. * mapValues(users, u => u.age);
  2042. * // => { fred: 40, pebbles: 1 }
  2043. */
  2044. var mapValues = function (obj, fn) { return Object.keys(obj).reduce(function (acc, k) {
  2045. acc[k] = fn(obj[k], k, obj);
  2046. return acc;
  2047. }, {}); };
  2048. /**
  2049. * 根据提供函数生成的键生成一个新对象
  2050. * 使用 Object.keys(obj) 来迭代对象的键。 使用 Array.reduce() 创建一个具有相同值的新对象,并使用 fn 来映射键。
  2051. *
  2052. * @param obj
  2053. * @param fn
  2054. * @returns {{}}
  2055. * @example
  2056. *
  2057. * mapKeys({ a: 1, b: 2 }, (val, key) => key + val);
  2058. * // => { a1: 1, b2: 2 }
  2059. */
  2060. var mapKeys = function (obj, fn) { return Object.keys(obj).reduce(function (acc, k) {
  2061. acc[fn(obj[k], k, obj)] = obj[k];
  2062. return acc;
  2063. }, {}); };
  2064. /**
  2065. * 反转对象的键值对
  2066. * 而不会改变它。使用 Object.keys() 和 Array.reduce() 来反转对象的键值对。
  2067. *
  2068. * @param obj
  2069. * @returns {{}}
  2070. * @example
  2071. *
  2072. * invertKeyValues({ name: 'John', age: 20 });
  2073. * // => { 20: 'age', John: 'name' }
  2074. */
  2075. var invertKeyValues = function (obj) { return Object.keys(obj).reduce(function (acc, key) {
  2076. acc[obj[key]] = key;
  2077. return acc;
  2078. }, {}); };
  2079. /**
  2080. * 获取数组,对象或字符串的大小。
  2081. * Get type of val (array, object or string). Use length property for arrays. Use length or size value if available or number of keys for objects. Use size of a Blob object created from val for strings.
  2082. 获取 val (array,object 或 string)的类型。 对于数组使用 length 属性。 对于对象,使用 length 或 size 如果可用的话,或者对象的键的数量。 对于字符串,使用根据 val 创建的Blob对象 的 size。
  2083. 通过 split('') 将字符串拆分成字符数组并返回其长度。
  2084. * @param val
  2085. * @returns {*}
  2086. * @example
  2087. *
  2088. * size([1, 2, 3, 4, 5]);
  2089. * // => 5
  2090. *
  2091. * size('size');
  2092. * // => 4
  2093. *
  2094. * size({ one: 1, two: 2, three: 3 });
  2095. * // => 3
  2096. */
  2097. var size = function (val) { return Array.isArray(val)
  2098. ? val.length
  2099. : val && typeof val === 'object'
  2100. ? val.size || val.length || Object.keys(val).length
  2101. : typeof val === 'string' ? new Blob([val]).size : 0; };
  2102. /**
  2103. * 清除空格
  2104. *
  2105. * @param str
  2106. * @param type 1-所有空格 2-前后空格 3-前空格 4-后空格
  2107. * @returns {*}
  2108. * @example
  2109. *
  2110. * trim(' 123 ');
  2111. * // => 123
  2112. */
  2113. function trim$1(str, type) {
  2114. if ( type === void 0 ) type = 1;
  2115. if (typeof str !== 'string') { throw new Error('输入值必须为字符串'); }
  2116. switch (type) {
  2117. case 1:
  2118. return str.replace(/\s+/g, '');
  2119. case 2:
  2120. return str.replace(/(^\s*)|(\s*$)/g, '');
  2121. case 3:
  2122. return str.replace(/(^\s*)/g, '');
  2123. case 4:
  2124. return str.replace(/(\s*$)/g, '');
  2125. default:
  2126. return str;
  2127. }
  2128. }
  2129. /**
  2130. * toZhCN 把字符串转成以分为单位的整数。
  2131. *
  2132. * @param {number|string} num 金额
  2133. * @returns {string} 中文大写的金额, 标准会计格式
  2134. * @example
  2135. *
  2136. * toZhCN(500.3);
  2137. * // => 伍佰元叁角整
  2138. */
  2139. function toZhCN(num) {
  2140. if (typeof num === 'number') {
  2141. num = String(num);
  2142. }
  2143. if (!/^(0|[1-9]\d*)(\.\d+)?$/.test(num)) {
  2144. throw new Error(("非法数据: " + (JSON.stringify(num))));
  2145. }
  2146. var unit = '京亿万仟佰拾兆万仟佰拾亿仟佰拾万仟佰拾元角分';
  2147. var str = '';
  2148. num += '00';
  2149. var pos = num.indexOf('.');
  2150. if (pos >= 0) {
  2151. num = num.substring(0, pos) + num.substr(pos + 1, 2);
  2152. }
  2153. unit = unit.substr(unit.length - num.length);
  2154. for (var i = 0, len = num.length; i < len; i++) {
  2155. str +=
  2156. '零壹贰叁肆伍陆柒捌玖'.charAt(Number(num.charAt(i))) + unit.charAt(i);
  2157. }
  2158. return str
  2159. .replace(/零(仟|佰|拾|角)/g, '零')
  2160. .replace(/(零)+/g, '零')
  2161. .replace(/零(兆|万|亿|元)/g, '$1')
  2162. .replace(/(兆|亿)万/g, '$1')
  2163. .replace(/(京|兆)亿/g, '$1')
  2164. .replace(/(京)兆/g, '$1')
  2165. .replace(/(京|兆|亿|仟|佰|拾)(万?)(.)仟/g, '$1$2零$3仟')
  2166. .replace(/^元零?|零分/g, '')
  2167. .replace(/(元|角)$/g, '$1整');
  2168. }
  2169. /**
  2170. * 深层克隆对象
  2171. *
  2172. * @param obj
  2173. * @returns {*}
  2174. * @example
  2175. *
  2176. * const a = { foo: 'bar', obj: { a: 1, b: 2 } };
  2177. * const b = deepClone(a);
  2178. * // => a !== b, a.obj !== b.obj
  2179. */
  2180. function deepClone(obj) {
  2181. var clone = Object.assign({}, obj);
  2182. Object.keys(clone).forEach(
  2183. function (key) { return (clone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]); }
  2184. );
  2185. return Array.isArray(obj) && obj.length
  2186. ? (clone.length = obj.length) && Array.from(clone)
  2187. : Array.isArray(obj)
  2188. ? Array.from(obj)
  2189. : clone;
  2190. }
  2191. /**
  2192. * 深层映射对象键
  2193. *
  2194. * @param obj
  2195. * @param fn
  2196. * @returns {{}}
  2197. * @example
  2198. *
  2199. * const obj = {
  2200. foo: '1',
  2201. nested: {
  2202. child: {
  2203. withArray: [
  2204. {
  2205. grandChild: ['hello']
  2206. }
  2207. ]
  2208. }
  2209. }
  2210. };
  2211. const upperKeysObj = deepMapKeys(obj, key => key.toUpperCase());
  2212. // =>
  2213. {
  2214. "FOO":"1",
  2215. "NESTED":{
  2216. "CHILD":{
  2217. "WITHARRAY":[
  2218. {
  2219. "GRANDCHILD":[ 'hello' ]
  2220. }
  2221. ]
  2222. }
  2223. }
  2224. }
  2225. */
  2226. function deepMapKeys(obj, fn) {
  2227. return Array.isArray(obj)
  2228. ? obj.map(function (val) { return deepMapKeys(val, fn); })
  2229. : typeof obj === 'object'
  2230. ? Object.keys(obj).reduce(function (acc, current) {
  2231. var val = obj[current];
  2232. acc[fn(current)] =
  2233. val !== null && typeof val === 'object' ? deepMapKeys(val, fn) : (acc[fn(current)] = val);
  2234. return acc;
  2235. }, {})
  2236. : obj;
  2237. }
  2238. /**
  2239. * 从对象中检索给定选择器指示的一组属性
  2240. *
  2241. * {@link https://30secondsofcode.org/object#get}
  2242. * @param from
  2243. * @param selectors
  2244. * @returns {string[]}
  2245. * @example
  2246. *
  2247. * const obj = { selector: { to: { val: 'val to select' } }, target: [1, 2, { a: 'test' }] };
  2248. * get(obj, 'selector.to.val', 'target[0]', 'target[2].a');
  2249. * // => ['val to select', 1, 'test']
  2250. */
  2251. function get(from) {
  2252. var selectors = [], len = arguments.length - 1;
  2253. while ( len-- > 0 ) selectors[ len ] = arguments[ len + 1 ];
  2254. return [].concat( selectors ).map(function (s) { return s
  2255. .replace(/\[([^\[\]]*)\]/g, '.$1.')
  2256. .split('.')
  2257. .filter(function (t) { return t !== ''; })
  2258. .reduce(function (prev, cur) { return prev && prev[cur]; }, from); }
  2259. );
  2260. }
  2261. /**
  2262. * 基于给定的键返回嵌套JSON对象中的目标值
  2263. *
  2264. * {@link https://30secondsofcode.org/object#dig}
  2265. * @param obj
  2266. * @param target
  2267. * @returns {any}
  2268. * @example
  2269. *
  2270. * const data = {
  2271. * level1: {
  2272. * level2: {
  2273. * level3: 'some data'
  2274. * }
  2275. * }
  2276. * };
  2277. * dig(data, 'level3');
  2278. * // => 'some data'
  2279. * dig(data, 'level4');
  2280. * // => undefined
  2281. */
  2282. function dig(obj, target) {
  2283. return target in obj
  2284. ? obj[target]
  2285. : Object.values(obj).reduce(function (acc, val) {
  2286. if (acc !== undefined) { return acc; }
  2287. if (typeof val === 'object') { return dig(val, target); }
  2288. }, undefined);
  2289. }
  2290. /**
  2291. * 是否为空
  2292. * 如果a值是空对象,集合,没有可枚举属性或任何不被视为集合的类型,则返回true。
  2293. *
  2294. * {@link https://30secondsofcode.org/type#isempty}
  2295. * @param val
  2296. * @returns {boolean}
  2297. * @example
  2298. *
  2299. * isEmpty([]);
  2300. * // => true
  2301. * isEmpty({});
  2302. * // => true
  2303. * isEmpty('');
  2304. * // => true
  2305. * isEmpty([1, 2]);
  2306. * // => false
  2307. * isEmpty({ a: 1, b: 2 });
  2308. * // => false
  2309. * isEmpty('text');
  2310. * // => false
  2311. * isEmpty(123);
  2312. * // => true - type is not considered a collection
  2313. * isEmpty(true);
  2314. * // => true - type is not considered a collection
  2315. */
  2316. function isEmpty(val) {
  2317. return val == null || !(Object.keys(val) || val).length;
  2318. }
  2319. /**
  2320. * 从两个或多个对象的组合中创建一个新对象
  2321. *
  2322. * {@link https://30secondsofcode.org/object#merge}
  2323. * @param objs
  2324. * @returns {*}
  2325. * @example
  2326. *
  2327. * merge(
  2328. {
  2329. a: [{ x: 2 }, { y: 4 }],
  2330. b: 1
  2331. },
  2332. {
  2333. a: { z: 3 },
  2334. b: [2, 3],
  2335. c: 'foo'
  2336. });
  2337. * // => { a: [ { x: 2 }, { y: 4 }, { z: 3 } ], b: [ 1, 2, 3 ], c: 'foo' }
  2338. */
  2339. function merge() {
  2340. var objs = [], len = arguments.length;
  2341. while ( len-- ) objs[ len ] = arguments[ len ];
  2342. return [].concat( objs ).reduce(
  2343. function (acc, obj) { return Object.keys(obj).reduce(function (a, k) {
  2344. acc[k] = acc.hasOwnProperty(k) ? [].concat(acc[k]).concat(obj[k]) : obj[k];
  2345. return acc;
  2346. }, {}); },
  2347. {}
  2348. );
  2349. }
  2350. /**
  2351. * 返回对象的白名单属性
  2352. *
  2353. * {@link https://github.com/tj/node-only}
  2354. * @param obj
  2355. * @param keys
  2356. * @returns {*}
  2357. * @example
  2358. *
  2359. * var obj = {
  2360. name: 'tobi',
  2361. last: 'holowaychuk',
  2362. email: 'tobi@learnboost.com',
  2363. _id: '12345'
  2364. };
  2365. * var user = only(obj, 'name last email');
  2366. * // => {
  2367. name: 'tobi',
  2368. last: 'holowaychuk',
  2369. email: 'tobi@learnboost.com'
  2370. }
  2371. */
  2372. function only(obj, keys) {
  2373. obj = obj || {};
  2374. if ('string' == typeof keys) { keys = keys.split(/ +/); }
  2375. return keys.reduce(function(ret, key){
  2376. if (null == obj[key]) { return ret; }
  2377. ret[key] = obj[key];
  2378. return ret;
  2379. }, {});
  2380. }
  2381. /**
  2382. * 是否为 Light OS(容器)
  2383. *
  2384. * @example
  2385. *
  2386. * isLightOS();
  2387. * // => true
  2388. */
  2389. function isLightOS() {
  2390. return window.navigator.userAgent.toLowerCase().indexOf('lightos') !== -1;
  2391. }
  2392. /**
  2393. * 兼容 lightOS 离线包及 tzyj 在线
  2394. * @param method {string}
  2395. * @param params {object}
  2396. * @param cb {function}
  2397. * @description
  2398. * 分为两种情况<br>
  2399. * 在线及离线<br>
  2400. * 在线的情况下,H5应用是运行在 tzyj 的webview里,目前 navigator.userAgent 并没有 lightos,所以只能用tzyj自身注入的对象 window.winner<br>
  2401. * 离线包情况下,H5应用是运行在 light 的容器里。因此就可以利用 light 本身提供的SDK,即使 tzyj 自身封装扩展的方法,如获取自选股等,是基于 light 的容器,也需要改造
  2402. *
  2403. */
  2404. function nativeJSBridge(ref) {
  2405. var method = ref.method; if ( method === void 0 ) method = '';
  2406. var params = ref.params; if ( params === void 0 ) params = null;
  2407. var cb = ref.cb; if ( cb === void 0 ) cb = null;
  2408. if (isLightOS()) {
  2409. // tzyj 在 light 的 基类上自行扩展封装的方法
  2410. if (method === 'winner.hsJsFunction') {
  2411. window.LightJSBridge && window.LightJSBridge.call(method, params, function (result) {
  2412. var ret = null;
  2413. if (result.info && result.info.error_message === 'success') {
  2414. ret = result.data;
  2415. }
  2416. typeof cb === 'function' && cb(ret);
  2417. });
  2418. } else {
  2419. // LightSDK
  2420. window.LightSDK && window.LightSDK.native[method](params, function (result) {
  2421. var ret = null;
  2422. if (result.info && result.info.error_message === 'success') {
  2423. ret = result.data;
  2424. }
  2425. typeof cb === 'function' && cb(ret);
  2426. });
  2427. }
  2428. } else {
  2429. // 非 Light 容器,
  2430. // 安卓和IOS的在线版本
  2431. if (typeof window.winner === 'object') {
  2432. window.winner.hsJsFunction(JSON.stringify(params));
  2433. }
  2434. }
  2435. }
  2436. /**
  2437. * 当 H5 页面完全展示之前需要和 native 先进行交互,此用来控制时序。只有 App 调用了 ready(deviceready,hsAppReady) 方法后,表示 App 端已经准备完毕,已注入了相关 js 对象。防止 App 还没注入 js 对象方法,H5 过早调用 App 提供的方法
  2438. * 兼容 lightOS 离线包及 tzyj 在线
  2439. * @param cb {function}
  2440. * @param type {string} 可选,默认为 light
  2441. */
  2442. function nativeReady(cb, type) {
  2443. if ( type === void 0 ) type = 'light';
  2444. if (isLightOS()) {
  2445. document.addEventListener('deviceready', function () {
  2446. typeof cb === 'function' && cb();
  2447. });
  2448. } else {
  2449. if (type === 'winner') {
  2450. document.addEventListener('hsAppReady', function () {
  2451. typeof cb === 'function' && cb();
  2452. });
  2453. }
  2454. typeof cb === 'function' && cb();
  2455. }
  2456. }
  2457. /**
  2458. * Creates a new URL by combining the specified URLs
  2459. *
  2460. * @param {string} baseURL The base URL
  2461. * @param {string} relativeURL The relative URL
  2462. * @returns {string} The combined URL
  2463. */
  2464. function combineURLs(baseURL, relativeURL) {
  2465. return relativeURL
  2466. ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
  2467. : baseURL;
  2468. }
  2469. /**
  2470. * 是否是微博内核
  2471. *
  2472. * @returns {boolean}
  2473. * @example
  2474. *
  2475. * inWeibo();
  2476. * // => false
  2477. */
  2478. function inWeibo() {
  2479. if (typeof navigator === 'undefined') { return; }
  2480. var ua = navigator.userAgent.toLowerCase();
  2481. return ua.indexOf('weibo') !== -1;
  2482. }
  2483. /**
  2484. * 动态加载 script
  2485. *
  2486. * @param {string} src
  2487. * @param {function} callback
  2488. * @example
  2489. *
  2490. * dynamicLoadScript('https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js', () => {
  2491. * console.log('加载成功');
  2492. * })
  2493. * // => 加载成功
  2494. *
  2495. */
  2496. function dynamicLoadScript(src, callback) {
  2497. var existingScript = document.getElementById(src);
  2498. var cb = callback || function () {
  2499. };
  2500. if (!existingScript) {
  2501. var script = document.createElement('script');
  2502. script.src = src; // src url for the third-party library being loaded.
  2503. script.id = src;
  2504. document.body.appendChild(script);
  2505. var onEnd = 'onload' in script ? stdOnEnd : ieOnEnd;
  2506. onEnd(script, cb);
  2507. }
  2508. if (existingScript && cb) { cb(null, existingScript); }
  2509. function stdOnEnd(script, cb) {
  2510. script.onload = function () {
  2511. // this.onload = null here is necessary
  2512. // because even IE9 works not like others
  2513. this.onerror = this.onload = null;
  2514. cb(null, script);
  2515. };
  2516. script.onerror = function () {
  2517. this.onerror = this.onload = null;
  2518. cb(new Error('Failed to load ' + src), script);
  2519. };
  2520. }
  2521. function ieOnEnd(script, cb) {
  2522. script.onreadystatechange = function () {
  2523. if (this.readyState !== 'complete' && this.readyState !== 'loaded') { return; }
  2524. this.onreadystatechange = null;
  2525. cb(null, script); // there is no way to catch loading errors in IE8
  2526. };
  2527. }
  2528. }
  2529. /**
  2530. * 是否为有效的微信号
  2531. * 6至20位,以字母开头,字母,数字,减号,下划线
  2532. *
  2533. * @param {string} val
  2534. * @returns {boolean}
  2535. * @example
  2536. *
  2537. * isValidWechatID('liwenbo_2010');
  2538. * // => true
  2539. */
  2540. function isValidWechatID(val) {
  2541. var reg = /^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/;
  2542. return reg.test(val);
  2543. }
  2544. /**
  2545. * 是否为有效的16进制颜色
  2546. *
  2547. * @param {string} val
  2548. * @returns {boolean}
  2549. * @example
  2550. *
  2551. * isValidHexadecimalColor('#f00');
  2552. * // => true
  2553. *
  2554. * isValidHexadecimalColor('#fe9de8');
  2555. * // => true
  2556. */
  2557. function isValidHexadecimalColor(val) {
  2558. var reg = /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/;
  2559. return reg.test(val);
  2560. }
  2561. /**
  2562. * 是否为有效的IP v4
  2563. *
  2564. * @param {string} val
  2565. * @returns {boolean}
  2566. * @example
  2567. *
  2568. * isValidIPV4('172.16.0.0');
  2569. * // => true
  2570. *
  2571. * isValidIPV4('127.0.0.0');
  2572. * // => true
  2573. *
  2574. */
  2575. function isValidIPV4(val) {
  2576. var reg = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
  2577. return reg.test(val);
  2578. }
  2579. /**
  2580. * 是否为有效的IP v6
  2581. *
  2582. * @param {string} val
  2583. * @returns {boolean}
  2584. * @example
  2585. *
  2586. * isValidIPV6('2031:0000:130f:0000:0000:09c0:876a:130b');
  2587. * // => true
  2588. *
  2589. */
  2590. function isValidIPV6(val) {
  2591. var reg = /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i;
  2592. return reg.test(val);
  2593. }
  2594. /**
  2595. * 是否为有效的护照(包含香港、澳门)
  2596. *
  2597. * @param {string} val
  2598. * @returns {boolean}
  2599. * @example
  2600. *
  2601. * isValidPassport('s28233515');
  2602. * // => true
  2603. *
  2604. * isValidPassport('141234567');
  2605. * // => true
  2606. *
  2607. * isValidPassport('159203084');
  2608. * // => true
  2609. *
  2610. * isValidPassport('MA1234567');
  2611. * // => true
  2612. *
  2613. * isValidPassport('K25345719');
  2614. * // => true
  2615. */
  2616. function isValidPassport(val) {
  2617. var reg = /(^[EeKkGgDdSsPpHh]\d{8}$)|(^(([Ee][a-fA-F])|([DdSsPp][Ee])|([Kk][Jj])|([Mm][Aa])|(1[45]))\d{7}$)/;
  2618. return reg.test(val);
  2619. }
  2620. /**
  2621. * 是否为有效的电话(座机)
  2622. *
  2623. * @param {string} val
  2624. * @returns {boolean}
  2625. * @example
  2626. *
  2627. * isTelephone('0571-4211236');
  2628. * // => true
  2629. */
  2630. function isValidTelephone(val) {
  2631. var reg = /^0\d{2,3}-\d{7,8}$/;
  2632. return reg.test(val);
  2633. }
  2634. /**
  2635. * 是否为有效的手机号,中国手机号(最宽松), 只要是1开头即可, 如果你的手机号是用来接收短信, 优先建议选择这一条
  2636. *
  2637. * @param {string} val
  2638. * @returns {boolean}
  2639. * @example
  2640. *
  2641. * isMobileLoose('008618311006933');
  2642. * // => true
  2643. *
  2644. * isMobileLoose('+8617888829981');
  2645. * // => true
  2646. *
  2647. * isMobileLoose('19119255642');
  2648. * // => true
  2649. */
  2650. function isMobileLoose(val) {
  2651. var reg = /^(?:(?:\+|00)86)?1\d{10}$/;
  2652. return reg.test(val);
  2653. }
  2654. /**
  2655. * 是否为有效的手机号,中国手机号(严谨), 根据工信部2019年最新公布的手机号段
  2656. *
  2657. * @param {string} val
  2658. * @returns {boolean}
  2659. * @example
  2660. *
  2661. * isMobileStrict('008618311006933');
  2662. * // => true
  2663. *
  2664. * isMobileStrict('+8617888829981');
  2665. * // => true
  2666. *
  2667. * isMobileStrict('19119255642');
  2668. * // => true
  2669. */
  2670. function isMobileStrict(val) {
  2671. var reg = /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-7|9])|(?:5[0-3|5-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[1|8|9]))\d{8}$/;
  2672. return reg.test(val);
  2673. }
  2674. /**
  2675. * 是否为有效的中文姓名
  2676. *
  2677. * @param val
  2678. * @returns {boolean}
  2679. * @example
  2680. *
  2681. * isValidChineseName('葛二蛋');
  2682. * // => true
  2683. *
  2684. * isValidChineseName('凯文·杜兰特');
  2685. * // => true
  2686. *
  2687. * isValidChineseName('德克·维尔纳·诺维茨基');
  2688. * // => true
  2689. */
  2690. function isValidChineseName(val) {
  2691. var reg = /^(?:[\u4e00-\u9fa5·]{2,16})$/;
  2692. return reg.test(val);
  2693. }
  2694. /**
  2695. * 是否为有效的新能源车牌号
  2696. *
  2697. * @param {string} val
  2698. * @returns {boolean}
  2699. * @example
  2700. *
  2701. * isNewEnergyLicenseNo('京AD92035');
  2702. * // => true
  2703. *
  2704. * isNewEnergyLicenseNo('甘G23459F');
  2705. * // => true
  2706. */
  2707. function isNewEnergyLicenseNo(val) {
  2708. var reg = /[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}(([0-9]{5}[DF])|([DF][A-HJ-NP-Z0-9][0-9]{4}))$/;
  2709. return reg.test(val);
  2710. }
  2711. /**
  2712. * 是否为有效的银行卡号(10到30位, 覆盖对公/私账户, 参考[微信支付](https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=22_1))
  2713. *
  2714. * @param {string} val
  2715. * @returns {boolean}
  2716. * @example
  2717. *
  2718. * isValidBankNo('6234567890');
  2719. * // => true
  2720. *
  2721. * isValidBankNo('6222026006705354217');
  2722. * // => true
  2723. */
  2724. function isValidBankNo(val) {
  2725. var reg = /^[1-9]\d{9,29}$/;
  2726. return reg.test(val);
  2727. }
  2728. /**
  2729. * 是否为有效的 base64格式
  2730. *
  2731. * @param {string} val
  2732. * @returns {boolean}
  2733. * @example
  2734. *
  2735. * isValidBase64Format('data:image/gif;base64,xxxx==')
  2736. * => true
  2737. */
  2738. function isValidBase64Format(val) {
  2739. var reg = /^\s*data:(?:[a-z]+\/[a-z0-9-+.]+(?:;[a-z-]+=[a-z0-9-]+)?)?(?:;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*?)\s*$/i;
  2740. return reg.test(val);
  2741. }
  2742. /**
  2743. * 是否为有效的A股代码
  2744. *
  2745. * @param {string} val
  2746. * @returns {boolean}
  2747. * @example
  2748. *
  2749. * isValidAShareCode('sz000858');
  2750. * // => true
  2751. *
  2752. * isValidAShareCode('SZ002136');
  2753. * // => true
  2754. *
  2755. * isValidAShareCode('SH600600');
  2756. * // => true
  2757. *
  2758. * isValidAShareCode('sh600600');
  2759. * // => true
  2760. */
  2761. function isValidAShareCode(val) {
  2762. var reg = /^(s[hz]|S[HZ])(000[\d]{3}|002[\d]{3}|300[\d]{3}|600[\d]{3}|60[\d]{4})$/;
  2763. return reg.test(val);
  2764. }
  2765. /**
  2766. * 是否为有效的统一社会信用代码
  2767. *
  2768. * @param {string} val
  2769. * @returns {boolean}
  2770. * @example
  2771. *
  2772. * isUnifiedSocialCreditCode('91230184MA1BUFLT44');
  2773. * // => true
  2774. *
  2775. * isUnifiedSocialCreditCode('92371000MA3MXH0E3W');
  2776. * // => true
  2777. */
  2778. function isUnifiedSocialCreditCode(val) {
  2779. var reg = /[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}/;
  2780. return reg.test(val);
  2781. }
  2782. /**
  2783. * 是否为有效的考卷分数, 大于等于0, 小于等于150, 支持小数位出现5, 如145.5
  2784. *
  2785. * @param {string} val
  2786. * @returns {boolean}
  2787. * @example
  2788. *
  2789. * isValidTestScores('150');
  2790. * // => true
  2791. *
  2792. * isValidTestScores('149.5');
  2793. * // => true
  2794. */
  2795. function isValidTestScores(val) {
  2796. var reg = /^150$|^(?:\d|[1-9]\d|1[0-4]\d)(?:.5)?$/;
  2797. return reg.test(val);
  2798. }
  2799. /**
  2800. * 版本号格式必须为X.Y.Z
  2801. *
  2802. * @param {string} val
  2803. * @returns {boolean}
  2804. * @example
  2805. *
  2806. * isValidSemverVersion('16.3.10');
  2807. * // => true
  2808. */
  2809. function isValidSemverVersion(val) {
  2810. var reg = /^150$|^(?:\d|[1-9]\d|1[0-4]\d)(?:.5)?$/;
  2811. return reg.test(val);
  2812. }
  2813. /**
  2814. * 是否为有效的国内座机电话
  2815. *
  2816. * @param {string} val
  2817. * @returns {boolean}
  2818. * @example
  2819. *
  2820. * isTelephone('0571-4211236');
  2821. * // => true
  2822. *
  2823. * isTelephone('0341-86091234');
  2824. * // => true
  2825. */
  2826. function isValidChineseTelephone(val) {
  2827. var reg = /\d{3}-\d{8}|\d{4}-\d{7}/;
  2828. return reg.test(val);
  2829. }
  2830. /**
  2831. * 是否为有效的迅雷链接
  2832. *
  2833. * @param {string} val
  2834. * @returns {boolean}
  2835. * @example
  2836. *
  2837. * isValidThunder('thunder://QUEsICdtYWduZXQ6P3h0PXVybjpidGloOjBCQTE0RTUxRkUwNjU1RjE0Qzc4NjE4RjY4NDY0QjZFNTEyNjcyOUMnWlo=');
  2838. * // => true
  2839. */
  2840. function isValidThunder(val) {
  2841. var reg = /^thunderx?:\/\/[a-zA-Z\d]+=$/;
  2842. return reg.test(val);
  2843. }
  2844. /**
  2845. * 是否为有效的ed2k链接(宽松匹配)
  2846. *
  2847. * @param {string} val
  2848. * @returns {boolean}
  2849. * @example
  2850. *
  2851. * isValidEd2kLinkLoose('ed2k://|file|%E5%AF%84%E7%94%9F%E8%99%AB.PARASITE.2019.HD-1080p.X264.AAC-UUMp4(ED2000.COM).mp4|2501554832|C0B93E0879C6071CBED732C20CE577A3|h=5HTKZPQFYRKORN52I3M7GQ4QQCIHFIBV|/');
  2852. * // => true
  2853. */
  2854. function isValidEd2kLinkLoose(val) {
  2855. var reg = /^ed2k:\/\/\|file\|.+\|\/$/;
  2856. return reg.test(val);
  2857. }
  2858. /**
  2859. * 是否为有效的磁力链接(宽松匹配)
  2860. *
  2861. * @param {string} val
  2862. * @returns {boolean}
  2863. * @example
  2864. *
  2865. * isValidMagnetLinkLoose('magnet:?xt=urn:btih:40A89A6F4FB1498A98087109D012A9A851FBE0FC');
  2866. * // => true
  2867. */
  2868. function isValidMagnetLinkLoose(val) {
  2869. var reg = /^magnet:\?xt=urn:btih:[0-9a-fA-F]{40,}.*$/;
  2870. return reg.test(val);
  2871. }
  2872. /**
  2873. * 是否为有效的子网掩码
  2874. *
  2875. * @param {string} val
  2876. * @returns {boolean}
  2877. * @example
  2878. *
  2879. * isValidSubnetMask('255.255.255.0');
  2880. * // => true
  2881. */
  2882. function isValidSubnetMask(val) {
  2883. var reg = /^(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/;
  2884. return reg.test(val);
  2885. }
  2886. /**
  2887. * 是否为有效的md5格式(32位)
  2888. *
  2889. * @param {string} val
  2890. * @returns {boolean}
  2891. * @example
  2892. *
  2893. * isValidMD5('21fe181c5bfc16306a6828c1f7b762e8');
  2894. * // => true
  2895. */
  2896. function isValidMD5(val) {
  2897. var reg = /^[a-f0-9]{32}$/;
  2898. return reg.test(val);
  2899. }
  2900. /**
  2901. * 是否为有效的视频链接地址(视频格式可按需增删)
  2902. *
  2903. * @param {string} val
  2904. * @returns {boolean}
  2905. * @example
  2906. *
  2907. * isValidVideoLink('http://www.abc.com/video/wc.avi');
  2908. * // => true
  2909. */
  2910. function isValidVideoLink(val) {
  2911. var reg = /^https?:\/\/.*?(?:swf|avi|flv|mpg|rm|mov|wav|asf|3gp|mkv|rmvb|mp4)$/i;
  2912. return reg.test(val);
  2913. }
  2914. /**
  2915. * 是否为有效的图片链接地址(图片格式可按需增删)
  2916. *
  2917. * @param {string} val
  2918. * @returns {boolean}
  2919. * @example
  2920. *
  2921. * isValidImageLink('https://www.abc.com/logo.png');
  2922. * // => true
  2923. */
  2924. function isValidImageLink(val) {
  2925. var reg = /^https?:\/\/.*?(?:gif|png|jpg|jpeg|webp|svg|psd|bmp|tif)$/i;
  2926. return reg.test(val);
  2927. }
  2928. /**
  2929. * 是否为有效的用户名,4到16位(字母,数字,下划线,减号)
  2930. *
  2931. * @param {string} val
  2932. * @param {number} minLength
  2933. * @param {number} maxLength
  2934. * @returns {boolean}
  2935. * @example
  2936. *
  2937. * isValidUserName('xiaohua_qq');
  2938. * // => true
  2939. */
  2940. function isValidUserName(val, minLength, maxLength) {
  2941. if ( minLength === void 0 ) minLength = 4;
  2942. if ( maxLength === void 0 ) maxLength = 16;
  2943. var reg = new RegExp(("^[a-zA-Z0-9_-]{" + minLength + "," + maxLength + "}$"));
  2944. return reg.test(val);
  2945. }
  2946. /**
  2947. * 是否为有效的密码强度,最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符
  2948. *
  2949. * @param {string} val
  2950. * @param {number} minLength 最小位数
  2951. * @returns {boolean}
  2952. * @example
  2953. *
  2954. * isValidPassword('Kd@curry666');
  2955. * // => true
  2956. */
  2957. function isValidPassword(val, minLength) {
  2958. if ( minLength === void 0 ) minLength = 6;
  2959. var reg = new RegExp(("^.*(?=.{" + minLength + ",})(?=.*\\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$"));
  2960. return reg.test(val);
  2961. }
  2962. /**
  2963. * 动态设置网页中的标题
  2964. *
  2965. * @param title
  2966. * @param img
  2967. */
  2968. function setDocumentTitle(title, img) {
  2969. if (!title || window.document.title === title) { return; }
  2970. document.title = title;
  2971. var mobile = navigator.userAgent.toLowerCase();
  2972. if (/iphone|ipad|ipod/.test(mobile)) {
  2973. var iframe = document.createElement('iframe');
  2974. iframe.style.display = 'none';
  2975. // 替换成站标favicon路径或者任意存在的较小的图片即可
  2976. iframe.setAttribute('src', img || 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
  2977. var iframeCallback = function () {
  2978. setTimeout(function () {
  2979. iframe.removeEventListener('load', iframeCallback);
  2980. document.body.removeChild(iframe);
  2981. }, 0);
  2982. };
  2983. iframe.addEventListener('load', iframeCallback);
  2984. document.body.appendChild(iframe);
  2985. }
  2986. }
  2987. /**
  2988. * 是否是QQ浏览器内核
  2989. *
  2990. * @returns {boolean}
  2991. * @example
  2992. *
  2993. * inQQBrowser();
  2994. * // => false
  2995. */
  2996. function inQQBrowser() {
  2997. if (typeof window.navigator === 'undefined') { return; }
  2998. var ua = window.navigator.userAgent.toLowerCase();
  2999. return ua.indexOf('mqqbrowser') !== -1;
  3000. }
  3001. /**
  3002. * 是否是UC浏览器内核
  3003. *
  3004. * @returns {boolean}
  3005. * @example
  3006. *
  3007. * inUCBrowser();
  3008. * // => false
  3009. */
  3010. function inUCBrowser() {
  3011. if (typeof window.navigator === 'undefined') { return; }
  3012. var ua = window.navigator.userAgent.toLowerCase();
  3013. return ua.indexOf('ucbrowser') !== -1;
  3014. }
  3015. /**
  3016. * 两个值之间的深入比较,以确定它们是否相等
  3017. *
  3018. * @param {Object} a
  3019. * @param {Object} b
  3020. * @returns {*}
  3021. * @example
  3022. *
  3023. * equals({ a: [2, { e: 3 }], b: [4], c: 'foo' }, { a: [2, { e: 3 }], b: [4], c: 'foo' });
  3024. *
  3025. * // => true
  3026. */
  3027. function equals(a, b) {
  3028. if (a === b) { return true; }
  3029. if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime(); }
  3030. if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) { return a === b; }
  3031. if (a === null || a === undefined || b === null || b === undefined) { return false; }
  3032. if (a.prototype !== b.prototype) { return false; }
  3033. var keys = Object.keys(a);
  3034. if (keys.length !== Object.keys(b).length) { return false; }
  3035. return keys.every(function (k) { return equals(a[k], b[k]); });
  3036. }
  3037. /**
  3038. * utf16字符串转实体字符
  3039. * @param {string} str 待编译的字符串
  3040. */
  3041. function utf16toEntities(str) {
  3042. if (!str) { return ''; }
  3043. if (typeof str !== 'string') {
  3044. console.error('需要编译的数据类型需要是字符串类型');
  3045. return str;
  3046. }
  3047. var patt = /[\ud800-\udbff][\udc00-\udfff]/g;
  3048. // 检测utf16字符正则
  3049. str = str.replace(patt, function (char) {
  3050. var H, L, code;
  3051. if (char.length === 2) {
  3052. H = char.charCodeAt(0);
  3053. // 取出高位
  3054. L = char.charCodeAt(1);
  3055. // 取出低位
  3056. code = (H - 0xd800) * 0x400 + 0x10000 + L - 0xdc00;
  3057. // 转换算法
  3058. return '&#' + code + ';';
  3059. } else {
  3060. return char;
  3061. }
  3062. });
  3063. return str;
  3064. }
  3065. /**
  3066. * 实体字符转utf16字符串
  3067. * @param {*} str 待解析的字符串
  3068. */
  3069. function entitiestoUtf16(str) {
  3070. if (!str) { return ''; }
  3071. if (typeof str !== 'string') {
  3072. console.error('需要解析的数据类型需要是字符串类型');
  3073. return str;
  3074. }
  3075. // 检测出形如&#12345;形式的字符串
  3076. var strObj = utf16toEntities(str);
  3077. var patt = /&#\d+;/g;
  3078. var H, L, code;
  3079. var arr = strObj.match(patt) || [];
  3080. for (var i = 0; i < arr.length; i++) {
  3081. code = arr[i];
  3082. code = code.replace('&#', '').replace(';', '');
  3083. // 高位
  3084. H = Math.floor((code - 0x10000) / 0x400) + 0xd800;
  3085. // 低位
  3086. L = ((code - 0x10000) % 0x400) + 0xdc00;
  3087. code = '&#' + code + ';';
  3088. var s = String.fromCharCode(H, L);
  3089. strObj = strObj.replace(code, s);
  3090. }
  3091. return strObj;
  3092. }
  3093. /**
  3094. * 处理emoji,用于把用utf16编码的字符转换成实体字符
  3095. * @param {string} str 需要编译/解析的字符串
  3096. * @param {string} type encode 编译 decode 转义
  3097. * @returns {string} 编译/解析后的字符串
  3098. * @example
  3099. *
  3100. * handleEmoji("😃", "encode");
  3101. * // => "&#128515;"
  3102. * handleEmoji("&#128522;", "decode");
  3103. * // => "😊"
  3104. */
  3105. function handleEmoji(str, type) {
  3106. if ( str === void 0 ) str = '';
  3107. if ( type === void 0 ) type = 'encode';
  3108. if (!str) { return ''; }
  3109. if (typeof str !== 'string') {
  3110. console.error('handleEmoji数据类型需要是字符串类型');
  3111. return str;
  3112. }
  3113. if (type === 'encode') {
  3114. return utf16toEntities(str);
  3115. } else if (type === 'decode') {
  3116. return entitiestoUtf16(str);
  3117. } else {
  3118. return str;
  3119. }
  3120. }
  3121. /**
  3122. * 获取图片的base64 url
  3123. * @param {string} url 图片url
  3124. * @returns {Promise} 图片base64信息
  3125. */
  3126. function getImgBase64(url) {
  3127. var Img = new Image();
  3128. var dataURL = '';
  3129. Img.setAttribute('crossOrigin', 'anonymous');
  3130. Img.src = url;
  3131. return new Promise(function (resolve, reject) {
  3132. Img.onload = function () {
  3133. var canvas = document.createElement('canvas'),
  3134. width = Img.width,
  3135. height = Img.height;
  3136. var ctx = canvas.getContext('2d');
  3137. var scale = 5;
  3138. ctx.scale(scale, scale);
  3139. canvas.width = width * scale;
  3140. canvas.height = height * scale;
  3141. ctx.drawImage(Img, 0, 0, width * scale, height * scale);
  3142. dataURL = canvas.toDataURL('image/png');
  3143. resolve(dataURL);
  3144. };
  3145. });
  3146. }
  3147. /**
  3148. * 删除对象里面value值为null的键值对
  3149. * @param {*} data 接口返回的blob数据
  3150. * @param {*} name excel名称
  3151. * @param {*} callBack 导出成功/失败回调 回调返回{type:fail/success} fail情况下 返回{ type: "fail", code, msg }
  3152. */
  3153. function exportXls(data, name, callBack) {
  3154. if ( name === void 0 ) name = 'excel';
  3155. if (!data || data.size === 0) {
  3156. callBack && callBack({type: 'fail', msg: '数据为空'});
  3157. return false;
  3158. }
  3159. var reader = new FileReader();
  3160. reader.readAsText(data, 'utf-8');
  3161. reader.onload = function (e) {
  3162. try {
  3163. var ref = JSON.parse(reader.result);
  3164. var code = ref.code;
  3165. var msg = ref.msg;
  3166. if (code && code !== 200) {
  3167. callBack && callBack({type: 'fail', code: code, msg: msg});
  3168. return false;
  3169. } else {
  3170. _downFile(data, name);
  3171. }
  3172. callBack && callBack({type: 'success'});
  3173. } catch (error) {
  3174. _downFile(data, name);
  3175. callBack && callBack({type: 'success'});
  3176. }
  3177. };
  3178. }
  3179. function _downFile(data, fileName) {
  3180. var blob = new Blob([data], {type: 'application/vnd.ms-excel,charset=UTF-8'});
  3181. if (window.navigator.msSaveOrOpenBlob) {
  3182. navigator.msSaveBlob(blob, fileName + '.xlsx');
  3183. } else {
  3184. var link = document.createElement('a');
  3185. link.href = window.URL.createObjectURL(blob);
  3186. link.download = fileName + '.xlsx';
  3187. link.click();
  3188. window.URL.revokeObjectURL(link.href);
  3189. }
  3190. }
  3191. /**
  3192. * 检查是否是emoji表情
  3193. * @param {*} value 正则校验变量
  3194. * @return {boolean} 正则校验结果 true: 是emoji表情 false: 不是emoji表情
  3195. */
  3196. function isEmoji(value) {
  3197. var arr = ['\ud83c[\udf00-\udfff]', '\ud83d[\udc00-\ude4f]', '\ud83d[\ude80-\udeff]'];
  3198. return new RegExp(arr.join('|'), 'g').test(value);
  3199. }
  3200. /**
  3201. * 检查是否为特殊字符
  3202. * @param {string} value 正则校验的变量
  3203. * @returns {boolean} 正则校验结果 true: 是特殊字符 false: 不是特殊字符
  3204. */
  3205. function isSpecialChar(value) {
  3206. var regEn = /[`~!@#$%^&*()_+<>?:"{},.\/;'[\]\s]/im;
  3207. var regCn = /[·!#¥(——):;“”‘、,|《。》?、【】[\]\s]/im;
  3208. return regEn.test(value) || regCn.test(value);
  3209. }
  3210. exports.accAdd = accAdd;
  3211. exports.accDiv = accDiv;
  3212. exports.accMul = accMul;
  3213. exports.accSub = accSub;
  3214. exports.addChineseUnit = addChineseUnit;
  3215. exports.appendStockSuffix = appendStockSuffix;
  3216. exports.encrypt = encrypt;
  3217. exports.extend = extend;
  3218. exports.formatBankCard = formatBankCard;
  3219. exports.formatDate = formatDate;
  3220. exports.formatTimeAgo = formatTimeAgo;
  3221. exports.formatDateToTimeStamp = formatDateToTimeStamp;
  3222. exports.formatMoney = formatMoney;
  3223. exports.formatPhone = formatPhone;
  3224. exports.getLocationHrefParam = getLocationHrefParam;
  3225. exports.getLocationSearchParam = getLocationSearchParam;
  3226. exports.getUrlNames = getUrlNames;
  3227. exports.generateGUID = generateGUID;
  3228. exports.getRandomInt = getRandomInt;
  3229. exports.htmlDecode = htmlDecode;
  3230. exports.htmlEncode = htmlEncode;
  3231. exports.inAlipay = inAlipay;
  3232. exports.inWeixin = inWeixin;
  3233. exports.isCardId = isCardId;
  3234. exports.isDigit = isDigit;
  3235. exports.isEmptyObject = isEmptyObject;
  3236. exports.isLeapYear = isLeapYear;
  3237. exports.isLetters = isLetters;
  3238. exports.isLicenseNo = isLicenseNo;
  3239. exports.isMobile = isMobile;
  3240. exports.isValidDate = isValidDate;
  3241. exports.isValidEmail = isValidEmail;
  3242. exports.isValidQQ = isValidQQ;
  3243. exports.isValidURI = isValidURI;
  3244. exports.isValidZipcode = isValidZipcode;
  3245. exports.preZeroFill = preZeroFill;
  3246. exports.bytesToSize = bytesToSize;
  3247. exports.dataURLToBlob = dataURLToBlob;
  3248. exports.getPixelRatio = getPixelRatio;
  3249. exports.insertAtCaret = insertAtCaret;
  3250. exports.getDevice = getDevice;
  3251. exports.getBrowser = getBrowser;
  3252. exports.getDiffDay = getDiffDay;
  3253. exports.addClass = addClass;
  3254. exports.hasClass = hasClass;
  3255. exports.removeClass = removeClass;
  3256. exports.toCamelCaseVar = toCamelCaseVar;
  3257. exports.formatNumber = formatNumber;
  3258. exports.compareVersion = compareVersion;
  3259. exports.getIn = getIn;
  3260. exports.rgbToHex = rgbToHex;
  3261. exports.hexToRgb = hexToRgb;
  3262. exports.anagrams = anagrams;
  3263. exports.capitalizeEveryWord = capitalizeEveryWord;
  3264. exports.fibonacci = fibonacci;
  3265. exports.getScrollPos = getScrollPos;
  3266. exports.last = last;
  3267. exports.timeTaken = timeTaken;
  3268. exports.objectFromPairs = objectFromPairs;
  3269. exports.objectToPairs = objectToPairs;
  3270. exports.scrollToTop = scrollToTop;
  3271. exports.isChinese = isChinese;
  3272. exports.isHTML = isHTML;
  3273. exports.getMonths = getMonths;
  3274. exports.getMonthOfDay = getMonthOfDay;
  3275. exports.getDays = getDays;
  3276. exports.getDayOfYear = getDayOfYear;
  3277. exports.getDayOfYearWeek = getDayOfYearWeek;
  3278. exports.getYearOfDay = getYearOfDay;
  3279. exports.photoCompress = photoCompress;
  3280. exports.changeMoneyToChinese = changeMoneyToChinese;
  3281. exports.numberToChinese = numberToChinese;
  3282. exports.toNonExponential = toNonExponential;
  3283. exports.getURLParameters = getURLParameters;
  3284. exports.mapValues = mapValues;
  3285. exports.mapKeys = mapKeys;
  3286. exports.invertKeyValues = invertKeyValues;
  3287. exports.size = size;
  3288. exports.trim = trim$1;
  3289. exports.toZhCN = toZhCN;
  3290. exports.deepClone = deepClone;
  3291. exports.deepMapKeys = deepMapKeys;
  3292. exports.get = get;
  3293. exports.dig = dig;
  3294. exports.isEmpty = isEmpty;
  3295. exports.merge = merge;
  3296. exports.only = only;
  3297. exports.isLightOS = isLightOS;
  3298. exports.nativeJSBridge = nativeJSBridge;
  3299. exports.nativeReady = nativeReady;
  3300. exports.combineURLs = combineURLs;
  3301. exports.inWeibo = inWeibo;
  3302. exports.dynamicLoadScript = dynamicLoadScript;
  3303. exports.isValidWechatID = isValidWechatID;
  3304. exports.isValidHexadecimalColor = isValidHexadecimalColor;
  3305. exports.isValidIPV4 = isValidIPV4;
  3306. exports.isValidIPV6 = isValidIPV6;
  3307. exports.isValidPassport = isValidPassport;
  3308. exports.isValidTelephone = isValidTelephone;
  3309. exports.isMobileLoose = isMobileLoose;
  3310. exports.isMobileStrict = isMobileStrict;
  3311. exports.isValidChineseName = isValidChineseName;
  3312. exports.isNewEnergyLicenseNo = isNewEnergyLicenseNo;
  3313. exports.isValidBankNo = isValidBankNo;
  3314. exports.isValidBase64Format = isValidBase64Format;
  3315. exports.isValidAShareCode = isValidAShareCode;
  3316. exports.isUnifiedSocialCreditCode = isUnifiedSocialCreditCode;
  3317. exports.isValidTestScores = isValidTestScores;
  3318. exports.isValidSemverVersion = isValidSemverVersion;
  3319. exports.isValidChineseTelephone = isValidChineseTelephone;
  3320. exports.isValidThunder = isValidThunder;
  3321. exports.isValidEd2kLinkLoose = isValidEd2kLinkLoose;
  3322. exports.isValidMagnetLinkLoose = isValidMagnetLinkLoose;
  3323. exports.isValidSubnetMask = isValidSubnetMask;
  3324. exports.isValidMD5 = isValidMD5;
  3325. exports.isValidVideoLink = isValidVideoLink;
  3326. exports.isValidImageLink = isValidImageLink;
  3327. exports.isValidUserName = isValidUserName;
  3328. exports.isValidPassword = isValidPassword;
  3329. exports.isPhoneX = isPhoneX;
  3330. exports.setDocumentTitle = setDocumentTitle;
  3331. exports.inQQBrowser = inQQBrowser;
  3332. exports.inUCBrowser = inUCBrowser;
  3333. exports.equals = equals;
  3334. exports.handleEmoji = handleEmoji;
  3335. exports.getImgBase64 = getImgBase64;
  3336. exports.exportXls = exportXls;
  3337. exports.isEmoji = isEmoji;
  3338. exports.isSpecialChar = isSpecialChar;