fix: correct local datetime display and remove false devtools detection
This commit is contained in:
@@ -13,101 +13,32 @@
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<!-- 开发者工具检测 - 必须放在最前面,优先执行 -->
|
||||
<!-- 安全快捷键拦截(避免误判开发者工具) -->
|
||||
<script>
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// 检查调试模式
|
||||
const isDebugMode = localStorage.getItem('debugMode') === 'true';
|
||||
if (isDebugMode) return; // 调试模式下跳过检测
|
||||
if (isDebugMode) return;
|
||||
|
||||
let devtoolsOpen = false;
|
||||
let checkCount = 0; // 检测次数计数器
|
||||
|
||||
// 方法1: Console对象toString检测(最可靠)
|
||||
const checkElement = /./;
|
||||
checkElement.toString = function() {
|
||||
devtoolsOpen = true;
|
||||
};
|
||||
|
||||
// 方法2: debugger暂停检测(可能误判,需要多次确认)
|
||||
function detectDebugger() {
|
||||
const start = performance.now();
|
||||
debugger;
|
||||
const end = performance.now();
|
||||
return (end - start) > 100;
|
||||
}
|
||||
|
||||
// 方法3: 窗口尺寸检测(辅助判断)
|
||||
function detectWindowSize() {
|
||||
const widthDiff = window.outerWidth - window.innerWidth;
|
||||
const heightDiff = window.outerHeight - window.innerHeight;
|
||||
// 提高阈值,避免误判
|
||||
return widthDiff > 200 || heightDiff > 200;
|
||||
}
|
||||
|
||||
// 综合检测(需要多个条件同时满足才判定)
|
||||
function checkDevTools() {
|
||||
devtoolsOpen = false;
|
||||
console.log('%c', checkElement);
|
||||
console.clear();
|
||||
|
||||
const consoleOpen = devtoolsOpen;
|
||||
const debuggerPause = detectDebugger();
|
||||
const windowSizeAbnormal = detectWindowSize();
|
||||
|
||||
// 严格判定:console检测为主,其他为辅助
|
||||
// 只有console明确检测到才触发警告
|
||||
return consoleOpen && (debuggerPause || windowSizeAbnormal);
|
||||
}
|
||||
|
||||
// 立即检测(页面加载时)
|
||||
const initialCheck = checkDevTools();
|
||||
|
||||
if (initialCheck) {
|
||||
// 检测到开发者工具,显示警告
|
||||
document.write('<style>body{margin:0;overflow:hidden;}</style>');
|
||||
document.write('<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);display:flex;align-items:center;justify-content:center;font-family:Arial,sans-serif;z-index:999999;">');
|
||||
document.write('<div style="background:white;padding:60px;border-radius:20px;text-align:center;max-width:500px;box-shadow:0 20px 60px rgba(0,0,0,0.3);">');
|
||||
document.write('<div style="font-size:80px;color:#667eea;margin-bottom:30px;">🛡️</div>');
|
||||
document.write('<h1 style="color:#333;margin-bottom:20px;font-size:32px;">检测到开发者工具</h1>');
|
||||
document.write('<p style="color:#666;font-size:18px;margin-bottom:30px;line-height:1.6;">为保护系统安全,请关闭浏览器开发者工具后访问</p>');
|
||||
document.write('<button onclick="location.reload()" style="background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:white;border:none;padding:15px 40px;font-size:16px;border-radius:10px;cursor:pointer;font-weight:600;box-shadow:0 4px 15px rgba(102,126,234,0.4);">🔄 刷新页面</button>');
|
||||
document.write('<p style="color:#999;font-size:14px;margin-top:20px;">如需使用开发者工具,请联系管理员开启调试模式</p>');
|
||||
document.write('</div></div>');
|
||||
document.close();
|
||||
throw new Error('DevTools detected');
|
||||
}
|
||||
|
||||
// 禁用右键和快捷键
|
||||
document.addEventListener('contextmenu', e => e.preventDefault());
|
||||
document.addEventListener('keydown', function(e) {
|
||||
// F12, Ctrl+Shift+I, Ctrl+Shift+J, Ctrl+U, Ctrl+Shift+C
|
||||
if (e.keyCode === 123 ||
|
||||
(e.ctrlKey && e.shiftKey && (e.keyCode === 73 || e.keyCode === 74 || e.keyCode === 67)) ||
|
||||
(e.ctrlKey && e.keyCode === 85)) {
|
||||
const key = String(e.key || '').toLowerCase();
|
||||
const blocked = e.keyCode === 123
|
||||
|| (e.ctrlKey && e.shiftKey && ['i', 'j', 'c'].includes(key))
|
||||
|| (e.ctrlKey && key === 'u');
|
||||
if (blocked) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// 持续监控(每2秒检测一次,避免频繁检测)
|
||||
setInterval(function() {
|
||||
checkCount++;
|
||||
|
||||
// 每次检测都要确认
|
||||
if (checkDevTools()) {
|
||||
// 检测到开发者工具打开,刷新页面
|
||||
location.reload();
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
// 禁用console(非localhost)
|
||||
if (window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1') {
|
||||
const noop = function() {};
|
||||
['log', 'info', 'warn', 'error', 'debug'].forEach(method => {
|
||||
try {
|
||||
console[method] = noop;
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -7599,6 +7530,6 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="app.js?v=20260218001"></script>
|
||||
<script src="app.js?v=20260218002"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -623,8 +623,14 @@ createApp({
|
||||
}
|
||||
|
||||
list.sort((a, b) => {
|
||||
const getTime = s => s.created_at ? new Date(s.created_at).getTime() : 0;
|
||||
const getExpire = s => s.expires_at ? new Date(s.expires_at).getTime() : Number.MAX_SAFE_INTEGER;
|
||||
const getTime = (s) => {
|
||||
const date = this.parseDateValue(s?.created_at);
|
||||
return date ? date.getTime() : 0;
|
||||
};
|
||||
const getExpire = (s) => {
|
||||
const date = this.parseDateValue(s?.expires_at);
|
||||
return date ? date.getTime() : Number.MAX_SAFE_INTEGER;
|
||||
};
|
||||
|
||||
switch (this.shareFilters.sort) {
|
||||
case 'created_asc':
|
||||
@@ -657,8 +663,10 @@ createApp({
|
||||
}
|
||||
|
||||
list.sort((a, b) => {
|
||||
const ta = a.created_at ? new Date(a.created_at).getTime() : 0;
|
||||
const tb = b.created_at ? new Date(b.created_at).getTime() : 0;
|
||||
const da = this.parseDateValue(a?.created_at);
|
||||
const db = this.parseDateValue(b?.created_at);
|
||||
const ta = da ? da.getTime() : 0;
|
||||
const tb = db ? db.getTime() : 0;
|
||||
return tb - ta;
|
||||
});
|
||||
|
||||
@@ -3006,7 +3014,8 @@ handleDragLeave(e) {
|
||||
formatExpireTime(expiresAt) {
|
||||
if (!expiresAt) return '永久有效';
|
||||
|
||||
const expireDate = new Date(expiresAt);
|
||||
const expireDate = this.parseDateValue(expiresAt);
|
||||
if (!expireDate) return String(expiresAt);
|
||||
const now = new Date();
|
||||
const diffMs = expireDate - now;
|
||||
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
||||
@@ -3040,7 +3049,8 @@ handleDragLeave(e) {
|
||||
// 判断是否即将过期(3天内)
|
||||
isExpiringSoon(expiresAt) {
|
||||
if (!expiresAt) return false;
|
||||
const expireDate = new Date(expiresAt);
|
||||
const expireDate = this.parseDateValue(expiresAt);
|
||||
if (!expireDate) return false;
|
||||
const now = new Date();
|
||||
const diffMs = expireDate - now;
|
||||
const diffDays = diffMs / (1000 * 60 * 60 * 24);
|
||||
@@ -3050,7 +3060,8 @@ handleDragLeave(e) {
|
||||
// 判断是否已过期
|
||||
isExpired(expiresAt) {
|
||||
if (!expiresAt) return false;
|
||||
const expireDate = new Date(expiresAt);
|
||||
const expireDate = this.parseDateValue(expiresAt);
|
||||
if (!expireDate) return false;
|
||||
const now = new Date();
|
||||
return expireDate <= now;
|
||||
},
|
||||
@@ -3148,8 +3159,8 @@ handleDragLeave(e) {
|
||||
// 格式化时间
|
||||
formatDateTime(value) {
|
||||
if (!value) return '--';
|
||||
const d = new Date(value);
|
||||
if (Number.isNaN(d.getTime())) return value;
|
||||
const d = this.parseDateValue(value);
|
||||
if (!d) return value;
|
||||
return d.toLocaleString();
|
||||
},
|
||||
|
||||
@@ -4141,14 +4152,41 @@ handleDragLeave(e) {
|
||||
return '不自动重置';
|
||||
},
|
||||
|
||||
parseDateValue(value) {
|
||||
if (!value) return null;
|
||||
if (value instanceof Date) {
|
||||
return Number.isNaN(value.getTime()) ? null : value;
|
||||
}
|
||||
|
||||
const raw = String(value).trim();
|
||||
if (!raw) return null;
|
||||
|
||||
const localMatch = raw.match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?$/);
|
||||
if (localMatch) {
|
||||
const year = Number(localMatch[1]);
|
||||
const month = Number(localMatch[2]);
|
||||
const day = Number(localMatch[3]);
|
||||
const hour = Number(localMatch[4] || 0);
|
||||
const minute = Number(localMatch[5] || 0);
|
||||
const second = Number(localMatch[6] || 0);
|
||||
|
||||
const localDate = new Date(year, month - 1, day, hour, minute, second);
|
||||
return Number.isNaN(localDate.getTime()) ? null : localDate;
|
||||
}
|
||||
|
||||
const normalized = raw.includes('T') ? raw : raw.replace(' ', 'T');
|
||||
const parsed = new Date(normalized);
|
||||
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
||||
},
|
||||
|
||||
toDateTimeLocalInput(dateString) {
|
||||
if (!dateString) return '';
|
||||
const normalized = String(dateString).trim().replace(' ', 'T');
|
||||
const match = normalized.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})/);
|
||||
if (match) return match[1];
|
||||
|
||||
const parsed = new Date(normalized);
|
||||
if (Number.isNaN(parsed.getTime())) return '';
|
||||
const parsed = this.parseDateValue(dateString);
|
||||
if (!parsed) return '';
|
||||
const year = parsed.getFullYear();
|
||||
const month = String(parsed.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(parsed.getDate()).padStart(2, '0');
|
||||
@@ -4170,15 +4208,8 @@ handleDragLeave(e) {
|
||||
formatDate(dateString) {
|
||||
if (!dateString) return '-';
|
||||
|
||||
// SQLite 返回的是 UTC 时间字符串,需要显式处理
|
||||
// 如果字符串不包含时区信息,手动添加 'Z' 标记为 UTC
|
||||
let dateStr = dateString;
|
||||
if (!dateStr.includes('Z') && !dateStr.includes('+') && !dateStr.includes('T')) {
|
||||
// SQLite 格式: "2025-11-13 16:37:19" -> ISO格式: "2025-11-13T16:37:19Z"
|
||||
dateStr = dateStr.replace(' ', 'T') + 'Z';
|
||||
}
|
||||
|
||||
const date = new Date(dateStr);
|
||||
const date = this.parseDateValue(dateString);
|
||||
if (!date) return String(dateString);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
@@ -4511,7 +4542,8 @@ handleDragLeave(e) {
|
||||
|
||||
formatLogTime(timestamp) {
|
||||
if (!timestamp) return '';
|
||||
const date = new Date(timestamp);
|
||||
const date = this.parseDateValue(timestamp);
|
||||
if (!date) return String(timestamp);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
|
||||
@@ -1369,17 +1369,36 @@
|
||||
return 'color: #9E9E9E;';
|
||||
},
|
||||
|
||||
formatDate(dateString) {
|
||||
if (!dateString) return '';
|
||||
|
||||
// SQLite 返回的是 UTC 时间字符串,需要显式处理
|
||||
let dateStr = dateString;
|
||||
if (!dateStr.includes('Z') && !dateStr.includes('+') && !dateStr.includes('T')) {
|
||||
// SQLite 格式: "2025-11-13 16:37:19" -> ISO格式: "2025-11-13T16:37:19Z"
|
||||
dateStr = dateStr.replace(' ', 'T') + 'Z';
|
||||
parseDateValue(value) {
|
||||
if (!value) return null;
|
||||
if (value instanceof Date) {
|
||||
return Number.isNaN(value.getTime()) ? null : value;
|
||||
}
|
||||
|
||||
const date = new Date(dateStr);
|
||||
const raw = String(value).trim();
|
||||
if (!raw) return null;
|
||||
|
||||
const localMatch = raw.match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?$/);
|
||||
if (localMatch) {
|
||||
const year = Number(localMatch[1]);
|
||||
const month = Number(localMatch[2]);
|
||||
const day = Number(localMatch[3]);
|
||||
const hour = Number(localMatch[4] || 0);
|
||||
const minute = Number(localMatch[5] || 0);
|
||||
const second = Number(localMatch[6] || 0);
|
||||
const localDate = new Date(year, month - 1, day, hour, minute, second);
|
||||
return Number.isNaN(localDate.getTime()) ? null : localDate;
|
||||
}
|
||||
|
||||
const normalized = raw.includes('T') ? raw : raw.replace(' ', 'T');
|
||||
const parsed = new Date(normalized);
|
||||
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
||||
},
|
||||
|
||||
formatDate(dateString) {
|
||||
if (!dateString) return '';
|
||||
const date = this.parseDateValue(dateString);
|
||||
if (!date) return String(dateString);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
@@ -1395,7 +1414,8 @@
|
||||
formatExpireTime(expiresAt) {
|
||||
if (!expiresAt) return '永久有效';
|
||||
|
||||
const expireDate = new Date(expiresAt);
|
||||
const expireDate = this.parseDateValue(expiresAt);
|
||||
if (!expireDate) return String(expiresAt);
|
||||
const now = new Date();
|
||||
const diffMs = expireDate - now;
|
||||
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
||||
@@ -1429,7 +1449,8 @@
|
||||
// 判断是否即将过期(3天内)
|
||||
isExpiringSoon(expiresAt) {
|
||||
if (!expiresAt) return false;
|
||||
const expireDate = new Date(expiresAt);
|
||||
const expireDate = this.parseDateValue(expiresAt);
|
||||
if (!expireDate) return false;
|
||||
const now = new Date();
|
||||
const diffMs = expireDate - now;
|
||||
const diffDays = diffMs / (1000 * 60 * 60 * 24);
|
||||
@@ -1439,7 +1460,8 @@
|
||||
// 判断是否已过期
|
||||
isExpired(expiresAt) {
|
||||
if (!expiresAt) return false;
|
||||
const expireDate = new Date(expiresAt);
|
||||
const expireDate = this.parseDateValue(expiresAt);
|
||||
if (!expireDate) return false;
|
||||
const now = new Date();
|
||||
return expireDate <= now;
|
||||
}
|
||||
@@ -1494,57 +1516,17 @@
|
||||
// 禁用F12和常见开发者工具快捷键(调试模式下不禁用)
|
||||
if (!isDebugMode) {
|
||||
document.addEventListener('keydown', function(e) {
|
||||
// F12
|
||||
if (e.key === 'F12' || e.keyCode === 123) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
// Ctrl+Shift+I (开发者工具)
|
||||
if (e.ctrlKey && e.shiftKey && (e.key === 'I' || e.keyCode === 73)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
// Ctrl+Shift+J (控制台)
|
||||
if (e.ctrlKey && e.shiftKey && (e.key === 'J' || e.keyCode === 74)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
// Ctrl+U (查看源代码)
|
||||
if (e.ctrlKey && (e.key === 'U' || e.keyCode === 85)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
// Ctrl+Shift+C (元素选择器)
|
||||
if (e.ctrlKey && e.shiftKey && (e.key === 'C' || e.keyCode === 67)) {
|
||||
const key = String(e.key || '').toLowerCase();
|
||||
const blocked = e.keyCode === 123
|
||||
|| (e.ctrlKey && e.shiftKey && ['i', 'j', 'c'].includes(key))
|
||||
|| (e.ctrlKey && key === 'u');
|
||||
if (blocked) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 检测开发者工具是否打开(调试模式下不检测)
|
||||
if (!isDebugMode) {
|
||||
(function() {
|
||||
const threshold = 160;
|
||||
let isDevToolsOpen = false;
|
||||
|
||||
setInterval(function() {
|
||||
const widthThreshold = window.outerWidth - window.innerWidth > threshold;
|
||||
const heightThreshold = window.outerHeight - window.innerHeight > threshold;
|
||||
|
||||
if (!(heightThreshold && widthThreshold) &&
|
||||
((window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized) || widthThreshold || heightThreshold)) {
|
||||
if (!isDevToolsOpen) {
|
||||
isDevToolsOpen = true;
|
||||
console.clear();
|
||||
}
|
||||
} else {
|
||||
isDevToolsOpen = false;
|
||||
}
|
||||
}, 500);
|
||||
})();
|
||||
}
|
||||
|
||||
// 禁用console输出(调试模式下不禁用)
|
||||
if (!isDebugMode && window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1') {
|
||||
console.log = function() {};
|
||||
|
||||
Reference in New Issue
Block a user