Spaces:
Running
Running
File size: 6,349 Bytes
b931367 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
(function() {
// Console 日志
console.info('全局异常捕获脚本已加载,开始监听错误...');
// 1. 初始化错误显示容器 (保留原有的 Visual Toast 功能)
const errorContainer = document.createElement('div');
errorContainer.id = 'global-error';
// 设置样式:全屏、黑色背景、红色文字、置顶
errorContainer.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.9);
color: #ff5555;
z-index: 999999;
overflow-y: auto;
padding: 20px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 14px;
white-space: pre-wrap;
display: none; /* 默认隐藏,有错误时显示 */
box-sizing: border-box;
`;
// 添加标题和关闭提示
const header = document.createElement('div');
header.style.borderBottom = '1px solid #555';
header.style.paddingBottom = '10px';
header.style.marginBottom = '10px';
header.innerHTML = '<h2 style="margin:0; color:#fff;">🚨 全局异常捕获监控</h2><small style="color:#aaa">点击页面任意处可临时关闭蒙层</small>';
errorContainer.appendChild(header);
// 点击关闭功能
errorContainer.addEventListener('click', () => {
errorContainer.style.display = 'none';
});
// 确保 DOM 加载后插入 body,或者如果 body 存在直接插入
if (document.body) {
document.body.appendChild(errorContainer);
} else {
window.addEventListener('DOMContentLoaded', () => document.body.appendChild(errorContainer));
}
// 2. 核心处理函数:既显示在屏幕上,也发送给父窗口
function handleError(type, details) {
// A. 显示在屏幕上 (Visual Toast)
logErrorToScreen(type, details);
// B. 发送给父窗口 (iframe 通信)
postErrorToParent(type, details);
}
// 显示在屏幕上的具体实现
function logErrorToScreen(type, details) {
// 显示蒙层
errorContainer.style.display = 'block';
const reportItem = document.createElement('div');
reportItem.style.marginBottom = '20px';
reportItem.style.borderBottom = '1px dashed #444';
reportItem.style.paddingBottom = '10px';
// 格式化时间
const time = new Date().toLocaleTimeString();
// 构建 HTML 内容
reportItem.innerHTML = `
<div style="color: #fff; font-weight: bold;">[${time}] <span style="background:#b00; padding:2px 5px; border-radius:3px;">${type}</span></div>
<div style="margin-top:5px; color: #ffaaaa;">${details.message || '无错误信息'}</div>
${details.filename ? `<div style="color: #888;">Location: ${details.filename}:${details.lineno}:${details.colno}</div>` : ''}
${details.stack ? `<pre style="color: #aaa; background: #111; padding: 10px; overflow-x: auto; margin-top:5px;">${details.stack}</pre>` : ''}
${details.selector ? `<div style="color: #888;">Element: <${details.selector}> (src: ${details.src})</div>` : ''}
`;
// 插入到标题之后,内容的顶部
header.after(reportItem);
}
// 发送给父窗口的具体实现
function postErrorToParent(type, details) {
if (window.parent && window.parent !== window) {
const msg = {
type: 'iframeError',
payload: {
errorType: type,
message: details.message || 'Unknown Error',
stack: details.stack || '',
filename: details.filename || '',
lineno: details.lineno || 0,
colno: details.colno || 0,
selector: details.selector || '',
src: details.src || '',
href: window.location.href,
timestamp: new Date().toISOString()
}
};
// 使用 '*' 允许跨域传递,或者根据需要指定特定 origin
window.parent.postMessage(msg, '*');
console.info('[CatchError] Posted error to parent:', payload);
}
}
// 3. 监听器 A: 捕获 JS 运行时错误 + 资源加载错误 (img/script)
// 注意:第三个参数 true (useCapture) 是捕获资源错误的关键
window.addEventListener('error', (event) => {
// 情况 1: 资源加载错误 (img, script, link)
// 资源错误没有冒泡,但在捕获阶段可以拦截,且 target 是 DOM 元素
if (event.target && (event.target instanceof HTMLElement)) {
const target = event.target;
handleError('Resource Error', {
message: `资源加载失败 (${target.tagName})`,
selector: target.tagName.toLowerCase(),
src: target.src || target.href || 'unknown source',
stack: 'N/A (Network Error)'
});
}
// 情况 2: 普通 JS 运行时错误
else {
handleError('Runtime Error', {
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error ? event.error.stack : '无堆栈信息'
});
}
}, true); // useCapture = true
// 4. 监听器 B: 捕获未处理的 Promise Rejection
window.addEventListener('unhandledrejection', (event) => {
// 提取错误原因
let reason = event.reason;
let stack = '无堆栈信息';
let message = '';
if (reason instanceof Error) {
message = reason.message;
stack = reason.stack;
} else {
// 如果 reject 的不是 Error 对象(例如 reject("foo"))
message = typeof reason === 'object' ? JSON.stringify(reason) : String(reason);
}
handleError('Unhandled Promise', {
message: `Promise 被 Reject 且未被 Catch: ${message}`,
stack: stack
});
});
console.info('全局异常捕获脚本已初始化完成 (Visual + PostMessage)。');
})();
|