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