util.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. // 引用 https://web.tianyunperfect.cn/simple/js/util.js
  2. function lt(obj) {
  3. console.table(JSON.parse(JSON.stringify(obj)));
  4. }
  5. /**
  6. * 深度克隆
  7. * @param obj
  8. * @returns {any}
  9. */
  10. function deepClone(obj) {
  11. return JSON.parse(JSON.stringify(obj))
  12. }
  13. function debounce(fn, wait = 50) {
  14. // 通过闭包缓存一个定时器 id
  15. let timer = null
  16. // 将 debounce 处理结果当作函数返回
  17. // 触发事件回调时执行这个返回函数
  18. return function (...args) {
  19. // 如果已经设定过定时器就清空上一次的定时器
  20. if (timer) clearTimeout(timer)
  21. // 开始设定一个新的定时器,定时器结束后执行传入的函数 fn
  22. timer = setTimeout(() => {
  23. fn.apply(this, args)
  24. }, wait)
  25. }
  26. }
  27. // 日期工具类
  28. const dataUtil = {
  29. // 日期 -> 字符串:formatDate(date, 'yyyy-MM-dd hh:mm:ss');
  30. formatDate: function (date, format) {
  31. const pad = (n) => (n < 10 ? '0' + n : n);
  32. const replacements = {
  33. 'yyyy': date.getFullYear(),
  34. 'MM': pad(date.getMonth() + 1),
  35. 'dd': pad(date.getDate()),
  36. 'hh': pad(date.getHours()),
  37. 'mm': pad(date.getMinutes()),
  38. 'ss': pad(date.getSeconds()),
  39. 'qq': Math.floor((date.getMonth() + 3) / 3), //季度
  40. 'SSS': pad(date.getMilliseconds(), 3) //毫秒
  41. };
  42. let result = format;
  43. for (const key in replacements) {
  44. result = result.replace(key, replacements[key]);
  45. }
  46. return result;
  47. },
  48. // 日期字符串 -> 日期: parseDate("2022-10-30 16:13:49")
  49. parseDate: function (dateString) {
  50. const date = new Date(Date.parse(dateString));
  51. return date;
  52. },
  53. // 当前日期
  54. getNowStr: function () {
  55. return this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss');
  56. },
  57. }
  58. /**
  59. * 远程请求
  60. * @type {{async: (function(*, *, *, *): Promise<unknown>), xhr_send: request.xhr_send, sync: (function(*, *, *, *): any)}}
  61. */
  62. const requestUtil = {
  63. xhr_send(xhr, method, headers, data) {
  64. if (headers) {
  65. for (const key in headers) {
  66. xhr.setRequestHeader(key, headers[key]);
  67. }
  68. }
  69. if (method.match(/^(POST|PUT|DELETE)$/i)) {
  70. if (!headers || !headers.hasOwnProperty("Content-Type")) {
  71. xhr.setRequestHeader("Content-Type", "application/json");
  72. }
  73. xhr.send(JSON.stringify(data));
  74. } else {
  75. xhr.send();
  76. }
  77. },
  78. /**
  79. * 异步请求:requestUtil.async('https://jsonplaceholder.typicode.com/posts/1', 'GET', null, null)
  80. * .then(data => console.log(data))
  81. * .catch(error => console.error(error));
  82. */
  83. async(url, method, data = {}, headers = {}) {
  84. return new Promise((resolve, reject) => {
  85. const xhr = new XMLHttpRequest();
  86. xhr.open(method, url, true);
  87. this.xhr_send(xhr, method, headers, data);
  88. xhr.onload = () => {
  89. resolve(JSON.parse(xhr.responseText));
  90. };
  91. xhr.onerror = () => reject(xhr.statusText);
  92. });
  93. },
  94. /**
  95. * 拼接 url
  96. */
  97. buildUrl(url, params) {
  98. const urlObj = new URL(url);
  99. // @ts-ignore
  100. for (const key in params) {
  101. urlObj.searchParams.set(key, params[key]);
  102. }
  103. return urlObj.toString();
  104. },
  105. /**
  106. * 同步请求 let a = request.sync("https://httpbin.tianyunperfect.cn/ip","GET",null,null)
  107. */
  108. sync(url, method, data = {}, headers = {}) {
  109. const xhr = new XMLHttpRequest();
  110. xhr.open(method, url, false);
  111. this.xhr_send(xhr, method, headers, data);
  112. return JSON.parse(xhr.responseText);
  113. },
  114. };
  115. /**
  116. * 休眠一段时间: await sleep(2000)
  117. * @param time
  118. * @returns {Promise<unknown>}
  119. */
  120. function sleep(time) {
  121. return new Promise((resolve) => setTimeout(resolve, time));
  122. }
  123. /**
  124. * 根据选择器 选择某一个dom,10秒钟内
  125. * @param sel
  126. * @returns {Promise<*>}
  127. */
  128. async function getDom(sel) {
  129. for (let i = 0; i < 100; i++) {
  130. let dom = document.querySelector(sel);
  131. if (dom) {
  132. return dom;
  133. } else {
  134. await sleep(100);
  135. }
  136. }
  137. }
  138. /**
  139. * 根据选择器 选择所有dom,10秒钟内
  140. * @param sel
  141. * @returns {Promise<*>}
  142. */
  143. async function getDomAll(sel) {
  144. for (let i = 0; i < 100; i++) {
  145. let dom = document.querySelectorAll(sel);
  146. if (dom.length > 0) {
  147. return dom;
  148. } else {
  149. await sleep(100);
  150. }
  151. }
  152. }
  153. /**
  154. * 添加全局样式: addGlobalStyle('.box {height: 100px !important;}');
  155. */
  156. function addGlobalStyle(newStyle) {
  157. let styleElement = document.getElementById('styles_js');
  158. if (!styleElement) {
  159. styleElement = document.createElement('style');
  160. styleElement.type = 'text/css';
  161. styleElement.id = 'styles_js';
  162. document.getElementsByTagName('head')[0].appendChild(styleElement);
  163. }
  164. styleElement.appendChild(document.createTextNode(newStyle));
  165. }
  166. /**
  167. * 获取 指定 name 的 url 参数
  168. * @param url
  169. * @param name
  170. * @returns {string|string}
  171. */
  172. function getQueryStringByUrl(url, name) {
  173. let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
  174. let r = url.substring(url.indexOf('?') + 1).match(reg); //获取url中"?"符后的字符串并正则匹配
  175. let context = "";
  176. if (r != null)
  177. context = r[2];
  178. reg = null;
  179. r = null;
  180. return context == null || context === "" || context === "undefined" ? "" : decodeURI(context);
  181. }
  182. /**
  183. * 获取 指定 name 的 url 参数
  184. * @param name
  185. * @returns {string}
  186. */
  187. function getQueryString(name) {
  188. return getQueryStringByUrl(location.href, name);
  189. }
  190. // 随机数
  191. const randomUtil = {
  192. /**
  193. * 获取随机数
  194. * @param min
  195. * @param max
  196. * @returns {number}
  197. */
  198. getInt: function (min, max) {
  199. min = Math.ceil(min);
  200. max = Math.floor(max);
  201. return Math.floor(Math.random() * (max - min) + min); //The maximum is exclusive and the minimum is inclusive
  202. },
  203. /**
  204. * 获取随机的一个值
  205. * @param arr
  206. * @returns {*}
  207. */
  208. getOneFromArray: function (arr) {
  209. return arr[this.getInt(0, arr.length)];
  210. }
  211. }
  212. /**
  213. * 创建一个<eleName k="attrs[k]">text</eleName>样式的页面元素
  214. * @param eleName
  215. * @param text
  216. * @param attrs
  217. * @returns {*}
  218. */
  219. function createEle(eleName, text, attrs) {
  220. let ele = document.createElement(eleName);
  221. // innerText 也就是 <p>text会被添加到这里</p>
  222. ele.innerText = text;
  223. // attrs 的类型是一个 map
  224. for (let k in attrs) {
  225. // 遍历 attrs, 给节点 ele 添加我们想要的属性
  226. ele.setAttribute(k, attrs[k]);
  227. }
  228. // 返回节点
  229. return ele;
  230. }
  231. /**
  232. * 自动关闭提示框
  233. * @param str 提示文本
  234. * @param sec 时间(秒)
  235. */
  236. function showMsg(str, sec) {
  237. const borderColor = "#336699"; //提示窗口的边框颜色
  238. const sWidth = document.body.offsetWidth;
  239. const sHeight = document.body.offsetHeight;
  240. //背景div
  241. const bgObj = document.createElement("div");
  242. let alertBgDiv = 'alertBgDiv';
  243. bgObj.setAttribute('id', alertBgDiv);
  244. bgObj.style.cssText = `position: fixed; top: 0; background: #E8E8E8; filter: progid:DXImageTransform.Microsoft.Alpha(style=3,opacity=25,finishOpacity=75; opacity: 0.6; left: 0; width: ${sWidth}px; height: ${sHeight}px; z-index: 10000`;
  245. document.body.appendChild(bgObj);
  246. //创建提示窗口的div
  247. const msgObj = document.createElement("div");
  248. let alertMsgDiv = "alertMsgDiv";
  249. msgObj.setAttribute("id", alertMsgDiv);
  250. msgObj.setAttribute("align", "center");
  251. msgObj.style.cssText = `background: white; border: 1px solid ${borderColor}; position: fixed; left: 50%; font: 15px/1.6em Verdana, Geneva, Arial, Helvetica, sans-serif; margin-left: -225px; top: ${document.body.scrollTop + (window.screen.availHeight / 2) - 150}px; text-align: center; line-height: 25px; z-index: 10001; min-width: 300px`;
  252. document.body.appendChild(msgObj);
  253. //提示信息标题
  254. const title = document.createElement("h4");
  255. let alertMsgTitle = "alertMsgTitle";
  256. title.setAttribute("id", alertMsgTitle);
  257. title.setAttribute("align", "left");
  258. title.style.cssText = `margin:0; padding:3px; background:${borderColor}; filter:progid:DXImageTransform.Microsoft.Alpha(startX=20, startY=20, finishX=100, finishY=100,style=1,opacity=75,finishOpacity=100); opacity:0.75; border:1px solid ${borderColor}; font:12px Verdana, Geneva, Arial, Helvetica, sans-serif; color:white`;
  259. title.innerHTML = "提示信息";
  260. document.getElementById(alertMsgDiv).appendChild(title);
  261. //提示信息
  262. const txt = document.createElement("p");
  263. txt.setAttribute("id", "msgTxt");
  264. txt.style.margin = "16px 0";
  265. txt.innerHTML = str;
  266. document.getElementById(alertMsgDiv).appendChild(txt);
  267. //设置关闭时间
  268. window.setTimeout(() => {
  269. document.body.removeChild(document.getElementById(alertBgDiv));
  270. document.getElementById(alertMsgDiv).removeChild(document.getElementById(alertMsgTitle));
  271. document.body.removeChild(document.getElementById(alertMsgDiv));
  272. }, sec * 1000);
  273. }
  274. /**
  275. * 打印信息
  276. * @param obj
  277. */
  278. function log(obj) {
  279. console.table(JSON.parse(JSON.stringify(obj)));
  280. }
  281. // 添加静态资源
  282. const staticLoader = {
  283. /**
  284. * 添加js引用 : addRemoteJs("https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js");
  285. */
  286. addRemoteJs: function (jsUrl) {
  287. if (document.querySelector(`script[src="${jsUrl}"]`)) {
  288. return;
  289. }
  290. const script = document.createElement('script');
  291. script.src = jsUrl;
  292. script.type = 'text/javascript';
  293. document.head.appendChild(script);
  294. },
  295. addRemoteCss: function (cssUrl) {
  296. if (document.querySelector(`link[href="${cssUrl}"]`)) {
  297. return;
  298. }
  299. const link = document.createElement('link');
  300. link.href = cssUrl;
  301. link.rel = 'stylesheet';
  302. link.type = 'text/css';
  303. document.head.appendChild(link);
  304. },
  305. /**
  306. * 添加 Jq
  307. */
  308. addJq: function () {
  309. const jqUrl = 'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js';
  310. this.addRemoteJs(jqUrl);
  311. }
  312. }
  313. // 复制文本工具类
  314. const copyUtil = {
  315. /**
  316. * 复制,支持复制html: copyHtml('#wish_search_list .wish_s_item');
  317. * @param css_selector
  318. * @returns {Promise<void>}
  319. */
  320. copyFromSelector: async function (css_selector) {
  321. const el = document.querySelector(css_selector);
  322. const html = el.outerHTML;
  323. await this.copyFromHtml(html);
  324. },
  325. copyFromHtml: async function (html) {
  326. try {
  327. await navigator.clipboard.write([
  328. new ClipboardItem({
  329. 'text/html': new Blob([html], {type: 'text/html'})
  330. })
  331. ]);
  332. console.log('复制成功');
  333. } catch (err) {
  334. console.error('复制失败', err);
  335. }
  336. },
  337. copyText: async function (text) {
  338. if (navigator.clipboard) {
  339. // clipboard api 复制
  340. await navigator.clipboard.writeText(text);
  341. } else {
  342. const textarea = document.createElement('textarea');
  343. document.body.appendChild(textarea);
  344. // 隐藏此输入框
  345. textarea.style.position = 'fixed';
  346. textarea.style.clip = 'rect(0 0 0 0)';
  347. textarea.style.top = '10px';
  348. // 赋值
  349. textarea.value = text;
  350. // 选中
  351. textarea.select();
  352. // 复制
  353. document.execCommand('copy', true);
  354. // 移除输入框
  355. document.body.removeChild(textarea);
  356. }
  357. },
  358. getClipboardText: async function () {
  359. try {
  360. const text = await navigator.clipboard.readText();
  361. console.log("剪贴板内容:", text);
  362. return text;
  363. } catch (err) {
  364. console.error("无法读取剪贴板内容:", err);
  365. }
  366. }
  367. }
  368. /**
  369. * base64图片转为webp格式
  370. * @param base64Image
  371. * @returns {Promise<string|*>}
  372. */
  373. async function convertToWebPAsync(base64Image) {
  374. // 检查图像格式是否为WebP
  375. if (base64Image.startsWith('data:image/webp')) {
  376. // 如果是WebP格式,直接返回原始的base64字符串
  377. return base64Image;
  378. } else {
  379. // 将图像转换为WebP格式
  380. const image = new Image();
  381. image.src = base64Image;
  382. await new Promise((resolve, reject) => {
  383. image.onload = resolve;
  384. image.onerror = reject;
  385. });
  386. const canvas = document.createElement('canvas');
  387. canvas.width = image.width;
  388. canvas.height = image.height;
  389. const ctx = canvas.getContext('2d');
  390. ctx.drawImage(image, 0, 0);
  391. return canvas.toDataURL('image/webp');
  392. }
  393. }
  394. function closeWindow() {
  395. const userAgent = navigator.userAgent;
  396. if (userAgent.indexOf("Firefox") !== -1 || userAgent.indexOf("Chrome") !== -1) {
  397. window.location.href = "about:blank";
  398. window.close();
  399. } else {
  400. window.opener = null;
  401. window.open("", "_self");
  402. window.close();
  403. }
  404. }
  405. function getTimeStamp() {
  406. const currentTimeStamp = Date.now();
  407. const targetTimeStamp = new Date('2024-01-01').getTime();
  408. return currentTimeStamp - targetTimeStamp;
  409. }
  410. /** 用于监控值的变化并执行相应的操作
  411. * monitorAndAct(generateRandomValue, logValueChange);
  412. *
  413. * @param func1 入参:无 出参:值 generateRandomValue()
  414. * @param func2 入参:旧值、新值 出参:无 logValueChange(oldValue, newValue)
  415. */
  416. function monitorAndAct(func1, func2) {
  417. let oldValue = func1();
  418. function checkValue() {
  419. const newValue = func1();
  420. if (newValue !== oldValue) {
  421. // 值已改变,执行func2并传入旧值和新值
  422. func2(oldValue, newValue);
  423. // 更新旧值为新值
  424. oldValue = newValue;
  425. }
  426. // 持续检查,这里假设每隔1秒检查一次
  427. setTimeout(checkValue, 1000);
  428. }
  429. // 启动检查过程
  430. checkValue();
  431. }
  432. function showTextOnTopRight(text) {
  433. let id1 = "showTextOnTopRight";
  434. // 删除之前的
  435. let oldDiv = document.getElementById(id1);
  436. if (oldDiv) {
  437. document.body.removeChild(oldDiv);
  438. }
  439. // 在屏幕右上角显示一个文本
  440. let div = document.createElement("div");
  441. // 设置id
  442. div.id = id1;
  443. div.style.position = "fixed";
  444. div.style.top = "8px";
  445. div.style.right = "8px";
  446. div.style.padding = "5px";
  447. div.style.backgroundColor = "#fff";
  448. div.style.border = "1px solid #ccc";
  449. div.style.borderRadius = "5px";
  450. div.style.zIndex = "9999";
  451. // 字体大小
  452. div.style.fontSize = "12px";
  453. div.innerHTML = text;
  454. document.body.appendChild(div);
  455. }
  456. function showBusyText() {
  457. showTextOnTopRight("同步中...");
  458. }
  459. function showDoneText() {
  460. showTextOnTopRight("✅");
  461. }
  462. // dispatchEventClick('.btn') 触发点击事件
  463. function dispatchEventClick(cssSelector) {
  464. let dom = document.querySelector(cssSelector);
  465. if (!dom) {
  466. console.log('dom is null');
  467. return;
  468. }
  469. const event = new Event('click');
  470. dom.dispatchEvent(event);
  471. }