bsn-utilities

工具箱

当前为 2024-11-18 提交的版本,查看 最新版本

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

  1. // 分组
  2. function groupBy(arr, predicate) {
  3. const obj = arr.reduce((acc, obj) => {
  4. const key = predicate(obj) ?? ''
  5. if (!acc[key]) {
  6. acc[key] = []
  7. }
  8. acc[key].push(obj)
  9. return acc
  10. }, {})
  11. return Object.keys(obj).map(x => ({
  12. key: x,
  13. items: obj[x]
  14. }))
  15. }
  16. // 随眠
  17. function sleep(time) {
  18. return new Promise(resolve => setTimeout(resolve, time))
  19. }
  20. // 修改输入框的值(模拟键盘输入)
  21. function setInputValue(input, value) {
  22. input.value = value;
  23. input.dispatchEvent(new InputEvent('input'));
  24. }
  25. // 获取粘贴板文字
  26. async function getClipboardText() {
  27. if (navigator.clipboard && navigator.clipboard.readText) {
  28. const text = await navigator.clipboard.readText();
  29. return text;
  30. }
  31. return "";
  32. }
  33. // 在HTML_ELEMENT中查找所有满足条件的元素,参数innerText可以是字符串(判断innerText是否相等)或REG
  34. function findAllIn(ele, selectors, innerText) {
  35. const arr = Array.from(ele.querySelectorAll(selectors));
  36. return innerText === undefined
  37. ? arr
  38. : typeof innerText === "string"
  39. ? arr.filter(x => x.innerText.trim() === innerText.trim())
  40. : arr.filter(x => x.innerText.trim().match(innerText));
  41. }
  42. // 在HTML_ELEMENT中查找第一个满足条件的元素,参数innerText可以是字符串(判断innerText是否相等)或REG
  43. function findIn(ele, selectors, innerText) {
  44. const arr = findAllIn(ele, selectors, innerText);
  45. return arr.length > 0 ? arr[0] : null;
  46. }
  47. // 在HTML_ELEMENT中查找最后一个满足条件的元素,参数innerText可以是字符串(判断innerText是否相等)或REG
  48. function findLastIn(ele, selectors, innerText) {
  49. const arr = findAllIn(ele, selectors, innerText);
  50. return arr.length > 0 ? arr[arr.length - 1] : null;
  51. }
  52. // 在document中查找所有满足条件的元素,参数innerText可以是字符串(判断innerText是否相等)或REG
  53. function findAll(selectors, innerText) {
  54. return findAllIn(document, selectors, innerText);
  55. }
  56. // 在document中查找第一个满足条件的元素,参数innerText可以是字符串(判断innerText是否相等)或REG
  57. function find(selectors, innerText) {
  58. return findIn(document, selectors, innerText);
  59. }
  60. // 在document中查找最后一个满足条件的元素,参数innerText可以是字符串(判断innerText是否相等)或REG
  61. function findLast(selectors, innerText) {
  62. return findLastIn(document, selectors, innerText);
  63. }
  64. // 选择下拉选项,input为下拉选项元素,wait为等待时间,optionParentSelectors为选项父元素的选择器,optionSelectors为选项的选择器,matchFunc为匹配函数(满足条件后触发点击操作)
  65. async function selectOptionIn(input, wait, optionParentSelectors, optionSelectors, matchFunc) {
  66. input.click();
  67. await sleep(wait);
  68. const optionParent = optionParentSelectors ? Array.from(findAll(optionParentSelectors)).find(x => x.style.display !== 'none') : document;
  69. const optionEles = findAllIn(optionParent, optionSelectors);
  70. const option = optionEles.find((x, i) => matchFunc(x.innerText, i));
  71. if (option) {
  72. option.click();
  73. }
  74. }
  75. // 选择下拉选项,input为下拉选项元素,wait为等待时间,optionSelectors为选项的选择器,matchFunc为匹配函数(满足条件后触发点击操作)
  76. async function selectOption(input, wait, optionSelectors, matchFunc) {
  77. await selectOptionIn(input, wait, null, optionSelectors, matchFunc);
  78. }
  79. // 创建naive对话框,增加异步功能,只能在组件的setup函数里调用
  80. function createNaiveDialog() {
  81. const dialog = naive.useDialog();
  82. ["create", "error", "info", "success", "warning"].forEach(x => {
  83. dialog[x + "Async"] = options => {
  84. return new Promise((resolve,reject) => {
  85. dialog[x]({
  86. ...options,
  87. onNegativeClick: () => resolve(false),
  88. onPositiveClick: () => {
  89. if (!options.onPositiveClick || options.onPositiveClick() ) resolve(true)
  90. }
  91. });
  92. });
  93. }
  94. });
  95. return dialog;
  96. }
  97. // 初始化Vue3,包括naive及自定义BTable组件
  98. function initVue3(Com) {
  99. const style = document.createElement('style');
  100. style.type = 'text/css';
  101. style.innerHTML=`
  102. .app-wrapper .btn-toggle {
  103. position: fixed;
  104. top: 50vh;
  105. right: 0;
  106. padding-left: 12px;
  107. padding-bottom: 4px;
  108. transform: translateX(calc(100% - 32px)) translateY(-50%);
  109. }
  110. .drawer-wrapper .n-form {
  111. margin: 0 8px;
  112. }
  113. .drawer-wrapper .n-form .n-form-item {
  114. margin: 8px 0;
  115. }
  116. .drawer-wrapper .n-form .n-form-item .n-space {
  117. flex: 1;
  118. }
  119. .drawer-wrapper .n-form .n-form-item .n-input-number {
  120. width: 100%;
  121. }
  122. `;
  123. document.getElementsByTagName('head').item(0).appendChild(style);
  124. const el = document.createElement("div");
  125. el.innerHTML = `
  126. <div id="app" class="app-wrapper"></div>`;
  127. document.body.append(el);
  128.  
  129. const BTable = {
  130. template: `
  131. <table cellspacing="0" cellpadding="0">
  132. <tr v-for="(row, rowIndex) in rows">
  133. <td v-for="cell in row" :rowspan="cell.rowspan" :colspan="cell.colspan" :width="cell.width" :class="cell.class">
  134. <slot :cell="cell">{{cell.value}}</slot>
  135. </td>
  136. </tr>
  137. </table>
  138. `,
  139. props: {
  140. rowCount: Number,
  141. columns: Array, // [{ key: "", label: "", width: "100px", unit: "", editable: false }]
  142. cells: Array // [{ row: 0, col: 0, rowspan: 1, colspan: 1, value: "", useColumnLabel: false }]
  143. },
  144. setup(props) {
  145. const data = Vue.reactive({
  146. rows: Vue.computed(() => {
  147. const arr1 = [];
  148. for(let i = 0; i < props.rowCount; i++) {
  149. const arr2 = [];
  150. for (let j = 0; j < props.columns.length; j++) {
  151. const column = props.columns[j];
  152. const cell = props.cells.find(x => x.row === i && x.col === j);
  153. if (cell) {
  154. const colspan = cell.colspan ?? 1;
  155. arr2.push({
  156. ...cell,
  157. rowspan: cell.rowspan ?? 1,
  158. colspan: colspan,
  159. value: cell.useColumnLabel ? column.label : cell.value,
  160. width: colspan > 1 ? undefined : column.width,
  161. column: column
  162. });
  163. }
  164. }
  165. arr1.push(arr2);
  166. }
  167. return arr1;
  168. })
  169. });
  170. return data;
  171. }
  172. }
  173. const app = Vue.createApp({
  174. template: `
  175. <n-dialog-provider>
  176. <n-message-provider>
  177. <n-button class="btn-toggle" type="primary" round @click="showDrawer=true">
  178. <template #icon>⇆</template>
  179. </n-button>
  180. <n-drawer v-model:show="showDrawer" display-directive="show" resizable class="drawer-wrapper">
  181. <com @closeDrawer="showDrawer=false"/>
  182. </n-drawer>
  183. </n-message-provider>
  184. </n-dialog-provider>
  185. `,
  186. setup() {
  187. const data = Vue.reactive({
  188. showDrawer: false
  189. });
  190. return data;
  191. }
  192. });
  193. app.use(naive);
  194. app.component('b-table', BTable);
  195. app.component('com', Com);
  196. app.mount("#app");
  197. }