Files
use/fuck_usr.js
2025-07-01 14:20:39 +08:00

301 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
; (async () => {
/**
* @typedef {Object} Gateway
* @property {string} devName - device name
* @property {Element} btn - button element
* @property {string} sn - serial number
*/
/**
* Centralised timeout/interval constants (milliseconds).
*/
const WAIT = {
POLL_INTERVAL: 100,
PANEL_FADE: 100,
PANEL_VISIBLE_TIMEOUT: 600,
BUTTON_FIND: 5000,
CONFIG_DIALOG_VISIBLE: 8000,
TEMPLATE_LIST_RENDER: 200,
DROPDOWN_CLOSE: 100,
MSGBOX_APPEAR: 2000,
DIALOG_CLOSE: 8000,
DETAIL_PAGE_RENDER: 15000,
TABLE_ROWS_RENDER: 15000,
}
/**
* Clicks through: 参数信息 → 参数配置 → 参数模板配置
*/
async function openTemplateConfig() {
// 1. Click the "参数信息" tab
const paramTab = document.getElementById('tab-parameterInfo')
|| [...document.querySelectorAll('.el-tabs__item')]
.find(el => el.textContent.trim() === '参数信息')
if (!paramTab) throw new Error('无法找到"参数信息"标签')
if (!paramTab.classList.contains('is-active')) {
// Some implementations bind click on the inner <div id="step1">,
// so try it first, then fall back to the wrapper.
const inner = paramTab.querySelector('#step1, .el-tabs-padding8')
; (inner ?? paramTab).click()
// wait for its panel to become visible
await new Promise(res => {
const panel = document.getElementById('pane-parameterInfo')
const mo = new MutationObserver(() => {
if (panel.style.display !== 'none') {
mo.disconnect()
// give any fade-in a moment
setTimeout(res, WAIT.PANEL_FADE)
}
})
mo.observe(panel, { attributes: true, attributeFilter: ['style'] })
// fallback
setTimeout(() => { mo.disconnect(); res() }, WAIT.PANEL_VISIBLE_TIMEOUT)
})
}
// 2. Wait for并点击 "参数配置" 按钮
const cfgBtn = await waitFor(() =>
[...document.querySelectorAll('#pane-parameterInfo button')]
.find(b => b.textContent.trim() === '参数配置'),
WAIT.BUTTON_FIND,
)
cfgBtn.click()
// 3. Wait for the configuration dialog to appear
await waitFor(() => {
const dlgWrapper = document.querySelector('.configurationDialog')
return dlgWrapper && getComputedStyle(dlgWrapper).display !== 'none'
}, WAIT.CONFIG_DIALOG_VISIBLE)
// 4. Click the "参数模板配置" radio
const radio = [...document.querySelectorAll('.configurationDialog .el-radio-button')]
.find(r => r.textContent.trim() === '参数模板配置')
if (!radio) throw new Error('无法找到"参数模板配置"项')
radio.click()
}
/**
* After 参数模板配置 is active, select a named template, apply it, and confirm the message box.
* @param {string} templateName — e.g. "gw_template_01"
*/
async function applyParameterTemplate(templateName = 'gw_template_01') {
// 1. Open the template-config UI
await openTemplateConfig() // assumes you've already defined openTemplateConfig()
// 2. Click the 参数配置 dropdown
const selectInput = document.querySelector('.configurationDialog input[placeholder*="参数模板"]')
if (!selectInput) throw new Error('找不到参数模板下拉框')
selectInput.click()
// 3. Wait a moment for the list to render
await new Promise(r => setTimeout(r, WAIT.TEMPLATE_LIST_RENDER))
// 4. Pick the desired template
const option = Array.from(document.querySelectorAll('.el-select-dropdown__item'))
.find(li => li.textContent.trim() === templateName)
if (!option) throw new Error(`模板 "${templateName}" 不存在`)
option.click()
// 5. Wait for the dropdown to close
await new Promise(r => setTimeout(r, WAIT.DROPDOWN_CLOSE))
// 6. Click the "配置" button in the dialog footer
const applyBtn = document.querySelector('.configurationDialog .el-dialog__footer .el-button.el-button--primary')
if (!applyBtn) throw new Error('找不到"配置"按钮')
applyBtn.click()
// 7. Wait for the confirmation message box to appear
await new Promise(resolve => {
const selector = '.el-message-box__wrapper[aria-label="提示"]'
const check = () => {
const mb = document.querySelector(selector)
if (mb && getComputedStyle(mb).display !== 'none') {
return setTimeout(resolve, WAIT.MSGBOX_APPEAR)
}
return false
}
if (check()) return
const mo = new MutationObserver(() => {
if (check()) {
mo.disconnect()
}
})
mo.observe(document.body, { childList: true, subtree: true })
// fallback timeout
setTimeout(() => { mo.disconnect(); resolve() }, WAIT.MSGBOX_APPEAR)
})
// 8. Click the "好的" button in the message-box footer
const okBtn = Array.from(document.querySelectorAll('.el-message-box__btns .el-button'))
.find(b => b.textContent.trim().startsWith('好的'))
if (!okBtn) throw new Error('找不到消息框中的"好的"按钮')
okBtn.click()
// 9. Wait for both message box and configuration dialog to disappear
await waitFor(() => {
const msgWrap = document.querySelector('.el-message-box__wrapper[aria-label="提示"]')
const cfgDlg = document.querySelector('.configurationDialog')
const msgHidden = !msgWrap || getComputedStyle(msgWrap).display === 'none'
const dlgHidden = cfgDlg && getComputedStyle(cfgDlg).display === 'none'
return msgHidden && dlgHidden
}, WAIT.DIALOG_CLOSE)
}
/**
* @returns {boolean}
*/
function isConsoleLogNative() {
try {
return console.log.toString().includes('native code')
} catch (e) {
return false
}
}
/**
* @description create a clean log function from iframe
* @returns {Function}
*/
function createCleanLog() {
const f = document.createElement('iframe')
f.style.display = 'none'
document.body.appendChild(f)
return f.contentWindow.console.log.bind(f.contentWindow.console)
}
/**
* @returns {Gateway[]}
*/
function getGateways() {
/**
* @type {Gateway[]}
*/
const gateways = []
const rows = document.querySelectorAll('.el-table__body .el-table__row')
rows.forEach(row => {
const devNameEl = row.querySelector('.DevNameCls')
const snEl = row.querySelectorAll('td')[3] // 4th column is SN
if (devNameEl && snEl) {
const btn = devNameEl
const devName = devNameEl.textContent.trim()
const sn = snEl.textContent.trim()
gateways.push({ devName, btn, sn })
}
})
return gateways
}
/**
* Wait until a predicate returns a truthy value.
* @template T
* @param {() => T} predicate polling function
* @param {number} [timeout=5000] max wait in ms
* @param {number} [interval=100] polling interval in ms
* @returns {Promise<T>}
*/
function waitFor(predicate, timeout = 5000, interval = WAIT.POLL_INTERVAL) {
const start = Date.now()
return new Promise((resolve, reject) => {
const check = () => {
let res
try { res = predicate() } catch { /* ignore */ }
if (res) return resolve(res)
if (Date.now() - start >= timeout) return reject(new Error('waitFor timeout'))
setTimeout(check, interval)
}
check()
})
}
/**
* Waits for a DOM element matching the selector.
* @param {string} selector
* @param {number} [timeout=5000]
* @returns {Promise<Element>}
*/
function waitForElement(selector, timeout = 5000) {
return waitFor(() => document.querySelector(selector), timeout)
}
/**
* Opens gateway details, applies the template, and navigates back.
* @param {string} devName gateway display name
* @param {string} [templateName='gw_template_01']
*/
async function processGateway(devName, templateName = 'gw_template_01') {
console.group(`Processing gateway: ${devName}`)
// Wait for the 参数信息 tab (or any detail-specific element) to appear
await waitFor(() => document.querySelector('#tab-parameterInfo'), WAIT.DETAIL_PAGE_RENDER)
// 2. Run the existing template-application workflow
await applyParameterTemplate(templateName)
// 3. Navigate back to the gateway list via breadcrumb
let listLink = [...document.querySelectorAll('.CompBreadcrumb a')]
.find(a => a.textContent.trim() === '网关列表')
if (!listLink) {
// 有时文字在 <span role="link"> 中
listLink = [...document.querySelectorAll('.CompBreadcrumb [role="link"]')]
.find(el => el.textContent.trim() === '网关列表')
}
if (!listLink) throw new Error('无法找到"网关列表"面包屑链接')
listLink.click()
// 4. Wait until the table rows reappear
await waitFor(() => document.querySelectorAll('.el-table__body .el-table__row').length > 0, WAIT.TABLE_ROWS_RENDER)
console.groupEnd()
}
/**
* Entrypoint iterates over every gateway and applies the template.
*/
async function init() {
// Expose helpers for manual debugging
Object.assign(window, {
openTemplateConfig,
applyParameterTemplate,
processGateway,
waitFor,
waitForElement,
getGateways,
})
if (!isConsoleLogNative()) {
const cleanLog = createCleanLog()
console.log = cleanLog
console.info("re-override console.log")
}
const initialGateways = getGateways()
const names = initialGateways.map(g => g.devName)
window.userState = { names }
console.info('Gateways detected:', names)
for (const devName of names) {
try {
// Re-query each round in case DOM has been re-rendered
const row = [...document.querySelectorAll('.el-table__body .el-table__row')]
.find(r => r.querySelector('.DevNameCls')?.textContent.trim() === devName)
if (!row) {
console.warn(`Row for "${devName}" not found; skipping.`)
continue
}
// Prefer anchor/button inside the DevName cell, else the cell div itself
const btn = row.querySelector('.DevNameCls a, .DevNameCls button') || row.querySelector('.DevNameCls')
if (!btn) {
console.warn(`Gateway button for "${devName}" not found; skipping.`)
continue
}
btn.click()
await processGateway(devName, 'gw_template_01')
} catch (e) {
console.error(`Failed to process gateway "${devName}"`, e)
}
}
console.info('Automation complete.')
}
await init()
})()