init
This commit is contained in:
300
fuck_usr.js
Normal file
300
fuck_usr.js
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
; (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()
|
||||||
|
})()
|
||||||
Reference in New Issue
Block a user