// ==UserScript== // @name 移动端开发者工具 // @namespace http://tampermonkey.net/ // @version 0.2 // @description 移动浏览器开发者调试工具,优化网络监控 // @author Your name // @match *://*/* // @grant none // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 样式定义 const styles = ` .devtools-container { position: fixed; top: 0; left: 0; right: 0; height: 50%; background: #fff; z-index: 10000; border-bottom: 1px solid #ccc; display: none; flex-direction: column; font-family: Arial, sans-serif; transition: transform 0.3s ease; transform: translateY(-100%); } .devtools-container.expanded { transform: translateY(0); } .devtools-header { display: flex; padding: 5px; border-bottom: 1px solid #ccc; background: #f5f5f5; } .devtools-tab { padding: 5px 10px; margin-right: 5px; cursor: pointer; border: 1px solid #ccc; border-radius: 3px; font-size: 14px; } .devtools-tab.active { background: #fff; border-bottom: none; } .devtools-content { flex: 1; overflow: auto; padding: 10px; } .devtools-panel { display: none; height: 100%; } .devtools-panel.active { display: block; } .toggle-devtools { position: fixed; top: 10px; right: 10px; z-index: 10001; padding: 5px 10px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px; box-shadow: 0 2px 5px rgba(0,0,0,0.2); } .expand-collapse { position: absolute; bottom: -20px; left: 50%; transform: translateX(-50%); background: #4CAF50; color: white; border: none; border-radius: 0 0 5px 5px; padding: 2px 10px; cursor: pointer; z-index: 10001; } .element-highlight { position: absolute; background: rgba(137, 196, 244, 0.3); border: 1px solid #89C4F4; pointer-events: none; z-index: 9999; } .console-input { width: 100%; padding: 5px; border: 1px solid #ccc; } .console-output { margin: 5px 0; padding: 5px; background: #f5f5f5; border-radius: 3px; } .network-item { padding: 10px; border-bottom: 1px solid #eee; cursor: pointer; } .network-item:hover { background-color: #f0f0f0; } .network-details { display: none; padding: 10px; background: #f9f9f9; margin-top: 5px; border-radius: 4px; } .network-item.expanded .network-details { display: block; } .network-summary { display: flex; justify-content: space-between; } .network-summary > div { margin-right: 10px; } .network-details pre { white-space: pre-wrap; word-wrap: break-word; background: #fff; padding: 8px; border-radius: 4px; border: 1px solid #eee; margin: 5px 0; max-height: 300px; overflow-y: auto; } `; // 添加样式 function addStyle(css) { const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); } // 创建开发者工具UI function createDevToolsUI() { const container = document.createElement('div'); container.className = 'devtools-container'; container.innerHTML = `
${element.outerHTML}
${JSON.stringify(window.getComputedStyle(element), null, 2)}
`;
}
document.addEventListener('mousemove', (e) => {
if (!elementsPanel.classList.contains('active')) return;
const element = document.elementFromPoint(e.clientX, e.clientY);
if (element && !element.closest('.devtools-container')) {
updateHighlight(element);
highlight.style.display = 'block';
}
});
document.addEventListener('click', (e) => {
if (!elementsPanel.classList.contains('active')) return;
const element = document.elementFromPoint(e.clientX, e.clientY);
if (element && !element.closest('.devtools-container')) {
e.preventDefault();
showElementInfo(element);
}
});
}
// 控制台功能
function initConsole(consolePanel) {
const input = consolePanel.querySelector('.console-input');
const outputContainer = consolePanel.querySelector('.console-output-container');
function log(message, type = 'log') {
const output = document.createElement('div');
output.className = `console-output ${type}`;
output.textContent = message;
outputContainer.appendChild(output);
outputContainer.scrollTop = outputContainer.scrollHeight;
}
const originalConsole = {
log: console.log,
error: console.error,
warn: console.warn
};
console.log = function(...args) {
log(args.join(' '));
originalConsole.log.apply(console, args);
};
console.error = function(...args) {
log(args.join(' '), 'error');
originalConsole.error.apply(console, args);
};
console.warn = function(...args) {
log(args.join(' '), 'warn');
originalConsole.warn.apply(console, args);
};
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
try {
const result = eval(input.value);
log(`> ${input.value}`);
log(result);
} catch (error) {
log(error.message, 'error');
}
input.value = '';
}
});
}
// 网络请求监控
function initNetworkMonitor(networkPanel) {
const originalFetch = window.fetch;
const originalXHR = window.XMLHttpRequest.prototype.open;
function formatHeaders(headers) {
if (headers instanceof Headers) {
return Array.from(headers.entries())
.map(([key, value]) => `${key}: ${value}`)
.join('\n');
} else if (typeof headers === 'string') {
// 处理XMLHttpRequest的getAllResponseHeaders()返回的字符串
return headers.split('\r\n').filter(line => line.trim()).join('\n');
} else if (headers && typeof headers === 'object') {
return Object.entries(headers)
.map(([key, value]) => `${key}: ${value}`)
.join('\n');
}
return 'No headers available';
}
function formatBody(body, contentType) {
if (contentType && contentType.includes('application/json')) {
try {
return JSON.stringify(JSON.parse(body), null, 2);
} catch (e) {
return body;
}
}
return body;
}
function createNetworkLogItem(requestInfo, responseInfo) {
const item = document.createElement('div');
item.className = 'network-item';
item.innerHTML = `
Request URL: ${requestInfo.url}
Request Method: ${requestInfo.method}
Status Code: ${responseInfo.status}
Remote Address: ${location.hostname}
Referrer Policy: ${document.referrer}
${formatHeaders(responseInfo.headers)}
${formatHeaders(requestInfo.headers)}
${formatBody(responseInfo.body, responseInfo.headers['content-type'] || responseInfo.headers.get('content-type'))}
${rules.map(rule => rule.cssText).join('\n')}
无法访问样式表 ${index + 1} (CORS限制)
`; } }); stylesPanel.innerHTML = html; } updateStyles(); } // 初始化开发者工具 function initDevTools() { addStyle(styles); const {container, toggleButton} = createDevToolsUI(); const elementsPanel = container.querySelector('.elements-panel'); const consolePanel = container.querySelector('.console-panel'); const networkPanel = container.querySelector('.network-panel'); const stylesPanel = container.querySelector('.styles-panel'); const expandCollapseBtn = container.querySelector('.expand-collapse'); initElementInspector(elementsPanel); initConsole(consolePanel); initNetworkMonitor(networkPanel); initStylesPanel(stylesPanel); // 标签切换 container.querySelector('.devtools-header').addEventListener('click', (e) => { if (e.target.classList.contains('devtools-tab')) { const panelName = e.target.dataset.panel; container.querySelectorAll('.devtools-tab').forEach(tab => tab.classList.remove('active')); container.querySelectorAll('.devtools-panel').forEach(panel => panel.classList.remove('active')); e.target.classList.add('active'); container.querySelector(`.${panelName}-panel`).classList.add('active'); } }); // 展开/收起面板 expandCollapseBtn.addEventListener('click', () => { container.classList.toggle('expanded'); expandCollapseBtn.textContent = container.classList.contains('expanded') ? '收起' : '展开'; }); // 显示/隐藏开发者工具 toggleButton.addEventListener('click', () => { container.style.display = container.style.display === 'none' ? 'flex' : 'none'; if (container.style.display === 'flex') { container.classList.add('expanded'); expandCollapseBtn.textContent = '收起'; } }); } // 页面加载完成后初始化 window.addEventListener('load', initDevTools); })();