|
<!DOCTYPE html> |
|
<html lang="zh-CN"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Huggingface环境矢量图形修复测试</title> |
|
<style> |
|
body { |
|
font-family: 'Microsoft YaHei', Arial, sans-serif; |
|
margin: 0; |
|
padding: 20px; |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
color: #333; |
|
min-height: 100vh; |
|
} |
|
|
|
.container { |
|
max-width: 1200px; |
|
margin: 0 auto; |
|
background: white; |
|
border-radius: 15px; |
|
box-shadow: 0 20px 40px rgba(0,0,0,0.1); |
|
overflow: hidden; |
|
} |
|
|
|
.header { |
|
background: linear-gradient(135deg, #ff6b6b, #ee5a24); |
|
color: white; |
|
padding: 30px; |
|
text-align: center; |
|
} |
|
|
|
.header h1 { |
|
margin: 0; |
|
font-size: 2.5em; |
|
font-weight: 300; |
|
} |
|
|
|
.header p { |
|
margin: 10px 0 0 0; |
|
opacity: 0.9; |
|
font-size: 1.1em; |
|
} |
|
|
|
.content { |
|
padding: 40px; |
|
} |
|
|
|
.section { |
|
margin-bottom: 40px; |
|
padding: 25px; |
|
border: 2px solid #f0f0f0; |
|
border-radius: 10px; |
|
background: #fafafa; |
|
} |
|
|
|
.section h2 { |
|
color: #2c3e50; |
|
margin-top: 0; |
|
border-bottom: 3px solid #3498db; |
|
padding-bottom: 10px; |
|
} |
|
|
|
.test-area { |
|
display: flex; |
|
gap: 20px; |
|
flex-wrap: wrap; |
|
margin: 20px 0; |
|
} |
|
|
|
.svg-container { |
|
flex: 1; |
|
min-width: 200px; |
|
padding: 20px; |
|
border: 2px dashed #ddd; |
|
border-radius: 8px; |
|
text-align: center; |
|
background: white; |
|
} |
|
|
|
.svg-container h3 { |
|
margin-top: 0; |
|
color: #34495e; |
|
} |
|
|
|
.test-svg { |
|
margin: 10px; |
|
border: 1px solid #eee; |
|
} |
|
|
|
.controls { |
|
margin: 20px 0; |
|
text-align: center; |
|
} |
|
|
|
.btn { |
|
background: linear-gradient(135deg, #667eea, #764ba2); |
|
color: white; |
|
border: none; |
|
padding: 12px 25px; |
|
margin: 5px; |
|
border-radius: 25px; |
|
cursor: pointer; |
|
font-size: 16px; |
|
transition: all 0.3s ease; |
|
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); |
|
} |
|
|
|
.btn:hover { |
|
transform: translateY(-2px); |
|
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4); |
|
} |
|
|
|
.btn:active { |
|
transform: translateY(0); |
|
} |
|
|
|
.btn.success { |
|
background: linear-gradient(135deg, #00b894, #00a085); |
|
} |
|
|
|
.btn.warning { |
|
background: linear-gradient(135deg, #fdcb6e, #e17055); |
|
} |
|
|
|
.btn.danger { |
|
background: linear-gradient(135deg, #fd79a8, #e84393); |
|
} |
|
|
|
.results { |
|
margin-top: 20px; |
|
padding: 20px; |
|
background: #f8f9fa; |
|
border-radius: 8px; |
|
border-left: 4px solid #007bff; |
|
} |
|
|
|
.log { |
|
background: #2c3e50; |
|
color: #ecf0f1; |
|
padding: 15px; |
|
border-radius: 8px; |
|
font-family: 'Courier New', monospace; |
|
font-size: 14px; |
|
max-height: 300px; |
|
overflow-y: auto; |
|
white-space: pre-wrap; |
|
margin-top: 15px; |
|
} |
|
|
|
.environment-info { |
|
background: linear-gradient(135deg, #74b9ff, #0984e3); |
|
color: white; |
|
padding: 20px; |
|
border-radius: 10px; |
|
margin-bottom: 20px; |
|
} |
|
|
|
.environment-info h3 { |
|
margin-top: 0; |
|
} |
|
|
|
.fix-list { |
|
background: #d4edda; |
|
border: 1px solid #c3e6cb; |
|
border-radius: 8px; |
|
padding: 20px; |
|
margin: 20px 0; |
|
} |
|
|
|
.fix-item { |
|
margin: 8px 0; |
|
padding: 8px 12px; |
|
background: white; |
|
border-radius: 5px; |
|
border-left: 4px solid #28a745; |
|
} |
|
|
|
.preview-container { |
|
display: flex; |
|
gap: 20px; |
|
margin-top: 20px; |
|
flex-wrap: wrap; |
|
} |
|
|
|
.preview-item { |
|
flex: 1; |
|
min-width: 300px; |
|
padding: 15px; |
|
border: 1px solid #ddd; |
|
border-radius: 8px; |
|
background: white; |
|
} |
|
|
|
.preview-item h4 { |
|
margin-top: 0; |
|
color: #495057; |
|
} |
|
|
|
.preview-image { |
|
max-width: 100%; |
|
border: 1px solid #eee; |
|
border-radius: 4px; |
|
} |
|
|
|
.status { |
|
display: inline-block; |
|
padding: 4px 8px; |
|
border-radius: 12px; |
|
font-size: 12px; |
|
font-weight: bold; |
|
margin-left: 10px; |
|
} |
|
|
|
.status.success { |
|
background: #d4edda; |
|
color: #155724; |
|
} |
|
|
|
.status.error { |
|
background: #f8d7da; |
|
color: #721c24; |
|
} |
|
|
|
.status.warning { |
|
background: #fff3cd; |
|
color: #856404; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="container"> |
|
<div class="header"> |
|
<h1>🚀 Huggingface环境矢量图形修复测试</h1> |
|
<p>专门针对Huggingface Spaces部署环境的矢量图形渲染优化</p> |
|
</div> |
|
|
|
<div class="content"> |
|
|
|
<div class="environment-info"> |
|
<h3>🌐 环境检测</h3> |
|
<div id="environment-status">正在检测环境...</div> |
|
</div> |
|
|
|
|
|
<div class="section"> |
|
<h2>🔧 Huggingface环境修复措施</h2> |
|
<div class="fix-list"> |
|
<div class="fix-item">✅ 禁用CORS检查,使用allowTaint模式</div> |
|
<div class="fix-item">✅ 优化html2canvas配置,适配Docker Alpine环境</div> |
|
<div class="fix-item">✅ 简化SVG序列化,移除problematic属性</div> |
|
<div class="fix-item">✅ 使用btoa编码替代复杂的Base64实现</div> |
|
<div class="fix-item">✅ 添加多级回退机制,确保渲染成功</div> |
|
<div class="fix-item">✅ 针对Chromium浏览器优化SVG处理</div> |
|
<div class="fix-item">✅ 禁用foreignObject渲染,避免跨域问题</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="section"> |
|
<h2>🎨 测试SVG图形</h2> |
|
<div class="test-area"> |
|
<div class="svg-container"> |
|
<h3>几何图形</h3> |
|
<svg class="test-svg" width="150" height="100" id="svg1"> |
|
<rect x="10" y="10" width="60" height="40" fill="#ff6b6b" stroke="#c0392b" stroke-width="2"/> |
|
<circle cx="100" cy="30" r="20" fill="#3498db" stroke="#2980b9" stroke-width="2"/> |
|
<polygon points="80,60 100,80 120,60 110,90 90,90" fill="#2ecc71" stroke="#27ae60" stroke-width="2"/> |
|
</svg> |
|
</div> |
|
|
|
<div class="svg-container"> |
|
<h3>路径图形</h3> |
|
<svg class="test-svg" width="150" height="100" id="svg2"> |
|
<path d="M20,50 Q40,20 60,50 T100,50" stroke="#9b59b6" stroke-width="3" fill="none"/> |
|
<path d="M10,80 L50,60 L90,80 L130,60" stroke="#f39c12" stroke-width="2" fill="none"/> |
|
<ellipse cx="75" cy="30" rx="30" ry="15" fill="#e74c3c" opacity="0.7"/> |
|
</svg> |
|
</div> |
|
|
|
<div class="svg-container"> |
|
<h3>文本图形</h3> |
|
<svg class="test-svg" width="150" height="100" id="svg3"> |
|
<rect width="100%" height="100%" fill="#ecf0f1"/> |
|
<text x="75" y="30" text-anchor="middle" font-family="Arial" font-size="14" fill="#2c3e50">SVG文本</text> |
|
<text x="75" y="50" text-anchor="middle" font-family="Arial" font-size="12" fill="#7f8c8d">Huggingface</text> |
|
<text x="75" y="70" text-anchor="middle" font-family="Arial" font-size="10" fill="#95a5a6">优化测试</text> |
|
</svg> |
|
</div> |
|
|
|
<div class="svg-container"> |
|
<h3>复杂图形</h3> |
|
<svg class="test-svg" width="150" height="100" id="svg4"> |
|
<defs> |
|
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%"> |
|
<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" /> |
|
<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" /> |
|
</linearGradient> |
|
</defs> |
|
<rect width="100%" height="100%" fill="url(#grad1)"/> |
|
<circle cx="75" cy="50" r="30" fill="white" opacity="0.8"/> |
|
<text x="75" y="55" text-anchor="middle" font-family="Arial" font-size="16" fill="#333">HF</text> |
|
</svg> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="section"> |
|
<h2>🧪 测试控制</h2> |
|
<div class="controls"> |
|
<button class="btn" onclick="testEnvironmentDetection()">检测环境</button> |
|
<button class="btn success" onclick="testSvgConversion()">测试SVG转换</button> |
|
<button class="btn warning" onclick="testHuggingfaceRenderer()">测试HF渲染器</button> |
|
<button class="btn danger" onclick="testExportFunction()">测试导出功能</button> |
|
<button class="btn" onclick="clearResults()">清除结果</button> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="section"> |
|
<h2>📊 测试结果</h2> |
|
<div id="test-results" class="results"> |
|
<p>点击上方按钮开始测试...</p> |
|
</div> |
|
|
|
|
|
<div class="preview-container" id="preview-container" style="display: none;"> |
|
<div class="preview-item"> |
|
<h4>原始SVG</h4> |
|
<div id="original-svg"></div> |
|
</div> |
|
<div class="preview-item"> |
|
<h4>转换结果</h4> |
|
<div id="converted-result"></div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="log" id="log-area"></div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
|
|
function isHuggingfaceEnvironment() { |
|
return ( |
|
window.location.hostname.includes('hf.space') || |
|
window.location.hostname.includes('huggingface.co') || |
|
window.location.hostname.includes('localhost') || |
|
window.location.hostname.includes('127.0.0.1') |
|
); |
|
} |
|
|
|
|
|
function log(message, type = 'info') { |
|
const logArea = document.getElementById('log-area'); |
|
const timestamp = new Date().toLocaleTimeString(); |
|
const logEntry = `[${timestamp}] ${type.toUpperCase()}: ${message}\n`; |
|
logArea.textContent += logEntry; |
|
logArea.scrollTop = logArea.scrollHeight; |
|
console.log(logEntry); |
|
} |
|
|
|
|
|
function updateResults(content) { |
|
document.getElementById('test-results').innerHTML = content; |
|
} |
|
|
|
|
|
function clearResults() { |
|
document.getElementById('test-results').innerHTML = '<p>结果已清除,点击测试按钮开始新的测试...</p>'; |
|
document.getElementById('log-area').textContent = ''; |
|
document.getElementById('preview-container').style.display = 'none'; |
|
} |
|
|
|
|
|
function testEnvironmentDetection() { |
|
log('开始环境检测测试'); |
|
|
|
const isHF = isHuggingfaceEnvironment(); |
|
const userAgent = navigator.userAgent; |
|
const hostname = window.location.hostname; |
|
|
|
const envInfo = { |
|
isHuggingface: isHF, |
|
hostname: hostname, |
|
userAgent: userAgent, |
|
isChromium: userAgent.includes('Chrome'), |
|
isDocker: userAgent.includes('HeadlessChrome'), |
|
viewport: `${window.innerWidth}x${window.innerHeight}` |
|
}; |
|
|
|
log(`环境检测结果: ${JSON.stringify(envInfo, null, 2)}`); |
|
|
|
const statusHtml = ` |
|
<h3>环境检测结果</h3> |
|
<p><strong>Huggingface环境:</strong> <span class="status ${isHF ? 'success' : 'warning'}">${isHF ? '是' : '否'}</span></p> |
|
<p><strong>主机名:</strong> ${hostname}</p> |
|
<p><strong>浏览器:</strong> ${envInfo.isChromium ? 'Chromium/Chrome' : '其他'}</p> |
|
<p><strong>Docker环境:</strong> ${envInfo.isDocker ? '是' : '否'}</p> |
|
<p><strong>视口尺寸:</strong> ${envInfo.viewport}</p> |
|
`; |
|
|
|
updateResults(statusHtml); |
|
|
|
|
|
document.getElementById('environment-status').innerHTML = ` |
|
<strong>当前环境:</strong> ${isHF ? 'Huggingface Spaces' : '本地开发'} |
|
<span class="status ${isHF ? 'success' : 'warning'}">${isHF ? 'HF环境' : '本地环境'}</span> |
|
`; |
|
} |
|
|
|
|
|
function huggingfaceOptimizedSvg2Base64(element) { |
|
try { |
|
log('开始Huggingface优化的SVG转换'); |
|
|
|
const clonedElement = element.cloneNode(true); |
|
|
|
if (clonedElement.tagName.toLowerCase() === 'svg') { |
|
const svgElement = clonedElement; |
|
|
|
|
|
svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); |
|
|
|
|
|
const rect = element.getBoundingClientRect(); |
|
const width = rect.width || 100; |
|
const height = rect.height || 100; |
|
|
|
|
|
svgElement.setAttribute('width', width.toString()); |
|
svgElement.setAttribute('height', height.toString()); |
|
svgElement.setAttribute('viewBox', `0 0 ${width} ${height}`); |
|
|
|
|
|
const removeProblematicAttrs = (elem) => { |
|
const problematicAttrs = ['vector-effect', 'xmlns:xlink']; |
|
problematicAttrs.forEach(attr => { |
|
if (elem.hasAttribute && elem.hasAttribute(attr)) { |
|
elem.removeAttribute(attr); |
|
} |
|
}); |
|
|
|
if (elem.children) { |
|
Array.from(elem.children).forEach(child => { |
|
removeProblematicAttrs(child); |
|
}); |
|
} |
|
}; |
|
|
|
removeProblematicAttrs(svgElement); |
|
|
|
|
|
let svgString = svgElement.outerHTML; |
|
|
|
|
|
svgString = svgString.replace(/vector-effect="[^"]*"/g, ''); |
|
svgString = svgString.replace(/xmlns:xlink="[^"]*"/g, ''); |
|
|
|
|
|
if (!svgString.includes('xmlns="http://www.w3.org/2000/svg"')) { |
|
svgString = svgString.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"'); |
|
} |
|
|
|
log(`SVG字符串准备完成: 长度=${svgString.length}`); |
|
|
|
|
|
const base64 = btoa(unescape(encodeURIComponent(svgString))); |
|
const result = `data:image/svg+xml;base64,${base64}`; |
|
|
|
log('SVG转换成功'); |
|
return result; |
|
} |
|
|
|
throw new Error('不是SVG元素'); |
|
|
|
} catch (error) { |
|
log(`SVG转换失败: ${error.message}`, 'error'); |
|
|
|
|
|
const rect = element.getBoundingClientRect(); |
|
const width = rect.width || 100; |
|
const height = rect.height || 100; |
|
|
|
const placeholderSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"> |
|
<rect width="100%" height="100%" fill="#f5f5f5" stroke="#ddd" stroke-width="1"/> |
|
<text x="50%" y="50%" text-anchor="middle" dy="0.3em" font-family="Arial, sans-serif" font-size="14" fill="#999">SVG</text> |
|
</svg>`; |
|
|
|
const base64 = btoa(unescape(encodeURIComponent(placeholderSvg))); |
|
log('使用占位符SVG'); |
|
return `data:image/svg+xml;base64,${base64}`; |
|
} |
|
} |
|
|
|
|
|
function testSvgConversion() { |
|
log('开始SVG转换测试'); |
|
|
|
const svgElements = document.querySelectorAll('.test-svg'); |
|
let successCount = 0; |
|
let totalCount = svgElements.length; |
|
const results = []; |
|
|
|
svgElements.forEach((svg, index) => { |
|
try { |
|
const startTime = performance.now(); |
|
const base64Result = huggingfaceOptimizedSvg2Base64(svg); |
|
const endTime = performance.now(); |
|
const duration = (endTime - startTime).toFixed(2); |
|
|
|
if (base64Result && base64Result.length > 100) { |
|
successCount++; |
|
results.push({ |
|
id: svg.id, |
|
status: 'success', |
|
duration: duration, |
|
size: base64Result.length, |
|
preview: base64Result.substring(0, 100) + '...' |
|
}); |
|
log(`SVG ${svg.id} 转换成功: ${duration}ms, 大小=${base64Result.length}`); |
|
} else { |
|
results.push({ |
|
id: svg.id, |
|
status: 'error', |
|
error: '转换结果无效' |
|
}); |
|
log(`SVG ${svg.id} 转换失败: 结果无效`, 'error'); |
|
} |
|
} catch (error) { |
|
results.push({ |
|
id: svg.id, |
|
status: 'error', |
|
error: error.message |
|
}); |
|
log(`SVG ${svg.id} 转换失败: ${error.message}`, 'error'); |
|
} |
|
}); |
|
|
|
const successRate = ((successCount / totalCount) * 100).toFixed(1); |
|
|
|
let resultHtml = ` |
|
<h3>SVG转换测试结果</h3> |
|
<p><strong>成功率:</strong> ${successRate}% (${successCount}/${totalCount})</p> |
|
<div style="margin-top: 15px;"> |
|
`; |
|
|
|
results.forEach(result => { |
|
const statusClass = result.status === 'success' ? 'success' : 'error'; |
|
resultHtml += ` |
|
<div style="margin: 10px 0; padding: 10px; border: 1px solid #ddd; border-radius: 5px;"> |
|
<strong>${result.id}:</strong> |
|
<span class="status ${statusClass}">${result.status}</span> |
|
${result.duration ? `<br>耗时: ${result.duration}ms` : ''} |
|
${result.size ? `<br>大小: ${result.size} 字符` : ''} |
|
${result.error ? `<br>错误: ${result.error}` : ''} |
|
</div> |
|
`; |
|
}); |
|
|
|
resultHtml += '</div>'; |
|
updateResults(resultHtml); |
|
|
|
log(`SVG转换测试完成: 成功率=${successRate}%`); |
|
} |
|
|
|
|
|
function testHuggingfaceRenderer() { |
|
log('开始Huggingface渲染器测试'); |
|
|
|
const isHF = isHuggingfaceEnvironment(); |
|
|
|
|
|
const config = { |
|
useCORS: false, |
|
allowTaint: true, |
|
foreignObjectRendering: false, |
|
scale: isHF ? 1 : 2, |
|
logging: false, |
|
timeout: 30000 |
|
}; |
|
|
|
log(`渲染配置: ${JSON.stringify(config, null, 2)}`); |
|
|
|
|
|
const svgElements = document.querySelectorAll('.test-svg'); |
|
const testResults = []; |
|
|
|
svgElements.forEach((svg, index) => { |
|
try { |
|
const rect = svg.getBoundingClientRect(); |
|
const hasValidDimensions = rect.width > 0 && rect.height > 0; |
|
|
|
testResults.push({ |
|
id: svg.id, |
|
dimensions: `${rect.width}x${rect.height}`, |
|
hasValidDimensions, |
|
childCount: svg.children.length, |
|
status: hasValidDimensions ? 'ready' : 'warning' |
|
}); |
|
|
|
log(`${svg.id}: 尺寸=${rect.width}x${rect.height}, 子元素=${svg.children.length}`); |
|
} catch (error) { |
|
testResults.push({ |
|
id: svg.id, |
|
status: 'error', |
|
error: error.message |
|
}); |
|
log(`${svg.id}: 检查失败 - ${error.message}`, 'error'); |
|
} |
|
}); |
|
|
|
let resultHtml = ` |
|
<h3>Huggingface渲染器测试</h3> |
|
<p><strong>环境:</strong> ${isHF ? 'Huggingface Spaces' : '本地环境'}</p> |
|
<p><strong>配置:</strong> CORS=${config.useCORS}, AllowTaint=${config.allowTaint}, Scale=${config.scale}</p> |
|
<div style="margin-top: 15px;"> |
|
`; |
|
|
|
testResults.forEach(result => { |
|
const statusClass = result.status === 'ready' ? 'success' : |
|
result.status === 'warning' ? 'warning' : 'error'; |
|
resultHtml += ` |
|
<div style="margin: 10px 0; padding: 10px; border: 1px solid #ddd; border-radius: 5px;"> |
|
<strong>${result.id}:</strong> |
|
<span class="status ${statusClass}">${result.status}</span> |
|
${result.dimensions ? `<br>尺寸: ${result.dimensions}` : ''} |
|
${result.childCount !== undefined ? `<br>子元素: ${result.childCount}` : ''} |
|
${result.error ? `<br>错误: ${result.error}` : ''} |
|
</div> |
|
`; |
|
}); |
|
|
|
resultHtml += '</div>'; |
|
updateResults(resultHtml); |
|
|
|
log('Huggingface渲染器测试完成'); |
|
} |
|
|
|
|
|
function testExportFunction() { |
|
log('开始导出功能测试'); |
|
|
|
const testSvg = document.getElementById('svg1'); |
|
if (!testSvg) { |
|
log('找不到测试SVG元素', 'error'); |
|
return; |
|
} |
|
|
|
try { |
|
|
|
document.getElementById('preview-container').style.display = 'flex'; |
|
|
|
|
|
const originalContainer = document.getElementById('original-svg'); |
|
const svgClone = testSvg.cloneNode(true); |
|
originalContainer.innerHTML = ''; |
|
originalContainer.appendChild(svgClone); |
|
|
|
|
|
const base64Result = huggingfaceOptimizedSvg2Base64(testSvg); |
|
|
|
|
|
const resultContainer = document.getElementById('converted-result'); |
|
const img = document.createElement('img'); |
|
img.src = base64Result; |
|
img.className = 'preview-image'; |
|
img.onload = () => { |
|
log('转换结果图片加载成功'); |
|
}; |
|
img.onerror = () => { |
|
log('转换结果图片加载失败', 'error'); |
|
}; |
|
|
|
resultContainer.innerHTML = ''; |
|
resultContainer.appendChild(img); |
|
|
|
const resultHtml = ` |
|
<h3>导出功能测试结果</h3> |
|
<p><strong>状态:</strong> <span class="status success">成功</span></p> |
|
<p><strong>Base64长度:</strong> ${base64Result.length} 字符</p> |
|
<p><strong>数据类型:</strong> ${base64Result.substring(0, 30)}...</p> |
|
<p>请查看下方预览区域对比原始SVG和转换结果。</p> |
|
`; |
|
|
|
updateResults(resultHtml); |
|
log('导出功能测试完成'); |
|
|
|
} catch (error) { |
|
const resultHtml = ` |
|
<h3>导出功能测试结果</h3> |
|
<p><strong>状态:</strong> <span class="status error">失败</span></p> |
|
<p><strong>错误:</strong> ${error.message}</p> |
|
`; |
|
|
|
updateResults(resultHtml); |
|
log(`导出功能测试失败: ${error.message}`, 'error'); |
|
} |
|
} |
|
|
|
|
|
window.addEventListener('load', () => { |
|
log('页面加载完成,开始初始化'); |
|
testEnvironmentDetection(); |
|
}); |
|
</script> |
|
</body> |
|
</html> |