Files
Yi.Framework/Yi.Ai.Vue3/index.html
2026-02-01 00:52:10 +08:00

294 lines
8.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<link rel="icon" href="/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="baidu-site-verification" content="codeva-mkVpSFmYJm"/>
<meta name="description" content="意心AI一站式多模型 AI 平台,提供 AI 服务"/>
<meta name="description" content="各大主流AI无限制使用直连AIclaude ,DeepSeek,open-ai"/>
<meta name="keywords" content="意心AI, 多模型AI, AI工具"/>
<meta name="keywords" content="橙子chengzi,橙子老哥ccnetcore意社区"/>
<meta name="author" content="橙子chengzi,橙子老哥ccnetcore"/>
<meta name="version" content="%VITE_APP_VERSION%"/>
<meta name="version" content="%VITE_WEB_TITLE%"/>
<title>%VITE_WEB_TITLE%</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<!-- DNS 预解析和预连接 -->
<link rel="dns-prefetch" href="//api.yourdomain.com">
<link rel="preconnect" href="//api.yourdomain.com" crossorigin>
<!-- 预加载关键资源 -->
<link rel="preload" href="/src/main.ts" as="script" crossorigin>
<link rel="modulepreload" href="/src/main.ts">
<style>
/* 全局样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 加载动画样式 */
.loader-container {
position: fixed;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #fff;
z-index: 1000;
transition: opacity 0.5s ease;
}
.loader-title {
font-size: clamp(1.5rem, 3vw, 2.5rem);
font-weight: bold;
margin-bottom: 0.5rem;
letter-spacing: -0.02em;
}
.loader-subtitle {
font-size: 0.875rem;
color: #555;
margin-bottom: 1.5rem;
}
.loader-logo {
font-size: 3rem;
margin-bottom: 1.5rem;
display: flex;
justify-content: center;
}
.pulse-box {
width: 32px;
height: 32px;
background: #000;
display: inline-block;
transform-origin: center;
animation: pulse 1.2s infinite ease-in-out;
}
@keyframes pulse {
0% {
transform: scale(1) rotate(0deg);
opacity: 1;
}
50% {
transform: scale(1.2) rotate(45deg);
opacity: 0.8;
}
100% {
transform: scale(1) rotate(90deg);
opacity: 1;
}
}
.loader-text {
font-size: 1.5rem;
font-weight: bold;
margin-bottom: 1rem;
}
.loader-progress-bar {
width: 90%;
max-width: 400px;
height: 8px;
background: #f0f0f0;
border-radius: 4px;
overflow: hidden;
}
.loader-progress {
height: 100%;
width: 0%;
background: #000;
transition: width 0.3s ease-out;
}
</style>
</head>
<body>
<!-- 加载动画容器 -->
<div id="yixinai-loader" class="loader-container">
<div class="loader-title">%APP_FULL_NAME%</div>
<div class="loader-subtitle">海外地址仅首次访问预计加载约10秒无需梯子</div>
<div class="loader-logo">
<div class="pulse-box"></div>
</div>
<div class="loader-progress-bar">
<div id="loader-progress" class="loader-progress"></div>
</div>
<div id="loader-text" class="loader-text" style="font-size: 0.875rem; margin-top: 0.5rem; color: #666;">加载中...</div>
</div>
<div id="app"></div>
<script>
// 资源加载进度跟踪 - 增强版
(function() {
const progressBar = document.getElementById('loader-progress');
const loaderText = document.getElementById('loader-text');
const loader = document.getElementById('yixinai-loader');
let progress = 0;
let resourcesLoaded = false;
let vueAppMounted = false;
let appRendered = false;
// 更新进度条
function updateProgress(value, text) {
progress = Math.min(value, 99);
if (progressBar) progressBar.style.width = progress.toFixed(1) + '%';
if (loaderText) loaderText.textContent = text;
}
// 阶段管理
const stages = {
init: { weight: 15, name: '初始化' },
resources: { weight: 35, name: '加载资源' },
scripts: { weight: 25, name: '执行脚本' },
render: { weight: 15, name: '渲染页面' },
complete: { weight: 10, name: '启动应用' }
};
let completedStages = new Set();
let currentStage = 'init';
function calculateProgress() {
let totalProgress = 0;
for (const [key, stage] of Object.entries(stages)) {
if (completedStages.has(key)) {
totalProgress += stage.weight;
} else if (key === currentStage) {
// 当前阶段完成一部分
totalProgress += stage.weight * 0.5;
}
}
return Math.min(totalProgress, 99);
}
// 阶段完成
function completeStage(stageName, nextStage) {
completedStages.add(stageName);
currentStage = nextStage || stageName;
const stage = stages[stageName];
updateProgress(calculateProgress(), stage ? `${stage.name}完成` : '加载中...');
}
// 监听资源加载 - 使用更可靠的方式
const resourceTimings = [];
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
resourceTimings.push(...entries);
// 统计未完成资源
const pendingResources = performance.getEntriesByType('resource')
.filter(r => !r.responseEnd || r.responseEnd === 0).length;
if (pendingResources === 0 && resourceTimings.length > 0) {
completeStage('resources', 'scripts');
} else {
updateProgress(calculateProgress(), `加载资源中... (${resourceTimings.length} 已加载)`);
}
});
try {
observer.observe({ entryTypes: ['resource'] });
} catch (e) {
// 降级处理
}
// 初始进度
let initProgress = 0;
function simulateInitProgress() {
if (initProgress < stages.init.weight) {
initProgress += 1;
updateProgress(initProgress, '正在初始化...');
if (initProgress < stages.init.weight) {
setTimeout(simulateInitProgress, 30);
} else {
completeStage('init', 'resources');
}
}
}
simulateInitProgress();
// 页面资源加载完成
window.addEventListener('load', () => {
completeStage('resources', 'scripts');
resourcesLoaded = true;
// 给脚本执行时间
setTimeout(() => {
completeStage('scripts', 'render');
}, 300);
checkAndHideLoader();
});
// 暴露全局方法供 Vue 应用调用 - 分阶段调用
window.__hideAppLoader = function(stage) {
if (stage === 'mounted') {
vueAppMounted = true;
completeStage('scripts', 'render');
} else if (stage === 'rendered') {
appRendered = true;
completeStage('render', 'complete');
}
checkAndHideLoader();
};
// 检查是否可以隐藏加载动画
function checkAndHideLoader() {
// 需要满足资源加载完成、Vue 挂载、页面渲染完成
if (resourcesLoaded && vueAppMounted && appRendered) {
completeStage('complete', '');
updateProgress(100, '加载完成');
// 确保最小显示时间,避免闪烁
const minDisplayTime = 1000;
const elapsed = Date.now() - startTime;
const remaining = Math.max(0, minDisplayTime - elapsed);
setTimeout(() => {
if (loader) {
loader.style.opacity = '0';
loader.style.transition = 'opacity 0.5s ease';
setTimeout(() => {
loader.remove();
delete window.__hideAppLoader;
}, 500);
}
}, remaining);
}
}
const startTime = Date.now();
// 超时保护:最多显示 30 秒
setTimeout(() => {
if (loader && loader.parentNode) {
console.warn('加载超时,强制隐藏加载动画');
vueAppMounted = true;
resourcesLoaded = true;
appRendered = true;
checkAndHideLoader();
}
}, 30000);
})();
</script>
<script type="module" src="/src/main.ts"></script>
</body>
</html>