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