您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
工具箱
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/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') }