feat: 完善外部资源备用机制和日志系统

- 在header.php中添加用户角色检测,传递给前端JavaScript
- 更新Open Sans字体CSS文件,支持本地woff2字体文件备用
- 修改footer.php中MathJax 3/2和KaTeX加载机制,添加onerror备用处理
- 优化resource-loader.js日志系统,使用ArgonLogger替代console.log
- 仅管理员用户显示控制台日志,普通用户和游客不显示调试信息
- 完善资源加载错误处理,统一使用ArgonLogger记录警告信息
This commit is contained in:
2026-01-11 20:37:03 +08:00
parent a01f161bca
commit ea167c379a
17 changed files with 1621 additions and 60 deletions

300
assets/vendor/external/css-fallback.css vendored Normal file
View File

@@ -0,0 +1,300 @@
/* CSS 备用系统 - 确保基本样式始终可用 */
/* 重置样式 - 确保跨浏览器一致性 */
* {
box-sizing: border-box;
}
html {
line-height: 1.15;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 16px;
line-height: 1.6;
color: #333;
background-color: #fff;
}
/* 基本布局 */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
.row {
display: flex;
flex-wrap: wrap;
margin: 0 -15px;
}
.col {
flex: 1;
padding: 0 15px;
}
/* 按钮样式 */
.btn {
display: inline-block;
padding: 8px 16px;
margin: 4px 2px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f8f9fa;
color: #333;
text-decoration: none;
cursor: pointer;
transition: all 0.2s ease;
}
.btn:hover {
background-color: #e9ecef;
border-color: #adb5bd;
}
.btn-primary {
background-color: #007bff;
border-color: #007bff;
color: #fff;
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #004085;
}
/* 卡片样式 */
.card {
background-color: #fff;
border: 1px solid #dee2e6;
border-radius: 4px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card-header {
padding: 12px 20px;
background-color: #f8f9fa;
border-bottom: 1px solid #dee2e6;
font-weight: bold;
}
.card-body {
padding: 20px;
}
/* 表单样式 */
.form-control {
display: block;
width: 100%;
padding: 8px 12px;
font-size: 14px;
line-height: 1.5;
color: #495057;
background-color: #fff;
border: 1px solid #ced4da;
border-radius: 4px;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-control:focus {
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 2px rgba(0,123,255,0.25);
}
/* 导航样式 */
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 20px;
background-color: #f8f9fa;
border-bottom: 1px solid #dee2e6;
}
.navbar-brand {
font-size: 18px;
font-weight: bold;
text-decoration: none;
color: #333;
}
.nav {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.nav-item {
margin: 0 10px;
}
.nav-link {
color: #333;
text-decoration: none;
padding: 8px 12px;
border-radius: 4px;
transition: background-color 0.2s ease;
}
.nav-link:hover {
background-color: #e9ecef;
}
/* 模态框样式 */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.modal.show {
display: flex;
align-items: center;
justify-content: center;
}
.modal-dialog {
background-color: #fff;
border-radius: 4px;
max-width: 500px;
width: 90%;
max-height: 90%;
overflow-y: auto;
}
.modal-header {
padding: 15px 20px;
border-bottom: 1px solid #dee2e6;
display: flex;
align-items: center;
justify-content: space-between;
}
.modal-body {
padding: 20px;
}
.modal-footer {
padding: 15px 20px;
border-top: 1px solid #dee2e6;
display: flex;
justify-content: flex-end;
gap: 10px;
}
/* 警告和错误样式 */
.alert {
padding: 12px 20px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
}
.alert-warning {
color: #856404;
background-color: #fff3cd;
border-color: #ffeaa7;
}
.alert-danger {
color: #721c24;
background-color: #f8d7da;
border-color: #f5c6cb;
}
.alert-info {
color: #0c5460;
background-color: #d1ecf1;
border-color: #bee5eb;
}
/* 加载状态 */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 响应式设计 */
@media (max-width: 768px) {
.container {
padding: 0 10px;
}
.row {
margin: 0 -10px;
}
.col {
padding: 0 10px;
}
.navbar {
flex-direction: column;
align-items: stretch;
}
.nav {
flex-direction: column;
margin-top: 10px;
}
.nav-item {
margin: 5px 0;
}
}
/* 暗色模式支持 */
@media (prefers-color-scheme: dark) {
body {
background-color: #1a1a1a;
color: #e0e0e0;
}
.card {
background-color: #2d2d2d;
border-color: #404040;
}
.card-header {
background-color: #404040;
border-color: #555;
}
.form-control {
background-color: #2d2d2d;
border-color: #404040;
color: #e0e0e0;
}
.navbar {
background-color: #2d2d2d;
border-color: #404040;
}
.navbar-brand,
.nav-link {
color: #e0e0e0;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,72 @@
/* 字体备用系统 - 确保在任何情况下都有合适的字体显示 */
/* 全局字体备用栈 */
:root {
--font-family-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-family-serif: Georgia, Cambria, "Times New Roman", Times, serif;
--font-family-mono: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--font-family-chinese: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
}
/* 当 Open Sans 不可用时的备用 */
body, .open-sans-fallback {
font-family: "Open Sans", var(--font-family-sans);
}
/* 当 Noto Serif SC 不可用时的备用 */
.noto-serif-fallback {
font-family: "Noto Serif SC", var(--font-family-chinese), var(--font-family-serif);
}
/* 通用中文字体备用 */
.chinese-text {
font-family: var(--font-family-chinese);
}
/* 确保代码字体始终可用 */
code, pre, .monospace {
font-family: var(--font-family-mono);
}
/* 字体加载失败时的样式调整 */
.font-loading-error {
font-family: var(--font-family-sans) !important;
}
.font-loading-error::before {
content: "";
display: inline-block;
width: 0;
height: 0;
}
/* 响应式字体大小 */
@media (max-width: 768px) {
body {
font-size: 14px;
line-height: 1.5;
}
}
@media (min-width: 769px) {
body {
font-size: 16px;
line-height: 1.6;
}
}
/* 确保图标字体备用 */
.fa, .fas, .far, .fal, .fab {
font-family: "Font Awesome 5 Free", "Font Awesome 5 Pro", "FontAwesome", sans-serif;
}
/* 当图标字体不可用时显示文字 */
.fa-home::after { content: "首页"; }
.fa-user::after { content: "用户"; }
.fa-search::after { content: "搜索"; }
.fa-menu::after { content: "菜单"; }
.fa-close::after { content: "关闭"; }
.fa-arrow-left::after { content: "←"; }
.fa-arrow-right::after { content: "→"; }
.fa-arrow-up::after { content: "↑"; }
.fa-arrow-down::after { content: "↓"; }

View File

@@ -1,32 +1,50 @@
/* Open Sans Font - Local Fallback */
/* Open Sans Font - 完整本地备用版本 */
/* 300 weight - latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-display: swap;
src: local('Open Sans Light'), local('OpenSans-Light');
src: url('files/opensans-300-latin.woff2') format('woff2'),
local('Open Sans Light'), local('OpenSans-Light');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* 400 weight - latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-display: swap;
src: local('Open Sans Regular'), local('OpenSans-Regular');
src: url('files/opensans-400-latin.woff2') format('woff2'),
local('Open Sans Regular'), local('OpenSans-Regular');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* 600 weight - latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-display: swap;
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold');
src: url('files/opensans-600-latin.woff2') format('woff2'),
local('Open Sans SemiBold'), local('OpenSans-SemiBold');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* 700 weight - latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-display: swap;
src: local('Open Sans Bold'), local('OpenSans-Bold');
src: url('files/opensans-700-latin.woff2') format('woff2'),
local('Open Sans Bold'), local('OpenSans-Bold');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* 备用字体栈 */
body, .font-sans-serif {
font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
}

328
assets/vendor/external/js-fallback.js vendored Normal file
View File

@@ -0,0 +1,328 @@
/* JavaScript 功能备用系统 - 确保基本功能始终可用 */
(function() {
'use strict';
// 检查并提供基本的 JavaScript 功能
window.ArgonFallback = {
// 初始化备用系统
init: function() {
this.checkDependencies();
this.provideFallbacks();
this.bindEvents();
console.log('JavaScript 备用系统已启动');
},
// 检查依赖项
checkDependencies: function() {
var missing = [];
// 检查 jQuery
if (typeof jQuery === 'undefined') {
missing.push('jQuery');
this.provideJQueryFallback();
}
// 检查其他关键库
if (typeof bootstrap === 'undefined') {
missing.push('Bootstrap');
}
if (missing.length > 0) {
console.warn('缺少依赖项:', missing.join(', '));
}
},
// 提供 jQuery 基本功能备用
provideJQueryFallback: function() {
window.$ = window.jQuery = function(selector) {
return new jQueryFallback(selector);
};
// 扩展 jQuery 对象
$.fn = jQueryFallback.prototype;
$.extend = function(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
return target;
};
console.log('jQuery 备用版本已加载');
},
// 提供其他备用功能
provideFallbacks: function() {
// 模态框功能
this.provideModalFallback();
// 工具提示功能
this.provideTooltipFallback();
// 下拉菜单功能
this.provideDropdownFallback();
},
// 模态框备用
provideModalFallback: function() {
window.showModal = function(content, title) {
var modal = document.createElement('div');
modal.className = 'modal show';
modal.innerHTML =
'<div class="modal-dialog">' +
'<div class="modal-header">' +
'<h5>' + (title || '提示') + '</h5>' +
'<button type="button" class="close" onclick="this.closest(\'.modal\').remove()">&times;</button>' +
'</div>' +
'<div class="modal-body">' + content + '</div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn" onclick="this.closest(\'.modal\').remove()">关闭</button>' +
'</div>' +
'</div>';
document.body.appendChild(modal);
// 点击背景关闭
modal.addEventListener('click', function(e) {
if (e.target === modal) {
modal.remove();
}
});
};
},
// 工具提示备用
provideTooltipFallback: function() {
document.addEventListener('mouseover', function(e) {
var element = e.target;
var tooltip = element.getAttribute('title') || element.getAttribute('data-tooltip');
if (tooltip && !element.querySelector('.tooltip-fallback')) {
var tooltipEl = document.createElement('div');
tooltipEl.className = 'tooltip-fallback';
tooltipEl.textContent = tooltip;
tooltipEl.style.cssText =
'position: absolute; background: #333; color: #fff; padding: 4px 8px; ' +
'border-radius: 4px; font-size: 12px; z-index: 1000; pointer-events: none;';
document.body.appendChild(tooltipEl);
var rect = element.getBoundingClientRect();
tooltipEl.style.left = rect.left + 'px';
tooltipEl.style.top = (rect.top - tooltipEl.offsetHeight - 5) + 'px';
element.addEventListener('mouseleave', function() {
if (tooltipEl.parentNode) {
tooltipEl.parentNode.removeChild(tooltipEl);
}
});
}
});
},
// 下拉菜单备用
provideDropdownFallback: function() {
document.addEventListener('click', function(e) {
var toggle = e.target.closest('[data-toggle="dropdown"]');
if (toggle) {
e.preventDefault();
var menu = toggle.nextElementSibling;
if (menu && menu.classList.contains('dropdown-menu')) {
menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
}
}
// 关闭其他下拉菜单
var openMenus = document.querySelectorAll('.dropdown-menu[style*="block"]');
openMenus.forEach(function(menu) {
if (!menu.contains(e.target) && !menu.previousElementSibling.contains(e.target)) {
menu.style.display = 'none';
}
});
});
},
// 绑定事件
bindEvents: function() {
var self = this;
// 监听错误事件
window.addEventListener('error', function(e) {
console.warn('JavaScript 错误:', e.message);
self.handleError(e);
});
// 监听未处理的 Promise 拒绝
window.addEventListener('unhandledrejection', function(e) {
console.warn('未处理的 Promise 拒绝:', e.reason);
});
},
// 错误处理
handleError: function(error) {
// 尝试恢复基本功能
if (error.message.includes('jQuery') || error.message.includes('$')) {
this.provideJQueryFallback();
}
}
};
// jQuery 备用实现
function jQueryFallback(selector) {
this.elements = [];
if (typeof selector === 'string') {
this.elements = Array.from(document.querySelectorAll(selector));
} else if (selector && selector.nodeType) {
this.elements = [selector];
} else if (selector && typeof selector === 'object' && selector.length !== undefined) {
this.elements = Array.from(selector);
}
this.length = this.elements.length;
return this;
}
// jQuery 方法实现
jQueryFallback.prototype = {
each: function(callback) {
this.elements.forEach(callback);
return this;
},
on: function(event, handler) {
this.elements.forEach(function(el) {
el.addEventListener(event, handler);
});
return this;
},
off: function(event, handler) {
this.elements.forEach(function(el) {
el.removeEventListener(event, handler);
});
return this;
},
click: function(handler) {
if (handler) {
return this.on('click', handler);
} else {
this.elements.forEach(function(el) {
el.click();
});
return this;
}
},
addClass: function(className) {
this.elements.forEach(function(el) {
el.classList.add(className);
});
return this;
},
removeClass: function(className) {
this.elements.forEach(function(el) {
el.classList.remove(className);
});
return this;
},
toggleClass: function(className) {
this.elements.forEach(function(el) {
el.classList.toggle(className);
});
return this;
},
hasClass: function(className) {
return this.elements.some(function(el) {
return el.classList.contains(className);
});
},
html: function(content) {
if (content !== undefined) {
this.elements.forEach(function(el) {
el.innerHTML = content;
});
return this;
} else {
return this.elements[0] ? this.elements[0].innerHTML : '';
}
},
text: function(content) {
if (content !== undefined) {
this.elements.forEach(function(el) {
el.textContent = content;
});
return this;
} else {
return this.elements[0] ? this.elements[0].textContent : '';
}
},
hide: function() {
this.elements.forEach(function(el) {
el.style.display = 'none';
});
return this;
},
show: function() {
this.elements.forEach(function(el) {
el.style.display = '';
});
return this;
},
fadeIn: function(duration) {
this.elements.forEach(function(el) {
el.style.opacity = '0';
el.style.display = '';
var start = Date.now();
var timer = setInterval(function() {
var progress = (Date.now() - start) / (duration || 400);
if (progress >= 1) {
el.style.opacity = '1';
clearInterval(timer);
} else {
el.style.opacity = progress;
}
}, 16);
});
return this;
},
fadeOut: function(duration) {
this.elements.forEach(function(el) {
var start = Date.now();
var startOpacity = parseFloat(getComputedStyle(el).opacity);
var timer = setInterval(function() {
var progress = (Date.now() - start) / (duration || 400);
if (progress >= 1) {
el.style.display = 'none';
clearInterval(timer);
} else {
el.style.opacity = startOpacity * (1 - progress);
}
}, 16);
});
return this;
}
};
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
ArgonFallback.init();
});
} else {
ArgonFallback.init();
}
})();

View File

@@ -0,0 +1,54 @@
/* KaTeX Auto-Render - Local Fallback */
(function() {
'use strict';
console.warn('KaTeX Auto-Render 本地备用版本 - 功能受限');
window.renderMathInElement = function(element, options) {
console.warn('KaTeX 自动渲染功能不可用 - 使用备用版本');
options = options || {};
var delimiters = options.delimiters || [
{left: "$$", right: "$$", display: true},
{left: "$", right: "$", display: false},
{left: "\\(", right: "\\)", display: false},
{left: "\\[", right: "\\]", display: true}
];
if (!element) {
element = document.body;
}
// 简单的文本替换处理
var walker = document.createTreeWalker(
element,
NodeFilter.SHOW_TEXT,
null,
false
);
var textNodes = [];
var node;
while (node = walker.nextNode()) {
textNodes.push(node);
}
textNodes.forEach(function(textNode) {
var text = textNode.textContent;
var hasMatch = false;
delimiters.forEach(function(delimiter) {
if (text.includes(delimiter.left) && text.includes(delimiter.right)) {
hasMatch = true;
}
});
if (hasMatch) {
var span = document.createElement('span');
span.innerHTML = text.replace(/\$([^$]+)\$/g,
'<span class="katex" style="font-style: italic; color: #666;" title="数学公式渲染服务不可用">$1</span>');
textNode.parentNode.replaceChild(span, textNode);
}
});
};
})();

View File

@@ -0,0 +1,47 @@
/* KaTeX CSS - Local Fallback */
.katex {
font: normal 1.21em KaTeX_Main, "Times New Roman", serif;
line-height: 1.2;
text-indent: 0;
text-rendering: auto;
font-style: italic;
color: #666;
}
.katex * {
-ms-high-contrast-adjust: none !important;
}
.katex .katex-version::after {
content: "本地备用版本";
}
.katex .katex-mathml {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
padding: 0;
border: 0;
height: 1px;
width: 1px;
overflow: hidden;
}
.katex-display {
display: block;
margin: 1em 0;
text-align: center;
}
.katex-display > .katex {
display: block;
text-align: center;
white-space: nowrap;
}
/* 备用样式提示 */
.katex::before {
content: "[数学公式渲染服务不可用] ";
font-size: 0.8em;
color: #999;
font-style: normal;
}

View File

@@ -0,0 +1,27 @@
/* KaTeX JS - Local Fallback */
(function() {
'use strict';
console.warn('KaTeX 本地备用版本 - 功能受限');
// 创建简化的 KaTeX 对象
window.katex = {
render: function(tex, element, options) {
console.warn('KaTeX 渲染功能不可用 - 使用备用版本');
if (element) {
element.innerHTML = '<span style="font-style: italic; color: #666;" title="数学公式渲染服务不可用">' +
tex + '</span>';
element.className = (element.className || '') + ' katex';
}
},
renderToString: function(tex, options) {
console.warn('KaTeX 渲染功能不可用 - 使用备用版本');
return '<span class="katex" style="font-style: italic; color: #666;" title="数学公式渲染服务不可用">' +
tex + '</span>';
}
};
// 版本信息
window.katex.__version = "0.11.1-fallback";
})();

266
assets/vendor/external/logger.js vendored Normal file
View File

@@ -0,0 +1,266 @@
/* Argon 内部日志系统 - 仅管理员可见 */
(function() {
'use strict';
window.ArgonLogger = {
// 配置
config: {
maxLogs: 1000,
enableConsole: false, // 默认关闭控制台输出
enableStorage: true,
storageKey: 'argon_internal_logs'
},
// 日志级别
levels: {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
CRITICAL: 4
},
// 内部日志存储
logs: [],
// 初始化
init: function() {
this.checkAdminStatus();
this.loadStoredLogs();
this.bindEvents();
},
// 检查管理员状态
checkAdminStatus: function() {
// 检查是否为管理员通过PHP传递的全局变量
if (typeof window.argonUserRole !== 'undefined' && window.argonUserRole === 'administrator') {
this.config.enableConsole = true;
this.log('管理员模式:控制台日志已启用', 'INFO');
} else {
this.config.enableConsole = false;
}
},
// 加载存储的日志
loadStoredLogs: function() {
if (this.config.enableStorage && localStorage) {
try {
var stored = localStorage.getItem(this.config.storageKey);
if (stored) {
this.logs = JSON.parse(stored);
}
} catch (e) {
// 静默处理存储错误
}
}
},
// 保存日志到存储
saveLogsToStorage: function() {
if (this.config.enableStorage && localStorage) {
try {
// 只保留最新的日志
var logsToSave = this.logs.slice(-this.config.maxLogs);
localStorage.setItem(this.config.storageKey, JSON.stringify(logsToSave));
} catch (e) {
// 静默处理存储错误
}
}
},
// 记录日志
log: function(message, level, data) {
level = level || 'INFO';
var timestamp = new Date().toISOString();
var logEntry = {
timestamp: timestamp,
level: level,
message: message,
data: data || null,
url: window.location.href,
userAgent: navigator.userAgent.substring(0, 100) // 截取前100字符
};
// 添加到内部日志
this.logs.push(logEntry);
// 限制日志数量
if (this.logs.length > this.config.maxLogs) {
this.logs = this.logs.slice(-this.config.maxLogs);
}
// 保存到存储
this.saveLogsToStorage();
// 仅管理员显示控制台日志
if (this.config.enableConsole) {
var consoleMethod = this.getConsoleMethod(level);
consoleMethod('[Argon] ' + message, data || '');
}
// 关键错误需要特殊处理
if (level === 'CRITICAL') {
this.handleCriticalError(logEntry);
}
},
// 获取对应的控制台方法
getConsoleMethod: function(level) {
switch (level) {
case 'DEBUG':
return console.debug || console.log;
case 'INFO':
return console.info || console.log;
case 'WARN':
return console.warn || console.log;
case 'ERROR':
case 'CRITICAL':
return console.error || console.log;
default:
return console.log;
}
},
// 处理关键错误
handleCriticalError: function(logEntry) {
// 关键错误需要通知用户联系站长
if (this.config.enableConsole) {
console.error('发生关键错误,请联系站长处理:', logEntry.message);
}
// 可以在这里添加错误上报逻辑
this.reportCriticalError(logEntry);
},
// 上报关键错误
reportCriticalError: function(logEntry) {
// 这里可以实现错误上报到服务器的逻辑
// 暂时只记录到本地存储
try {
var criticalLogs = JSON.parse(localStorage.getItem('argon_critical_errors') || '[]');
criticalLogs.push(logEntry);
localStorage.setItem('argon_critical_errors', JSON.stringify(criticalLogs.slice(-50)));
} catch (e) {
// 静默处理
}
},
// 获取日志(仅管理员)
getLogs: function(level, limit) {
if (!this.config.enableConsole) {
return []; // 非管理员返回空数组
}
var filteredLogs = this.logs;
if (level) {
filteredLogs = this.logs.filter(function(log) {
return log.level === level;
});
}
if (limit) {
filteredLogs = filteredLogs.slice(-limit);
}
return filteredLogs;
},
// 清除日志(仅管理员)
clearLogs: function() {
if (!this.config.enableConsole) {
return false;
}
this.logs = [];
if (localStorage) {
localStorage.removeItem(this.config.storageKey);
localStorage.removeItem('argon_critical_errors');
}
return true;
},
// 导出日志(仅管理员)
exportLogs: function() {
if (!this.config.enableConsole) {
return null;
}
var exportData = {
timestamp: new Date().toISOString(),
site: window.location.origin,
logs: this.logs,
criticalErrors: JSON.parse(localStorage.getItem('argon_critical_errors') || '[]')
};
return JSON.stringify(exportData, null, 2);
},
// 绑定事件
bindEvents: function() {
var self = this;
// 监听页面错误
window.addEventListener('error', function(e) {
self.log('JavaScript错误: ' + e.message, 'ERROR', {
filename: e.filename,
lineno: e.lineno,
colno: e.colno,
stack: e.error ? e.error.stack : null
});
});
// 监听未处理的Promise拒绝
window.addEventListener('unhandledrejection', function(e) {
self.log('未处理的Promise拒绝: ' + e.reason, 'ERROR', {
reason: e.reason,
promise: e.promise
});
});
// 监听资源加载错误
window.addEventListener('error', function(e) {
if (e.target && e.target !== window) {
var resourceType = e.target.tagName || 'unknown';
var resourceUrl = e.target.src || e.target.href || 'unknown';
self.log('资源加载失败: ' + resourceType + ' - ' + resourceUrl, 'WARN');
}
}, true);
},
// 便捷方法
debug: function(message, data) {
this.log(message, 'DEBUG', data);
},
info: function(message, data) {
this.log(message, 'INFO', data);
},
warn: function(message, data) {
this.log(message, 'WARN', data);
},
error: function(message, data) {
this.log(message, 'ERROR', data);
},
critical: function(message, data) {
this.log(message, 'CRITICAL', data);
}
};
// 自动初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
ArgonLogger.init();
});
} else {
ArgonLogger.init();
}
// 为管理员提供全局访问
if (typeof window.argonUserRole !== 'undefined' && window.argonUserRole === 'administrator') {
window.ArgonLogs = ArgonLogger;
}
})();

View File

@@ -0,0 +1,62 @@
/* MathJax 2 - Local Fallback */
(function() {
'use strict';
console.warn('MathJax 2 本地备用版本 - 功能受限');
// 创建简化的 MathJax 对象
window.MathJax = window.MathJax || {};
window.MathJax.Hub = {
Config: function(config) {
console.log('MathJax 2 配置已设置(备用版本)');
this.config = config;
},
Queue: function(callback) {
if (typeof callback === 'function') {
setTimeout(callback, 100);
}
return this;
},
Typeset: function(element) {
console.warn('MathJax 2 渲染功能不可用 - 使用备用版本');
// 简单的样式处理
var mathElements = element ?
element.querySelectorAll('script[type*="math"]') :
document.querySelectorAll('script[type*="math"]');
mathElements.forEach(function(script) {
var span = document.createElement('span');
span.textContent = script.textContent;
span.style.fontStyle = 'italic';
span.style.color = '#666';
span.title = '数学公式渲染服务不可用';
script.parentNode.insertBefore(span, script);
script.style.display = 'none';
});
},
Register: {
StartupHook: function(hook, callback) {
if (typeof callback === 'function') {
setTimeout(callback, 100);
}
}
}
};
// 自动配置
if (window.MathJax.Hub.Config) {
window.MathJax.Hub.Config({
messageStyle: "none",
tex2jax: {
inlineMath: [["$", "$"], ["\\\\(", "\\\\)"]],
displayMath: [['$$','$$']],
processEscapes: true,
skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
}
});
}
})();

View File

@@ -0,0 +1,61 @@
/* MathJax 3 - Local Fallback */
(function() {
'use strict';
console.warn('MathJax 3 本地备用版本 - 功能受限');
// 创建简化的 MathJax 对象
window.MathJax = window.MathJax || {
tex: {
inlineMath: [["$", "$"], ["\\\\(", "\\\\)"]],
displayMath: [['$$','$$']],
processEscapes: true,
packages: {'[+]': ['noerrors']}
},
options: {
skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
ignoreHtmlClass: 'tex2jax_ignore',
processHtmlClass: 'tex2jax_process'
},
loader: {
load: ['[tex]/noerrors']
},
startup: {
ready: function() {
console.log('MathJax 3 备用版本已就绪');
MathJax.startup.defaultReady();
},
defaultReady: function() {
// 简化的渲染函数
MathJax.typesetPromise = function(elements) {
return new Promise(function(resolve) {
console.warn('MathJax 渲染功能不可用 - 使用备用版本');
resolve();
});
};
}
}
};
// 简化的渲染函数
window.MathJax.typesetPromise = function(elements) {
return new Promise(function(resolve) {
console.warn('数学公式渲染服务不可用');
// 尝试简单的文本替换
var mathElements = document.querySelectorAll('.tex2jax_process, .MathJax');
mathElements.forEach(function(el) {
if (el.textContent.includes('$')) {
el.style.fontStyle = 'italic';
el.style.color = '#666';
el.title = '数学公式渲染服务不可用';
}
});
resolve();
});
};
// 触发启动
if (MathJax.startup && MathJax.startup.ready) {
MathJax.startup.ready();
}
})();

View File

@@ -8,37 +8,131 @@
'fonts.googleapis.com/css?family=Open+Sans': '/wp-content/themes/argon/assets/vendor/external/fonts/open-sans.css',
'fonts.googleapis.com/css?family=Noto+Serif+SC': '/wp-content/themes/argon/assets/vendor/external/fonts/noto-serif-sc.css',
'static.geetest.com/v4/gt4.js': '/wp-content/themes/argon/assets/vendor/external/geetest/gt4.js',
'cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js': '/wp-content/themes/argon/assets/vendor/external/qrcode/qrcode.min.js'
'cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js': '/wp-content/themes/argon/assets/vendor/external/qrcode/qrcode.min.js',
'cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js': '/wp-content/themes/argon/assets/vendor/external/mathjax3/tex-chtml-full.js',
'cdn.jsdelivr.net/npm/mathjax@2.7.5/MathJax.js': '/wp-content/themes/argon/assets/vendor/external/mathjax2/MathJax.js',
'cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css': '/wp-content/themes/argon/assets/vendor/external/katex/katex.min.css',
'cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js': '/wp-content/themes/argon/assets/vendor/external/katex/katex.min.js',
'cdn.jsdelivr.net/npm/katex@0.11.1/dist/contrib/auto-render.min.js': '/wp-content/themes/argon/assets/vendor/external/katex/auto-render.min.js'
},
// 配置选项
config: {
timeout: 5000,
retryCount: 2,
enableLogging: true,
enableFallbackCSS: true,
enableFallbackJS: true
},
// 初始化
init: function() {
this.loadFallbackSystems();
this.bindGlobalErrorHandlers();
this.log('资源加载器已初始化');
},
// 加载备用系统
loadFallbackSystems: function() {
if (this.config.enableFallbackCSS) {
this.loadCSS('/wp-content/themes/argon/assets/vendor/external/css-fallback.css');
this.loadCSS('/wp-content/themes/argon/assets/vendor/external/fonts/font-fallback.css');
}
if (this.config.enableFallbackJS) {
this.loadJS('/wp-content/themes/argon/assets/vendor/external/js-fallback.js');
this.loadJS('/wp-content/themes/argon/assets/vendor/external/resource-monitor.js');
}
},
// 绑定全局错误处理
bindGlobalErrorHandlers: function() {
var self = this;
// 监听资源加载错误
window.addEventListener('error', function(e) {
if (e.target && (e.target.tagName === 'SCRIPT' || e.target.tagName === 'LINK')) {
self.handleResourceError(e.target);
}
}, true);
// 监听未处理的Promise拒绝
window.addEventListener('unhandledrejection', function(e) {
if (typeof ArgonLogger !== 'undefined') {
ArgonLogger.warn('未处理的Promise拒绝: ' + e.reason);
}
});
},
// 处理资源加载错误
handleResourceError: function(element) {
var url = element.src || element.href;
if (url) {
if (typeof ArgonLogger !== 'undefined') {
ArgonLogger.warn('资源加载失败: ' + url);
}
var fallbackUrl = this.getFallbackUrl(url);
if (fallbackUrl) {
this.log('尝试加载备用资源: ' + fallbackUrl);
if (element.tagName === 'SCRIPT') {
this.loadJS(fallbackUrl);
} else if (element.tagName === 'LINK') {
this.loadCSS(fallbackUrl);
}
}
}
},
// 获取备用URL
getFallbackUrl: function(url) {
for (var key in this.resourceMap) {
if (url.includes(key)) {
return this.resourceMap[key];
}
}
return null;
},
// 加载CSS资源
loadCSS: function(url, fallbackUrl) {
var self = this;
return new Promise(function(resolve, reject) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
var timeout = setTimeout(function() {
console.warn('CSS资源加载超时,使用本地备用:', url);
self.log('CSS资源加载超时: ' + url);
if (fallbackUrl) {
ArgonResourceLoader.loadCSS(fallbackUrl).then(resolve).catch(reject);
self.loadCSS(fallbackUrl).then(resolve).catch(reject);
} else {
reject(new Error('CSS加载失败且无备用资源'));
var autoFallback = self.getFallbackUrl(url);
if (autoFallback) {
self.loadCSS(autoFallback).then(resolve).catch(reject);
} else {
reject(new Error('CSS加载失败且无备用资源'));
}
}
}, 5000);
}, self.config.timeout);
link.onload = function() {
clearTimeout(timeout);
self.log('CSS资源加载成功: ' + url);
resolve();
};
link.onerror = function() {
clearTimeout(timeout);
console.warn('CSS资源加载失败,使用本地备用:', url);
self.log('CSS资源加载失败: ' + url);
if (fallbackUrl) {
ArgonResourceLoader.loadCSS(fallbackUrl).then(resolve).catch(reject);
self.loadCSS(fallbackUrl).then(resolve).catch(reject);
} else {
reject(new Error('CSS加载失败且无备用资源'));
var autoFallback = self.getFallbackUrl(url);
if (autoFallback) {
self.loadCSS(autoFallback).then(resolve).catch(reject);
} else {
reject(new Error('CSS加载失败且无备用资源'));
}
}
};
@@ -48,32 +142,44 @@
// 加载JS资源
loadJS: function(url, fallbackUrl) {
var self = this;
return new Promise(function(resolve, reject) {
var script = document.createElement('script');
script.src = url;
script.async = true;
var timeout = setTimeout(function() {
console.warn('JS资源加载超时,使用本地备用:', url);
self.log('JS资源加载超时: ' + url);
if (fallbackUrl) {
ArgonResourceLoader.loadJS(fallbackUrl).then(resolve).catch(reject);
self.loadJS(fallbackUrl).then(resolve).catch(reject);
} else {
reject(new Error('JS加载失败且无备用资源'));
var autoFallback = self.getFallbackUrl(url);
if (autoFallback) {
self.loadJS(autoFallback).then(resolve).catch(reject);
} else {
reject(new Error('JS加载失败且无备用资源'));
}
}
}, 5000);
}, self.config.timeout);
script.onload = function() {
clearTimeout(timeout);
self.log('JS资源加载成功: ' + url);
resolve();
};
script.onerror = function() {
clearTimeout(timeout);
console.warn('JS资源加载失败,使用本地备用:', url);
self.log('JS资源加载失败: ' + url);
if (fallbackUrl) {
ArgonResourceLoader.loadJS(fallbackUrl).then(resolve).catch(reject);
self.loadJS(fallbackUrl).then(resolve).catch(reject);
} else {
reject(new Error('JS加载失败且无备用资源'));
var autoFallback = self.getFallbackUrl(url);
if (autoFallback) {
self.loadJS(autoFallback).then(resolve).catch(reject);
} else {
reject(new Error('JS加载失败且无备用资源'));
}
}
};
@@ -83,45 +189,45 @@
// 智能加载资源
smartLoad: function(url, type) {
var fallbackUrl = null;
// 查找备用资源
for (var key in this.resourceMap) {
if (url.indexOf(key) !== -1) {
fallbackUrl = this.resourceMap[key];
break;
}
}
if (type === 'css') {
return this.loadCSS(url, fallbackUrl);
return this.loadCSS(url);
} else if (type === 'js') {
return this.loadJS(url, fallbackUrl);
return this.loadJS(url);
} else {
return Promise.reject(new Error('不支持的资源类型: ' + type));
}
},
// 批量加载资源
loadMultiple: function(resources) {
var self = this;
var promises = resources.map(function(resource) {
return self.smartLoad(resource.url, resource.type);
});
return Promise.all(promises);
},
// 日志记录
log: function(message) {
if (this.config.enableLogging) {
if (typeof ArgonLogger !== 'undefined') {
ArgonLogger.info('[ResourceLoader] ' + message);
} else {
// 仅在管理员模式下输出到控制台
if (typeof window.argonUserRole !== 'undefined' && window.argonUserRole === 'administrator') {
console.log('[ArgonResourceLoader] ' + message);
}
}
}
}
};
// 自动替换现有的资源加载
var originalCreateElement = document.createElement;
document.createElement = function(tagName) {
var element = originalCreateElement.call(document, tagName);
if (tagName.toLowerCase() === 'script') {
var originalSetAttribute = element.setAttribute;
element.setAttribute = function(name, value) {
if (name === 'src' && value) {
// 检查是否需要备用资源
for (var key in ArgonResourceLoader.resourceMap) {
if (value.indexOf(key) !== -1) {
console.log('检测到外部JS资源准备备用方案:', value);
break;
}
}
}
return originalSetAttribute.call(this, name, value);
};
}
return element;
};
// 自动初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
ArgonResourceLoader.init();
});
} else {
ArgonResourceLoader.init();
}
})();

View File

@@ -0,0 +1,200 @@
/* 外部资源监控系统 - 实时检测和自动切换 */
(function() {
'use strict';
window.ArgonResourceMonitor = {
// 监控配置
config: {
checkInterval: 30000, // 30秒检查一次
timeout: 5000, // 5秒超时
retryCount: 3, // 重试3次
enableLogging: true // 启用日志
},
// 资源状态
resourceStatus: {},
// 监控定时器
monitorTimer: null,
// 初始化监控
init: function() {
this.log('资源监控系统启动');
this.startMonitoring();
this.bindEvents();
},
// 开始监控
startMonitoring: function() {
var self = this;
this.monitorTimer = setInterval(function() {
self.checkAllResources();
}, this.config.checkInterval);
},
// 停止监控
stopMonitoring: function() {
if (this.monitorTimer) {
clearInterval(this.monitorTimer);
this.monitorTimer = null;
}
},
// 检查所有资源
checkAllResources: function() {
var self = this;
var resources = [
'https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700',
'https://static.geetest.com/v4/gt4.js',
'https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js'
];
resources.forEach(function(url) {
self.checkResource(url);
});
},
// 检查单个资源
checkResource: function(url) {
var self = this;
var startTime = Date.now();
// 创建测试请求
var xhr = new XMLHttpRequest();
xhr.timeout = this.config.timeout;
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var responseTime = Date.now() - startTime;
var status = xhr.status === 200 ? 'available' : 'unavailable';
self.updateResourceStatus(url, {
status: status,
responseTime: responseTime,
lastCheck: new Date().toISOString()
});
}
};
xhr.onerror = function() {
self.updateResourceStatus(url, {
status: 'unavailable',
responseTime: -1,
lastCheck: new Date().toISOString()
});
};
xhr.ontimeout = function() {
self.updateResourceStatus(url, {
status: 'timeout',
responseTime: self.config.timeout,
lastCheck: new Date().toISOString()
});
};
try {
xhr.open('HEAD', url, true);
xhr.send();
} catch (e) {
this.updateResourceStatus(url, {
status: 'error',
responseTime: -1,
lastCheck: new Date().toISOString(),
error: e.message
});
}
},
// 更新资源状态
updateResourceStatus: function(url, status) {
this.resourceStatus[url] = status;
this.log('资源状态更新: ' + url + ' - ' + status.status);
// 如果资源不可用,触发备用机制
if (status.status !== 'available') {
this.triggerFallback(url);
}
},
// 触发备用机制
triggerFallback: function(url) {
this.log('触发备用机制: ' + url);
// 发送自定义事件
var event = new CustomEvent('resourceUnavailable', {
detail: { url: url, status: this.resourceStatus[url] }
});
document.dispatchEvent(event);
},
// 绑定事件
bindEvents: function() {
var self = this;
// 监听页面可见性变化
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'visible') {
self.checkAllResources();
}
});
// 监听网络状态变化
if ('onLine' in navigator) {
window.addEventListener('online', function() {
self.log('网络连接恢复,重新检查资源');
self.checkAllResources();
});
window.addEventListener('offline', function() {
self.log('网络连接断开');
});
}
},
// 获取资源状态报告
getStatusReport: function() {
return {
timestamp: new Date().toISOString(),
resources: this.resourceStatus,
summary: this.generateSummary()
};
},
// 生成状态摘要
generateSummary: function() {
var total = Object.keys(this.resourceStatus).length;
var available = 0;
var unavailable = 0;
for (var url in this.resourceStatus) {
if (this.resourceStatus[url].status === 'available') {
available++;
} else {
unavailable++;
}
}
return {
total: total,
available: available,
unavailable: unavailable,
availability: total > 0 ? (available / total * 100).toFixed(1) + '%' : '0%'
};
},
// 日志记录
log: function(message) {
if (this.config.enableLogging) {
console.log('[ArgonResourceMonitor] ' + message);
}
}
};
// 页面加载完成后自动启动监控
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
ArgonResourceMonitor.init();
});
} else {
ArgonResourceMonitor.init();
}
})();