feat: 新增5个应用+分页轮播+GPU优化

- 新增 MaxCount/法时时/技能人才胜任力/K12AI/医科大学 5个应用
- 改为分页轮播模式,每页9个,左右翻页+圆点指示器
- 移除所有backdrop-filter降低GPU占用
- mousemove用requestAnimationFrame节流
- 帮助弹窗新增5个应用测试账号信息
- 美雇描述改为劳动关系管理
This commit is contained in:
selfrelease
2026-06-28 23:28:39 +08:00
parent 06b37e3056
commit 4fadaad383
+263 -14
View File
@@ -201,6 +201,82 @@
animation: fadeInUp 0.64s cubic-bezier(0.22, 1, 0.36, 1) forwards;
opacity: 0;
}
.carousel-wrapper {
position: relative;
overflow: hidden;
width: 100%;
}
.carousel-track {
display: flex;
width: 100%;
transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.carousel-page {
flex: 0 0 100%;
min-width: 100%;
width: 100%;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 220px;
align-content: start;
gap: 1rem;
padding: 0 0.25rem;
}
@media (max-width: 768px) {
.carousel-page { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 520px) {
.carousel-page { grid-template-columns: 1fr; }
}
.carousel-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 10;
width: 2rem;
height: 2rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: oklch(0.7 0.02 260);
background: oklch(0.15 0.015 260 / 0.8);
border: 1px solid oklch(0.3 0.03 265 / 0.5);
cursor: pointer;
transition: all 0.25s ease;
}
.carousel-btn:hover {
color: oklch(0.9 0.05 260);
background: oklch(0.2 0.02 260 / 0.9);
border-color: oklch(0.4 0.05 265 / 0.6);
}
.carousel-btn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.carousel-btn.prev { left: -0.75rem; }
.carousel-btn.next { right: -0.75rem; }
.carousel-dots {
display: flex;
justify-content: center;
gap: 0.4rem;
margin-top: 1.25rem;
}
.carousel-dot {
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
background: oklch(0.3 0.02 265 / 0.5);
border: none;
cursor: pointer;
transition: all 0.25s ease;
padding: 0;
}
.carousel-dot.active {
background: oklch(0.68 0.16 235);
width: 1.25rem;
border-radius: 0.25rem;
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
@@ -318,7 +394,7 @@
<div class="flex items-center gap-3">
<span class="hidden md:inline-flex items-center gap-2 rounded-full bg-white/5 px-2.5 py-1 text-[11px] font-semibold text-slate-400 ring-1 ring-white/10">
<span class="h-1.5 w-1.5 rounded-full bg-emerald-400 shadow-[0_0_12px_oklch(0.72_0.18_150)]"></span>
9 个智能应用在线 / 9 Apps Online
14 个智能应用在线 / 14 Apps Online
</span>
<button class="help-btn w-7 h-7 rounded-full flex items-center justify-center" onclick="toggleHelpModal(true)" aria-label="帮助 / Help">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 18v-3"/><circle cx="12" cy="12" r="9"/><path d="M8.5 8.5a3.5 3.5 0 1 1 5 3c-1 .5-1.5 1-1.5 2"/></svg>
@@ -344,8 +420,15 @@
</section>
<!-- App Grid -->
<section class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-5" id="appGrid">
<!-- Cards will be injected by JS -->
<section class="carousel-wrapper" id="carouselWrapper">
<button class="carousel-btn prev" id="carouselPrev" aria-label="上一页 / Previous" onclick="changePage(-1)">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M15 18l-6-6 6-6"/></svg>
</button>
<div class="carousel-track" id="carouselTrack"></div>
<button class="carousel-btn next" id="carouselNext" aria-label="下一页 / Next" onclick="changePage(1)">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 18l6-6-6-6"/></svg>
</button>
<div class="carousel-dots" id="carouselDots"></div>
</section>
<!-- Footer -->
@@ -443,13 +526,13 @@
},
{
name: '美雇 / MeiGu',
subtitle: '灵活用工平台 / Flexible Staffing',
desc: '专业灵活用工与雇佣服务平台 · Professional flexible staffing and employment platform',
subtitle: '劳动关系管理 / Labor Relations',
desc: '企业用工权与劳动关系管理平台,合规高效管理雇佣关系 · Enterprise labor relations and employment rights management platform',
url: 'https://www.uumeigu.com/',
icon: 'briefcase',
accent: 'oklch(0.64 0.18 210)',
accent2: 'oklch(0.69 0.16 235)',
tag: '用工 / Staffing'
tag: '劳资 / Labor'
},
{
name: '项目风险管控 / Risk Control',
@@ -470,10 +553,63 @@
accent: 'oklch(0.47 0.07 255)',
accent2: 'oklch(0.56 0.08 235)',
tag: '政务 / Gov'
},
{
name: 'MaxCount',
subtitle: '财务记账 / Finance & Accounting',
desc: '智能财务记账系统,高效管理企业收支与账目 · Smart financial bookkeeping for enterprise income and expenses',
url: 'http://62.234.107.180:9001/',
icon: 'chart',
accent: 'oklch(0.56 0.18 150)',
accent2: 'oklch(0.62 0.16 180)',
tag: '财务 / Finance'
},
{
name: '法时时 / FaShiShi',
subtitle: '法律服务平台 / Legal Services',
desc: '专业法律服务平台,随时获取法律咨询与支持 · Professional legal services platform for on-demand advice',
url: 'http://www.fashishi.cn',
icon: 'briefcase',
accent: 'oklch(0.55 0.16 30)',
accent2: 'oklch(0.62 0.14 55)',
tag: '法律 / Legal'
},
{
name: '技能人才胜任力',
subtitle: '技能胜任力评估 / Skills Assessment',
desc: '技能人才胜任力评估系统,科学测评技能水平 · Technical skill competency assessment system',
url: 'https://lianxingtest.feihec.com/',
icon: 'clipboard',
accent: 'oklch(0.58 0.18 210)',
accent2: 'oklch(0.65 0.16 235)',
tag: '技能 / Skills'
},
{
name: 'K12AI应用中心 / K12 AI Hub',
subtitle: 'K12 AI 教育应用 / K12 AI Education',
desc: 'K12 场景 AI 应用统一入口,涵盖管理、家长、课堂多端 · Unified K12 AI education portal for admin, parents and classrooms',
url: 'http://101.96.196.218',
icon: 'users',
accent: 'oklch(0.62 0.2 80)',
accent2: 'oklch(0.68 0.18 105)',
tag: 'K12 / Edu'
},
{
name: '医科大学AI学习中心 / Med AI Center',
subtitle: '医学AI学习平台 / Medical AI Learning',
desc: '医科大学 AI 学习平台,助力医学教育与智能训练 · Medical university AI learning center for smart education',
url: 'http://101.96.196.218:4100/login',
icon: 'brain',
accent: 'oklch(0.58 0.18 200)',
accent2: 'oklch(0.65 0.16 225)',
tag: '医学 / Med'
}
];
const grid = document.getElementById('appGrid');
const grid = document.getElementById('carouselTrack');
const PAGE_SIZE = 9;
let currentPage = 0;
const totalPages = Math.ceil(apps.length / PAGE_SIZE);
/**
* 生成统一的专业线性 SVG 图标,保持门户卡片视觉一致性。
@@ -486,7 +622,11 @@
* 根据应用链接提取简洁域名,用于卡片底部展示访问来源。
*/
function getHostname(url) {
return new URL(url).hostname;
try {
return new URL(url).hostname || '示例 / Demo';
} catch (e) {
return '示例 / Demo';
}
}
/**
@@ -526,7 +666,7 @@
card.href = app.url;
card.target = '_blank';
card.rel = 'noopener noreferrer';
card.className = 'portal-card group rounded-[1rem] p-3.5 flex min-h-[168px] flex-col animate-fade-in';
card.className = 'portal-card group rounded-[1rem] p-3.5 flex h-[220px] flex-col animate-fade-in';
card.style.cssText = `--accent: ${app.accent}; --accent-2: ${app.accent2}; animation-delay: ${i * 0.08}s;`;
card.innerHTML = `
<div class="relative z-10 flex h-full flex-col">
@@ -553,9 +693,71 @@
return card;
}
apps.forEach((app, i) => {
grid.appendChild(createAppCard(app, i));
});
/**
* 渲染分页轮播,每页最多9个应用卡片。
*/
function renderCarousel() {
grid.innerHTML = '';
for (let p = 0; p < totalPages; p++) {
const pageDiv = document.createElement('div');
pageDiv.className = 'carousel-page';
const start = p * PAGE_SIZE;
const pageApps = apps.slice(start, start + PAGE_SIZE);
pageApps.forEach((app, i) => {
pageDiv.appendChild(createAppCard(app, i));
});
grid.appendChild(pageDiv);
}
renderDots();
updateCarousel();
}
/**
* 渲染分页指示点。
*/
function renderDots() {
const dotsContainer = document.getElementById('carouselDots');
dotsContainer.innerHTML = '';
for (let i = 0; i < totalPages; i++) {
const dot = document.createElement('button');
dot.className = 'carousel-dot' + (i === currentPage ? ' active' : '');
dot.onclick = () => goToPage(i);
dotsContainer.appendChild(dot);
}
}
/**
* 更新轮播位置和按钮状态。
*/
function updateCarousel() {
grid.style.transform = `translateX(-${currentPage * 100}%)`;
document.getElementById('carouselPrev').disabled = currentPage === 0;
document.getElementById('carouselNext').disabled = currentPage === totalPages - 1;
document.querySelectorAll('.carousel-dot').forEach((d, i) => {
d.classList.toggle('active', i === currentPage);
});
}
/**
* 翻页:direction 为 -1(上一页)或 1(下一页)。
*/
function changePage(direction) {
const next = currentPage + direction;
if (next >= 0 && next < totalPages) {
currentPage = next;
updateCarousel();
}
}
/**
* 跳转到指定页。
*/
function goToPage(page) {
currentPage = page;
updateCarousel();
}
renderCarousel();
/**
* 帮助弹窗的测试账号数据,中英双语展示。
@@ -619,7 +821,7 @@
},
{
title: '美雇 / MeiGu',
subtitle: '灵活用工平台 / Flexible Staffing Platform',
subtitle: '劳动关系管理 / Labor Relations Management',
url: 'https://www.uumeigu.com/',
type: 'text',
content: '用户可注册测试 / Users can self-register for testing'
@@ -637,6 +839,53 @@
url: 'https://gov.opc8ai.com/',
type: 'text',
content: 'Login 页面有测试用户 / Test users available on the login page'
},
{
title: 'MaxCount',
subtitle: '财务记账 / Finance & Accounting',
url: 'http://62.234.107.180:9001/',
type: 'text',
content: 'Login 页面有测试用户 / Test users available on the login page'
},
{
title: '法时时 / FaShiShi',
subtitle: '法律服务平台 / Legal Services',
url: 'http://www.fashishi.cn',
type: 'table',
headers: ['账号 / Account', '密码 / Password'],
rows: [
['13911908280', 'Ruichi888']
]
},
{
title: '技能人才胜任力 / Skills Assessment',
subtitle: '技能胜任力评估 / Competency Assessment',
url: 'https://lianxingtest.feihec.com/',
type: 'table',
headers: ['角色 / Role', '账号 / Account', '密码 / Password'],
rows: [
['手机号 / Phone', '18811111111', '123321'],
['管理员 / Admin', 'admin', '123321']
]
},
{
title: 'K12AI应用中心 / K12 AI Hub',
subtitle: 'K12 AI 教育应用 / K12 AI Education',
url: 'http://k12.all8ai.top/',
type: 'table',
headers: ['端 / Portal', '地址 / URL'],
rows: [
['管理后台 / Admin', 'http://101.96.196.218'],
['家长端 / Parent', 'http://101.96.196.218:3002'],
['互动课堂 / Classroom', 'http://101.96.196.218:3001']
]
},
{
title: '医科大学AI学习中心 / Med AI Center',
subtitle: '医学AI学习平台 / Medical AI Learning',
url: 'http://mc.all8ai.top/',
type: 'text',
content: '登录地址 / Login URL: http://101.96.196.218:4100/login'
}
];
@@ -663,7 +912,7 @@
<span class="text-xs font-bold text-slate-100">${item.title}</span>
<span class="text-[10px] text-slate-500 ml-2">${item.subtitle}</span>
</div>
<a href="${item.url}" target="_blank" rel="noopener noreferrer" class="help-link text-[10px] font-medium">${new URL(item.url).hostname}</a>
<a href="${item.url}" target="_blank" rel="noopener noreferrer" class="help-link text-[10px] font-medium">${getHostname(item.url)}</a>
</div>
<div class="p-2">${bodyHtml}</div>
</div>