bsn-libs

工具箱

当前为 2025-02-27 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/520145/1544015/bsn-libs.js

  1. /** 获取本地存储 */
  2. window.getLocalStorage = function (key, defaultValue) {
  3. return lscache.get(key) ?? defaultValue;
  4. };
  5.  
  6. /** 设置本地存储 */
  7. window.setLocalStorage = function (key, value) {
  8. lscache.set(key, value);
  9. };
  10.  
  11. /** 睡眠 */
  12. window.sleep = function (time) {
  13. return new Promise(resolve => setTimeout(resolve, time));
  14. };
  15.  
  16. /** 获取粘贴板文字 */
  17. window.getClipboardText = async function () {
  18. if (navigator.clipboard && navigator.clipboard.readText) {
  19. const text = await navigator.clipboard.readText();
  20. return text;
  21. }
  22. return '';
  23. };
  24.  
  25. /** 设置粘贴板文字 */
  26. window.setClipboardText = async function (data) {
  27. if (navigator.clipboard && navigator.clipboard.writeText) {
  28. await navigator.clipboard.writeText(data);
  29. }
  30. };
  31.  
  32. /** 查找所有满足条件的元素 */
  33. window.findAll = function (options) {
  34. const { selectors, parent, findTargets } = options;
  35. const parentEl =
  36. parent && parent.tagName.toLocaleLowerCase() === 'iframe'
  37. ? parent.contentDocument.body
  38. : parent;
  39. const eles = Array.from((parentEl ?? document.body).querySelectorAll(selectors));
  40. return findTargets ? findTargets(eles) : eles;
  41. };
  42.  
  43. /** 查找第一个满足条件的元素 */
  44. window.find = function (options) {
  45. const eles = window.findAll(options);
  46. return eles.length > 0 ? eles[0] : null;
  47. };
  48.  
  49. /** 查找最后一个满足条件的元素 */
  50. window.findLast = function (options) {
  51. const eles = window.findAll(options);
  52. return eles.length > 0 ? eles[eles.length - 1] : null;
  53. };
  54.  
  55. /** 模拟点击 */
  56. window.simulateClick = function (element, original) {
  57. if (original) {
  58. element.click();
  59. } else {
  60. ['mousedown', 'click', 'mouseup'].forEach(mouseEventType =>
  61. element.dispatchEvent(
  62. new MouseEvent(mouseEventType, {
  63. bubbles: true,
  64. cancelable: true,
  65. buttons: 1
  66. })
  67. )
  68. );
  69. }
  70. };
  71.  
  72. /** 模拟输入 */
  73. window.simulateInput = function (element, val, original) {
  74. const lastValue = element.value;
  75. element.value = val;
  76. if (original) {
  77. element.dispatchEvent(new Event('keydown'));
  78. element.dispatchEvent(new Event('keypress'));
  79. element.dispatchEvent(new Event('input'));
  80. element.dispatchEvent(new Event('keyup'));
  81. element.dispatchEvent(new Event('change'));
  82. } else {
  83. const event = new Event('input', {
  84. bubbles: true
  85. });
  86. let keyPress = new KeyboardEvent('keyup', {
  87. bubbles: true,
  88. key: 'enter'
  89. });
  90. // hack React15
  91. event.simulated = true;
  92. // hack React16
  93. const tracker = element._valueTracker;
  94. if (tracker) {
  95. tracker.setValue(lastValue);
  96. }
  97. element.dispatchEvent(event);
  98. element.dispatchEvent(keyPress);
  99. }
  100. };
  101.  
  102. /**
  103. * 模拟操作
  104. * actions: [
  105. * {
  106. * type: 'sleep',
  107. * time: 1000
  108. * },
  109. * {
  110. * type: 'focus',
  111. * selectors: '',
  112. * parent?: HTMLELEMENT,
  113. * findTarget?: els => undefined,
  114. * waiting?: 1000,
  115. * nextSteps?: []
  116. * },
  117. * {
  118. * type: 'input',
  119. * selectors: '',
  120. * value: '',
  121. * parent?: HTMLELEMENT,
  122. * findTarget?: els => undefined,
  123. * waiting?: 1000,
  124. * focusable?: true,
  125. * original?: true,
  126. * nextSteps?: []
  127. * },
  128. * {
  129. * type: 'click',
  130. * selectors: '',
  131. * parent?: HTMLELEMENT,
  132. * findTarget?: els => undefined,
  133. * waiting?: 1000,
  134. * focusable?: true,
  135. * original?: true,
  136. * nextSteps?: []
  137. * }
  138. * ]
  139. */
  140. window.simulateOperate = async function (actions) {
  141. for (const action of actions) {
  142. if (action.type === 'sleep') {
  143. await sleep(action.time);
  144. } else {
  145. const { selectors, parent, waiting, findTarget } = action;
  146. if (waiting) await sleep(waiting);
  147. const parentEl =
  148. parent && parent.tagName.toLocaleLowerCase() === 'iframe'
  149. ? parent.contentDocument.body
  150. : parent;
  151. const eles = Array.from((parentEl ?? document.body).querySelectorAll(selectors));
  152. if (eles.length === 0) return;
  153. const target = findTarget ? findTarget(eles) : eles[0];
  154. if (!target) return;
  155. switch (action.type) {
  156. case 'focus':
  157. target.focus();
  158. break;
  159. case 'input':
  160. if (action.focusable) target.focus();
  161. window.simulateInput(target, action.value, action.original);
  162. break;
  163. case 'click':
  164. window.simulateClick(target, action.original);
  165. break;
  166. }
  167. action.nextSteps?.();
  168. }
  169. }
  170. };
  171.  
  172. /** 创建naive对话框(增加异步功能且只能在组件的setup函数里调用) */
  173. window.createNaiveDialog = function () {
  174. const dialog = naive.useDialog();
  175. ['create', 'error', 'info', 'success', 'warning'].forEach(x => {
  176. dialog[x + 'Async'] = options => {
  177. return new Promise(resolve => {
  178. dialog[x]({
  179. ...options,
  180. onNegativeClick: () => resolve(false),
  181. onPositiveClick: () => resolve(true)
  182. });
  183. });
  184. };
  185. });
  186. return dialog;
  187. };
  188.  
  189. /** 初始化Vue3(包括naive及自定义BTable组件) */
  190. window.initVue3 = function (com) {
  191. const style = document.createElement('style');
  192. style.type = 'text/css';
  193. style.innerHTML = `
  194. body {
  195. text-align: left;
  196. }
  197. .app-wrapper .btn-toggle {
  198. position: fixed;
  199. top: 50vh;
  200. right: 0;
  201. padding-left: 12px;
  202. padding-bottom: 4px;
  203. transform: translateX(calc(100% - 32px)) translateY(-50%);
  204. }
  205. .drawer-wrapper .n-form {
  206. margin: 0 8px;
  207. }
  208. .drawer-wrapper .n-form .n-form-item {
  209. margin: 8px 0;
  210. }
  211. .drawer-wrapper .n-form .n-form-item .n-space {
  212. flex: 1;
  213. }
  214. .drawer-wrapper .n-form .n-form-item .n-input-number {
  215. width: 100%;
  216. }
  217. `;
  218. document.getElementsByTagName('head').item(0).appendChild(style);
  219. const el = document.createElement('div');
  220. el.innerHTML = `<div id="app" class="app-wrapper"></div>`;
  221. el.style.backgroundColor = 'transparent';
  222. el.style.border = 'none';
  223. document.body.append(el);
  224. el.popover = 'manual';
  225. el.showPopover();
  226.  
  227. const BTable = {
  228. template: `
  229. <table cellspacing="0" cellpadding="0">
  230. <tr v-for="(row, rowIndex) in rows">
  231. <td v-for="cell in row" :rowspan="cell.rowspan" :colspan="cell.colspan" :width="cell.width" :class="cell.class">
  232. <slot :cell="cell">{{cell.value}}</slot>
  233. </td>
  234. </tr>
  235. </table>
  236. `,
  237. props: {
  238. rowCount: Number,
  239. columns: Array, // [{ key: "", label: "", width: "100px", unit: "", editable: false }]
  240. cells: Array // [{ row: 0, col: 0, rowspan: 1, colspan: 1, value: "", useColumnLabel: false }]
  241. },
  242. setup(props) {
  243. const data = Vue.reactive({
  244. rows: Vue.computed(() => {
  245. const arr1 = [];
  246. for (let i = 0; i < props.rowCount; i++) {
  247. const arr2 = [];
  248. for (let j = 0; j < props.columns.length; j++) {
  249. const column = props.columns[j];
  250. const cell = props.cells.find(x => x.row === i && x.col === j);
  251. if (cell) {
  252. const colspan = cell.colspan ?? 1;
  253. arr2.push({
  254. ...cell,
  255. rowspan: cell.rowspan ?? 1,
  256. colspan: colspan,
  257. value: cell.useColumnLabel ? column.label : cell.value,
  258. width: colspan > 1 ? undefined : column.width,
  259. column: column
  260. });
  261. }
  262. }
  263. arr1.push(arr2);
  264. }
  265. return arr1;
  266. })
  267. });
  268. return data;
  269. }
  270. };
  271. const app = Vue.createApp({
  272. template: `
  273. <n-dialog-provider>
  274. <n-message-provider>
  275. <n-button v-if="!showDrawer" class="btn-toggle" type="primary" round @click="showDrawer=true">
  276. <template #icon>⇆</template>
  277. </n-button>
  278. <n-drawer v-model:show="showDrawer" display-directive="show" resizable class="drawer-wrapper">
  279. <com @closeDrawer="showDrawer=false"/>
  280. </n-drawer>
  281. </n-message-provider>
  282. </n-dialog-provider>
  283. `,
  284. setup() {
  285. const data = Vue.reactive({
  286. showDrawer: false
  287. });
  288. return data;
  289. }
  290. });
  291. app.use(naive);
  292. app.component('b-table', BTable);
  293. app.component('com', com);
  294. app.mount('#app');
  295. };
  296.  
  297. //#region 扩展
  298. Object.typedAssign = Object.assign;
  299. Object.typedKeys = Object.keys;
  300. Object.toArray = obj => {
  301. const keys = Object.keys(obj);
  302. return keys.map(x => ({ key: x, value: obj[x] }));
  303. };
  304. Object.deepClone = function (target) {
  305. if (typeof target !== 'object' || target === null) return target;
  306. if (target instanceof Date) {
  307. return new Date(target.getTime());
  308. }
  309. if (target instanceof RegExp) {
  310. return new RegExp(target);
  311. }
  312. if (target instanceof Array) {
  313. return target.map(x => Object.deepClone(x));
  314. }
  315. // 对象
  316. const newObj = Object.create(
  317. Reflect.getPrototypeOf(target),
  318. Object.getOwnPropertyDescriptors(target)
  319. );
  320. Reflect.ownKeys(target).forEach(key => {
  321. newObj[key] = Object.deepClone(target[key]);
  322. });
  323. return newObj;
  324. };
  325.  
  326. const compare = (item1, item2) =>
  327. typeof item1 === 'string' && typeof item2 === 'string'
  328. ? item1.localeCompare(item2, 'zh')
  329. : item1 > item2
  330. ? 1
  331. : item2 > item1
  332. ? -1
  333. : 0;
  334. Array.prototype.flatTreeNode = function () {
  335. const arr = [];
  336. for (const node of this) {
  337. arr.push(node);
  338. if (node.children instanceof Array) {
  339. arr.push(...node.children.flatTreeNode());
  340. }
  341. }
  342. return arr;
  343. };
  344. Array.prototype.traverseTreeNode = function (callback) {
  345. for (const node of this) {
  346. callback(node);
  347. if (node.children instanceof Array) {
  348. node.children.traverseTreeNode(callback);
  349. }
  350. }
  351. };
  352. Array.prototype.findTreeNodePath = function (match) {
  353. for (const node of this) {
  354. if (match(node)) {
  355. return [node];
  356. }
  357. if (node.children instanceof Array) {
  358. const result = node.children.findTreeNodePath(match);
  359. if (result) {
  360. return [node, ...result];
  361. }
  362. }
  363. }
  364. return undefined;
  365. };
  366. Array.prototype.localSort = function () {
  367. return this.sort(compare);
  368. };
  369. Array.prototype.sortBy = function (predicate) {
  370. return this.sort((a, b) => compare(predicate(a), predicate(b)));
  371. };
  372. Array.prototype.sortByDescending = function (predicate) {
  373. return this.sort((a, b) => -compare(predicate(a), predicate(b)));
  374. };
  375. Array.prototype.orderBy = function (predicate) {
  376. return [...this].sort((a, b) => compare(predicate(a), predicate(b)));
  377. };
  378. Array.prototype.orderByDescending = function (predicate) {
  379. return [...this].sort((a, b) => -compare(predicate(a), predicate(b)));
  380. };
  381. Array.prototype.orderByMany = function (predicates) {
  382. return [...this].sort((a, b) => {
  383. for (const predicate of predicates) {
  384. const result = compare(predicate(a), predicate(b));
  385. if (result) {
  386. return result;
  387. }
  388. }
  389. return 0;
  390. });
  391. };
  392. Array.prototype.orderByManyDescending = function (predicates) {
  393. return [...this].sort((a, b) => {
  394. for (const predicate of predicates) {
  395. const result = -compare(predicate(a), predicate(b));
  396. if (result) {
  397. return result;
  398. }
  399. }
  400. return 0;
  401. });
  402. };
  403. Array.prototype.first = function (predicate) {
  404. const arr = predicate === undefined ? this : this.filter(predicate);
  405. return arr[0];
  406. };
  407. Array.prototype.firstOrDefault = function (predicate) {
  408. const arr = predicate === undefined ? this : this.filter(predicate);
  409. return arr.length === 0 ? undefined : arr[0];
  410. };
  411. Array.prototype.groupBy = function (predicate) {
  412. const obj = this.reduce((acc, obj) => {
  413. const key = predicate(obj) ?? '';
  414. if (!acc[key]) {
  415. acc[key] = [];
  416. }
  417. acc[key].push(obj);
  418. return acc;
  419. }, {});
  420. return Object.typedKeys(obj).map(x => ({
  421. key: x,
  422. items: obj[x]
  423. }));
  424. };
  425.  
  426. Array.prototype.clear = function () {
  427. this.length = 0;
  428. };
  429. Array.prototype.remove = function (item) {
  430. for (let i = this.length - 1; i >= 0; i--) {
  431. if (this[i] === item) {
  432. this.splice(i, 1);
  433. }
  434. }
  435. };
  436. Array.prototype.removeRange = function (items) {
  437. for (let i = this.length - 1; i >= 0; i--) {
  438. if (items.indexOf(this[i]) >= 0) {
  439. this.splice(i, 1);
  440. }
  441. }
  442. };
  443. Array.prototype.unique = function () {
  444. const hash = [];
  445. for (let i = 0; i < this.length; i++) {
  446. let isOk = true;
  447. for (let j = 0; j < i; j++) {
  448. if (this[i] === this[j]) {
  449. isOk = false;
  450. break;
  451. }
  452. }
  453. if (isOk) {
  454. hash.push(this[i]);
  455. }
  456. }
  457. return hash;
  458. };
  459. Array.prototype.sum = function (deep) {
  460. let total = 0;
  461. for (const item of this) {
  462. if (typeof item === 'number') {
  463. total += item;
  464. } else if (deep && item instanceof Array) {
  465. total += item.sum(true);
  466. }
  467. }
  468. return total;
  469. };
  470. Array.prototype.average = function () {
  471. let total = 0;
  472. let k = 0;
  473. for (const item of this) {
  474. if (typeof item === 'number') {
  475. total += item;
  476. k++;
  477. }
  478. }
  479. return k === 0 ? undefined : total / k;
  480. };
  481. Array.prototype.swap = function (oldIndex, newIndex) {
  482. this.splice(newIndex, 0, this.splice(oldIndex, 1)[0]);
  483. return this;
  484. };
  485. Array.prototype.max = function (predicate) {
  486. return this.length === 0
  487. ? undefined
  488. : predicate === undefined
  489. ? this.reduce((max, current) => {
  490. return current > max ? current : max;
  491. })
  492. : this.reduce((max, current) => {
  493. return predicate(current) > predicate(max) ? current : max;
  494. });
  495. };
  496. Array.prototype.min = function (predicate) {
  497. return this.length === 0
  498. ? undefined
  499. : predicate === undefined
  500. ? this.reduce((min, current) => {
  501. return current < min ? current : min;
  502. })
  503. : this.reduce((min, current) => {
  504. return predicate(current) < predicate(min) ? current : min;
  505. });
  506. };
  507. Array.prototype.mapObject = function (mapKey, mapValue) {
  508. return Object.fromEntries(this.map((el, i, arr) => [mapKey(el, i, arr), mapValue(el, i, arr)]));
  509. };
  510.  
  511. Number.prototype.angleToRadian = function () {
  512. const value = Number(this);
  513. return (value * Math.PI) / 180;
  514. };
  515. Number.prototype.radianToAngle = function () {
  516. const value = Number(this);
  517. return (180 * value) / Math.PI;
  518. };
  519. Number.prototype.fillZero = function (length) {
  520. const value = Number(this);
  521. return Number.isInteger(value) && value.toString().length < length
  522. ? ('0'.repeat(length) + value).slice(-length)
  523. : value.toString();
  524. };
  525. Number.prototype.toThousands = function (unit, withSpaceBetween = true, prepend) {
  526. return this.toString().toThousands(unit, withSpaceBetween, prepend);
  527. };
  528. Number.prototype.toPercentage = function (fractionDigits) {
  529. const value = Number(this);
  530. return `${(value * 100).toFixed(fractionDigits)}%`;
  531. };
  532. Number.prototype.simplify = function (chinese, fractionDigits = 2) {
  533. const value = Number(this);
  534. const units = chinese ? ['万', '亿'] : ['million', 'billion'];
  535. const divisors = chinese ? [10_000, 100_000_000] : [1_000_000, 1_000_000_000];
  536. const index = value < divisors[1] ? 0 : 1;
  537. const num = (value / divisors[index]).toFixed(fractionDigits);
  538. const result = Number(num);
  539. return result === 0 ? '0' : result + ' ' + units[index];
  540. };
  541. Number.prototype.accurate = function (precision = 2) {
  542. const value = Number(this);
  543. if (precision >= 0) {
  544. return Number(value.toFixed(precision));
  545. }
  546. const num = Math.pow(10, precision);
  547. return Number((value * num).toFixed(0)) / num;
  548. };
  549. Number.prototype.toPageCount = function (pageSize) {
  550. const value = Number(this);
  551. return Math.floor(Math.abs(value - 1) / pageSize) + 1;
  552. };
  553.  
  554. String.prototype.replaceAll = function (find, replace) {
  555. return this.replace(new RegExp(find, 'g'), replace);
  556. };
  557. String.prototype.equals = function (value, ignoreCase = true) {
  558. const txt = value ?? '';
  559. return ignoreCase ? this.toLowerCase() === txt.toLowerCase() : this === txt;
  560. };
  561. String.prototype.toThousands = function (unit, withSpaceBetween = true, prepend) {
  562. const value = this;
  563. const index = value.indexOf('.');
  564. const firstPart = index >= 0 ? value.substring(0, index) : value;
  565. const lastPart = index >= 0 ? value.substring(index) : '';
  566. const result = firstPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + lastPart;
  567. return unit
  568. ? prepend
  569. ? withSpaceBetween
  570. ? `${unit} ${result}`
  571. : unit + result
  572. : withSpaceBetween
  573. ? `${result} ${unit}`
  574. : result + unit
  575. : result;
  576. };
  577. //#endregion

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址