此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.cloud/scripts/520145/1499997/bsn-libs.js
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
(我已经安装了用户样式管理器,让我安装!)
/** 分组 */
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 'focus':
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 === 'focus') {
target.focus();
} else 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');
};