bsn-libs

工具箱

Version vom 08.12.2024. Aktuellste Version

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyforks.org/scripts/520145/1499228/bsn-libs.js

/** 分组 */
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.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 eles = Array.from((parent ?? 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 'input':
      case 'click':
        const { selectors, value, parent, findTarget } = action
        const eles = Array.from((parent ?? 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 === 'input') {
            target.value = value
            target.dispatchEvent(new InputEvent('input'))
          } else {
            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 = `
  .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>`
  document.body.append(el)

  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 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')
}
长期地址
遇到问题?请前往 GitHub 提 Issues。