页面优化,功能修复

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

View File

@@ -10,12 +10,20 @@
<div class="position-relative">
<span class="hero-chip"><i class="bi bi-grid-1x2-fill"></i> 工具列表</span>
<h1 class="h2 fw-bold mt-2">AI工具独立列表页</h1>
<p class="hero-subtext">从工具集首页点击“更多”进入,支持完整筛选与分页浏览。</p>
<p class="hero-subtext">从工具集首页点击“查看更多”进入,支持完整筛选与分页浏览。</p>
</div>
</section>
<div class="channel-body">
<section class="channel-panel">
<x-portal.top-nav
active="tool-list"
status-label="收录工具"
:status-value="$toolStats['total'] ?? 0"
wrapper-class="tool-top"
status-class="tool-status"
/>
<form class="channel-search mb-3" method="get" action="{{ route('tools.list') }}" role="search" aria-label="筛选 AI 工具列表">
<input type="hidden" name="tab" value="{{ $activeTab }}">
<input type="search" name="q" value="{{ $filters['q'] ?? '' }}" placeholder="搜索工具名称或能力" autocomplete="off" spellcheck="false">
@@ -72,37 +80,31 @@
</section>
<aside class="channel-list">
<section class="channel-panel">
<header class="channel-panel-head">
<h3 class="channel-panel-title">站点概览</h3>
</header>
<article class="side-list-item">
<a href="{{ route('tools.index') }}">返回工具集首页</a>
<small>支持分类定位导航</small>
</article>
<article class="side-list-item">
<a href="{{ route('models.index') }}">模型推荐</a>
<small>综合评分维度筛选</small>
</article>
<article class="side-list-item">
<a href="{{ route('news.index') }}">资讯文章</a>
<small>追踪行业动态</small>
</article>
<article class="side-list-item">
<a href="{{ route('guides.index') }}">教程学习</a>
<small>入门到实战路径</small>
</article>
</section>
<x-portal.side-list-section
title="站点概览"
:items="[
['title' => '返回工具集首页', 'meta' => '支持分类定位导航', 'url' => route('tools.index')],
['title' => '模型推荐', 'meta' => '综合评分维度筛选', 'url' => route('models.index')],
['title' => '资讯文章', 'meta' => '追踪行业动态', 'url' => route('news.index')],
['title' => '教程学习', 'meta' => '入门到实战路径', 'url' => route('guides.index')],
]"
:item-url="fn ($item) => $item['url']"
:item-title="fn ($item) => $item['title']"
:item-meta="fn ($item) => $item['meta']"
/>
<section class="channel-panel">
<header class="channel-panel-head">
<h3 class="channel-panel-title">数据统计</h3>
</header>
<article class="side-list-item"><a href="#">收录工具</a><small>{{ $toolStats['total'] ?? 0 }}</small></article>
<article class="side-list-item"><a href="#">支持 API</a><small>{{ $toolStats['api'] ?? 0 }}</small></article>
<article class="side-list-item"><a href="#">免费可用</a><small>{{ $toolStats['free'] ?? 0 }}</small></article>
<article class="side-list-item"><a href="#">7天更新</a><small>{{ $toolStats['updated_7d'] ?? 0 }}</small></article>
</section>
<x-portal.side-list-section
title="数据统计"
:items="[
['title' => '收录工具', 'meta' => (string) ($toolStats['total'] ?? 0)],
['title' => '支持 API', 'meta' => (string) ($toolStats['api'] ?? 0)],
['title' => '免费可用', 'meta' => (string) ($toolStats['free'] ?? 0)],
['title' => '7天更新', 'meta' => (string) ($toolStats['updated_7d'] ?? 0)],
]"
:item-url="fn () => '#'"
:item-title="fn ($item) => $item['title']"
:item-meta="fn ($item) => $item['meta']"
/>
</aside>
</div>
@endsection