bsn-libs

工具箱

Verzia zo dňa 10.12.2024. Pozri najnovšiu verziu.

Tento skript by nemal byť nainštalovaný priamo. Je to knižnica pre ďalšie skripty, ktorú by mali používať cez meta príkaz // @require https://update.greasyfork.org/scripts/520145/1500045/bsn-libs.js

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

/** 分组 */
window.groupBy = function (arr, predicate) {
  const obj = arr.reduce((acc, obj) => {
    const key = predicate(obj) ?? '';
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
  return Object.keys(obj).map(x => ({
    key: x,
    items: obj[x]
  }));
};

/** 睡眠 */
window.sleep = function (time) {
  return new Promise(resolve => setTimeout(resolve, time));
};

/** 设置粘贴板文字 */
window.setClipboardText = async function (data) {
  if (navigator.clipboard && navigator.clipboard.writeText) {
    await navigator.clipboard.writeText(data);
  }
};

/** 获取粘贴板文字 */
window.getClipboardText = async function () {
  if (navigator.clipboard && navigator.clipboard.readText) {
    const text = await navigator.clipboard.readText();
    return text;
  }
  return '';
};

/** 查找所有满足条件的元素 */
window.findAll = function (options) {
  const { selectors, parent, findTarget } = options;
  const parentEl =
    parent && parent.tagName.toLocaleLowerCase() === 'iframe'
      ? parent.contentDocument.body
      : parent;
  const eles = Array.from((parentEl ?? document.body).querySelectorAll(selectors));
  return findTarget ? eles.map((el, index) => findTarget(el, index)).filter(x => x) : eles;
};

/** 查找第一个满足条件的元素 */
window.find = function (options) {
  const eles = window.findAll(options);
  return eles.length > 0 ? eles[0] : null;
};

/** 查找最后一个满足条件的元素 */
window.findLast = function (options) {
  const eles = window.findAll(options);
  return eles.length > 0 ? eles[eles.length - 1] : null;
};

/** 模拟操作 */
window.simulateOperate = async function (actions) {
  for (const action of actions) {
    switch (action.type) {
      case 'sleep':
        await sleep(action.time);
        break;
      case 'focus':
      case 'input':
      case 'click':
        const { selectors, value, parent, waiting, findTarget } = action;
        if (waiting) await sleep(waiting);
        const parentEl =
          parent && parent.tagName.toLocaleLowerCase() === 'iframe'
            ? parent.contentDocument.body
            : parent;
        const eles = Array.from((parentEl ?? document.body).querySelectorAll(selectors));
        const targets = findTarget
          ? eles.map((el, index) => findTarget(el, index)).filter(x => x)
          : eles;
        if (targets.length > 0) {
          const target = targets[0];
          if (action.type === 'focus' || action.focusable) target.focus();
          if (action.type === 'input') {
            target.value = value;
            target.dispatchEvent(new Event('keydown'));
            target.dispatchEvent(new Event('keypress'));
            target.dispatchEvent(new Event('input'));
            target.dispatchEvent(new Event('keyup'));
            target.dispatchEvent(new Event('change'));
          } else if (action.type === 'click') {
            target.click();
          }
        }
        break;
    }
  }
};

/** 创建naive对话框(增加异步功能且只能在组件的setup函数里调用) */
window.createNaiveDialog = function () {
  const dialog = naive.useDialog();
  ['create', 'error', 'info', 'success', 'warning'].forEach(x => {
    dialog[x + 'Async'] = options => {
      return new Promise(resolve => {
        dialog[x]({
          ...options,
          onNegativeClick: () => resolve(false),
          onPositiveClick: () => resolve(true)
        });
      });
    };
  });
  return dialog;
};

/** 初始化Vue3(包括naive及自定义BTable组件) */
window.initVue3 = function (com) {
  const style = document.createElement('style');
  style.type = 'text/css';
  style.innerHTML = `
  body {
    text-align: left;
  }
  .app-wrapper .btn-toggle {
    position: fixed;
    top: 50vh;
    right: 0;
    padding-left: 12px;
    padding-bottom: 4px;
    transform: translateX(calc(100% - 32px)) translateY(-50%);
  }
  .drawer-wrapper .n-form {
    margin: 0 8px;
  }
  .drawer-wrapper .n-form .n-form-item {
    margin: 8px 0;
  }
  .drawer-wrapper .n-form .n-form-item .n-space {
    flex: 1;
  }
  .drawer-wrapper .n-form .n-form-item .n-input-number {
    width: 100%;
  }
    `;
  document.getElementsByTagName('head').item(0).appendChild(style);
  const el = document.createElement('div');
  el.innerHTML = `<div id="app" class="app-wrapper"></div>`;
  el.style.backgroundColor = 'transparent';
  el.style.border = 'none';
  document.body.append(el);
  el.popover = 'manual';
  el.showPopover();

  const BTable = {
    template: `
    <table cellspacing="0" cellpadding="0">
      <tr v-for="(row, rowIndex) in rows">
        <td v-for="cell in row" :rowspan="cell.rowspan" :colspan="cell.colspan" :width="cell.width" :class="cell.class">
          <slot :cell="cell">{{cell.value}}</slot>
        </td>
      </tr>
    </table>
      `,
    props: {
      rowCount: Number,
      columns: Array, // [{ key: "", label: "", width: "100px", unit: "", editable: false }]
      cells: Array // [{ row: 0, col: 0, rowspan: 1, colspan: 1, value: "", useColumnLabel: false }]
    },
    setup(props) {
      const data = Vue.reactive({
        rows: Vue.computed(() => {
          const arr1 = [];
          for (let i = 0; i < props.rowCount; i++) {
            const arr2 = [];
            for (let j = 0; j < props.columns.length; j++) {
              const column = props.columns[j];
              const cell = props.cells.find(x => x.row === i && x.col === j);
              if (cell) {
                const colspan = cell.colspan ?? 1;
                arr2.push({
                  ...cell,
                  rowspan: cell.rowspan ?? 1,
                  colspan: colspan,
                  value: cell.useColumnLabel ? column.label : cell.value,
                  width: colspan > 1 ? undefined : column.width,
                  column: column
                });
              }
            }
            arr1.push(arr2);
          }
          return arr1;
        })
      });
      return data;
    }
  };
  const app = Vue.createApp({
    template: `
<n-dialog-provider>
  <n-message-provider>
    <n-button v-if="!showDrawer" class="btn-toggle" type="primary" round @click="showDrawer=true">
      <template #icon>⇆</template>
    </n-button>
  <n-drawer v-model:show="showDrawer" display-directive="show" resizable class="drawer-wrapper">
    <com @closeDrawer="showDrawer=false"/>
  </n-drawer>
  </n-message-provider>
</n-dialog-provider>
`,
    setup() {
      const data = Vue.reactive({
        showDrawer: false
      });
      return data;
    }
  });
  app.use(naive);
  app.component('b-table', BTable);
  app.component('com', com);
  app.mount('#app');
};