页面优化,功能修复

This commit is contained in:
jiangdong.cheng
2026-02-12 13:06:12 +08:00
parent d35c397e8d
commit 67cd9501de
24 changed files with 975 additions and 242 deletions

View File

@@ -83,6 +83,13 @@
box-shadow: inset 2px 0 0 #5f72ff;
}
.tool-side-link.is-active {
border-color: #d4ddf2;
background: #eef3ff;
color: #1f3157;
box-shadow: inset 3px 0 0 #536eff;
}
.tool-side-link i {
color: #687ba2;
text-align: center;
@@ -474,47 +481,51 @@
<aside class="tool-side" aria-label="工具分类侧边栏">
<div class="tool-side-logo"><span class="tool-side-logo-dot">AI</span>AI工具集</div>
<nav class="tool-side-links">
<a class="tool-side-link active" href="#section-hot">
<i class="bi bi-fire"></i>
<span>热门工具</span>
<small>{{ $hotTools->count() }}</small>
</a>
<a class="tool-side-link" href="#section-latest">
<i class="bi bi-clock-history"></i>
<span>最新收录</span>
<small>{{ $latestTools->count() }}</small>
</a>
@foreach($categorySections as $index => $section)
<a class="tool-side-link" href="#section-{{ $section['slug'] }}">
<i class="bi {{ $icons[$index % count($icons)] }}"></i>
<span>{{ $section['name'] }}</span>
<small>{{ $section['count'] }}</small>
@if(($modules['hot_tools']['enabled'] ?? true) === true)
<a class="tool-side-link" href="#section-hot">
<i class="bi bi-fire"></i>
<span>热门工具</span>
<small>{{ $hotTools->count() }}</small>
</a>
@endforeach
@endif
@if(($modules['latest_tools']['enabled'] ?? true) === true)
<a class="tool-side-link" href="#section-latest">
<i class="bi bi-clock-history"></i>
<span>最新收录</span>
<small>{{ $latestTools->count() }}</small>
</a>
@endif
@if(($modules['category_sections']['enabled'] ?? true) === true)
@foreach($categorySections as $index => $section)
<a class="tool-side-link" href="#section-{{ $section['slug'] }}">
<i class="bi {{ $icons[$index % count($icons)] }}"></i>
<span>{{ $section['name'] }}</span>
<small>{{ $section['count'] }}</small>
</a>
@endforeach
@endif
</nav>
</aside>
<section class="tool-main">
<header class="tool-top">
<nav class="channel-tabs" aria-label="顶部频道导航">
<a class="channel-tab active" href="{{ route('tools.index') }}">AI工具集</a>
<a class="channel-tab" href="{{ route('models.index') }}">AI应用集</a>
<a class="channel-tab" href="{{ route('news.index') }}">每日AI资讯</a>
<a class="channel-tab" href="{{ route('guides.index') }}">AI教程资源</a>
</nav>
<div class="tool-status">收录工具 <b>{{ $toolStats['total'] ?? 0 }}</b></div>
</header>
<x-portal.top-nav active="tools" status-label="收录工具" :status-value="$toolStats['total'] ?? 0" wrapper-class="tool-top" />
<nav class="tool-mobile-cats" aria-label="移动分类导航">
<a class="tool-side-link" href="#section-hot"><i class="bi bi-fire"></i><span>热门</span><small>{{ $hotTools->count() }}</small></a>
<a class="tool-side-link" href="#section-latest"><i class="bi bi-clock-history"></i><span>最新</span><small>{{ $latestTools->count() }}</small></a>
@foreach($categorySections as $index => $section)
<a class="tool-side-link" href="#section-{{ $section['slug'] }}">
<i class="bi {{ $icons[$index % count($icons)] }}"></i>
<span>{{ $section['name'] }}</span>
<small>{{ $section['count'] }}</small>
</a>
@endforeach
@if(($modules['hot_tools']['enabled'] ?? true) === true)
<a class="tool-side-link" href="#section-hot"><i class="bi bi-fire"></i><span>热门</span><small>{{ $hotTools->count() }}</small></a>
@endif
@if(($modules['latest_tools']['enabled'] ?? true) === true)
<a class="tool-side-link" href="#section-latest"><i class="bi bi-clock-history"></i><span>最新</span><small>{{ $latestTools->count() }}</small></a>
@endif
@if(($modules['category_sections']['enabled'] ?? true) === true)
@foreach($categorySections as $index => $section)
<a class="tool-side-link" href="#section-{{ $section['slug'] }}">
<i class="bi {{ $icons[$index % count($icons)] }}"></i>
<span>{{ $section['name'] }}</span>
<small>{{ $section['count'] }}</small>
</a>
@endforeach
@endif
</nav>
<section class="tool-hero">
@@ -534,14 +545,19 @@
@endforeach
</nav>
<section class="tool-kpis" aria-label="工具统计">
<article class="tool-kpi"><span>收录工具</span><b>{{ $toolStats['total'] ?? 0 }}</b></article>
<article class="tool-kpi"><span>支持 API</span><b>{{ $toolStats['api'] ?? 0 }}</b></article>
<article class="tool-kpi"><span>免费可用</span><b>{{ $toolStats['free'] ?? 0 }}</b></article>
<article class="tool-kpi"><span>7天更新</span><b>{{ $toolStats['updated_7d'] ?? 0 }}</b></article>
</section>
<x-portal.stat-grid
grid-class="tool-kpis"
item-class="tool-kpi"
:stats="[
['label' => '收录工具', 'value' => $toolStats['total'] ?? 0],
['label' => '支持 API', 'value' => $toolStats['api'] ?? 0],
['label' => '免费可用', 'value' => $toolStats['free'] ?? 0],
['label' => '7天更新', 'value' => $toolStats['updated_7d'] ?? 0],
]"
/>
</section>
@if(($modules['channel_cards']['enabled'] ?? true) === true)
<section class="tool-channel" aria-label="频道入口">
<aside class="tool-channel-mini">
<a href="{{ route('news.index') }}"><i class="bi bi-newspaper"></i>AI资讯</a>
@@ -555,7 +571,9 @@
<a class="tool-channel-card tutorial" href="{{ route('guides.index') }}">热门教程</a>
<a class="tool-channel-card ad" href="{{ route('tools.list') }}">工具列表</a>
</section>
@endif
@if(($modules['promo_banners']['enabled'] ?? true) === true)
<section class="tool-banner-row" aria-label="横幅推荐">
<article class="tool-banner">
<b>超全图片视频模板一键复制</b>
@@ -566,39 +584,29 @@
<small>免费试用</small>
</article>
</section>
@endif
@if(($modules['hot_tools']['enabled'] ?? true) === true)
<section id="section-hot" class="tool-section" aria-label="热门工具">
<header class="tool-section-head">
<h2><i class="bi bi-fire text-danger"></i> 热门工具</h2>
<a href="{{ route('tools.list', ['tab' => 'recommended']) }}">查看更多</a>
</header>
<div class="tool-grid">
@foreach($hotTools as $tool)
<a class="tool-card" href="{{ route('tools.show', $tool->slug) }}">
<div class="tool-card-name">{{ $tool->name }}</div>
<div class="tool-card-meta">{{ $tool->category?->name ?? '未分类' }} · {{ $tool->pricing_type }}</div>
<span class="tool-card-tag">{{ $tool->has_api ? '支持 API' : '无 API' }}</span>
</a>
@endforeach
</div>
<x-portal.tool-grid :tools="$hotTools" />
</section>
@endif
@if(($modules['latest_tools']['enabled'] ?? true) === true)
<section id="section-latest" class="tool-section" aria-label="最新收录">
<header class="tool-section-head">
<h2><i class="bi bi-clock-history text-primary"></i> 最新收录</h2>
<a href="{{ route('tools.list', ['tab' => 'latest']) }}">查看更多</a>
</header>
<div class="tool-grid">
@foreach($latestTools as $tool)
<a class="tool-card" href="{{ route('tools.show', $tool->slug) }}">
<div class="tool-card-name">{{ $tool->name }}</div>
<div class="tool-card-meta">{{ $tool->category?->name ?? '未分类' }} · {{ $tool->pricing_type }}</div>
<span class="tool-card-tag">{{ $tool->has_api ? '支持 API' : '无 API' }}</span>
</a>
@endforeach
</div>
<x-portal.tool-grid :tools="$latestTools" />
</section>
@endif
@if(($modules['category_sections']['enabled'] ?? true) === true)
@foreach($categorySections as $section)
<section id="section-{{ $section['slug'] }}" class="tool-section" aria-label="{{ $section['name'] }}工具">
<header class="tool-section-head">
@@ -607,22 +615,108 @@
</header>
@if($section['tools']->isNotEmpty())
<div class="tool-grid">
@foreach($section['tools'] as $tool)
<a class="tool-card" href="{{ route('tools.show', $tool->slug) }}">
<div class="tool-card-name">{{ $tool->name }}</div>
<div class="tool-card-meta">{{ $tool->category?->name ?? '未分类' }} · {{ $tool->pricing_type }}</div>
<span class="tool-card-tag">{{ $tool->has_api ? '支持 API' : '无 API' }}</span>
</a>
@endforeach
</div>
<x-portal.tool-grid :tools="$section['tools']" />
@else
<p class="text-muted-soft mb-0">该分类暂未收录工具</p>
@endif
</section>
@endforeach
@endif
</section>
</div>
@endsection
@section('scripts')
<script>
(() => {
const links = Array.from(document.querySelectorAll('.tool-side-link[href^="#section-"]'));
const sections = Array.from(document.querySelectorAll('.tool-section[id^="section-"]'));
if (!links.length || !sections.length || !('IntersectionObserver' in window)) {
return;
}
const linksById = new Map();
links.forEach((link) => {
const id = link.getAttribute('href')?.replace('#', '');
if (id) {
const group = linksById.get(id) || [];
group.push(link);
linksById.set(id, group);
}
});
const setActive = (activeId) => {
links.forEach((link) => {
const id = link.getAttribute('href')?.replace('#', '') || '';
link.classList.toggle('is-active', id === activeId);
});
};
const syncHash = (id) => {
const hash = `#${id}`;
if (window.location.hash !== hash) {
history.replaceState(null, '', hash);
}
};
links.forEach((link) => {
link.addEventListener('click', (event) => {
const id = link.getAttribute('href')?.replace('#', '');
if (!id) {
return;
}
const target = document.getElementById(id);
if (!target) {
return;
}
event.preventDefault();
setActive(id);
syncHash(id);
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
});
const observer = new IntersectionObserver((entries) => {
const visible = entries
.filter((entry) => entry.isIntersecting)
.sort((a, b) => b.intersectionRatio - a.intersectionRatio);
if (!visible.length) {
return;
}
const top = visible[0].target;
if (top instanceof HTMLElement) {
setActive(top.id);
syncHash(top.id);
}
}, {
rootMargin: '-18% 0px -64% 0px',
threshold: [0.2, 0.45, 0.7],
});
sections.forEach((section) => observer.observe(section));
if (window.location.hash) {
const hashId = window.location.hash.replace('#', '');
const target = document.getElementById(hashId);
if (target) {
setActive(hashId);
window.requestAnimationFrame(() => {
target.scrollIntoView({ behavior: 'auto', block: 'start' });
});
} else {
setActive(sections[0].id);
syncHash(sections[0].id);
}
} else {
setActive(sections[0].id);
syncHash(sections[0].id);
}
})();
</script>
@endsection