feat: 添加移动端适配和测试功能

refactor(layout): 优化页脚布局和备案信息展示
feat(constants): 添加ICP备案和公安备案信息
feat(header): 实现移动端加载时的骨架屏效果
style(globals): 调整文字颜色和添加移动端响应样式
feat(breadcrumb): 增加返回按钮和响应式优化
feat(e2e): 添加移动端测试工具和测试用例
docs: 添加页脚重设计文档
This commit is contained in:
张翔
2026-03-05 11:40:21 +08:00
parent 834fb3bc3b
commit 6797c24b5c
15 changed files with 2320 additions and 10 deletions
+17 -6
View File
@@ -1,7 +1,7 @@
'use client';
import Link from 'next/link';
import { ChevronRight, Home } from 'lucide-react';
import { ChevronRight, Home, ArrowLeft } from 'lucide-react';
interface BreadcrumbItem {
label: string;
@@ -10,20 +10,31 @@ interface BreadcrumbItem {
interface BreadcrumbProps {
items: BreadcrumbItem[];
showBackButton?: boolean;
onBackClick?: () => void;
}
export function Breadcrumb({ items }: BreadcrumbProps) {
export function Breadcrumb({ items, showBackButton = false, onBackClick }: BreadcrumbProps) {
return (
<nav aria-label="breadcrumb" className="flex items-center space-x-2 text-sm text-[#5C5C5C] py-4">
<Link href="/" className="flex items-center hover:text-[#C41E3A] transition-colors">
<nav aria-label="breadcrumb" className="flex items-center space-x-2 text-sm text-[#4A4A4A] py-4 px-4 md:px-0">
{showBackButton && (
<button
onClick={onBackClick}
className="flex items-center mr-2 hover:text-[#C41E3A] transition-colors focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-offset-2"
aria-label="返回"
>
<ArrowLeft className="w-5 h-5" />
</button>
)}
<Link href="/" className="flex items-center hover:text-[#C41E3A] transition-colors focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-offset-2 rounded" aria-label="首页">
<Home className="w-4 h-4" />
</Link>
{items.map((item, index) => (
<div key={index} className="flex items-center">
<ChevronRight className="w-4 h-4 text-[#E5E5E5]" />
<ChevronRight className="w-4 h-4 text-[#E5E5E5] flex-shrink-0" aria-hidden="true" />
<Link
href={item.href}
className="ml-2 hover:text-[#C41E3A] transition-colors"
className="ml-2 hover:text-[#C41E3A] transition-colors focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-offset-2 rounded truncate max-w-[120px] md:max-w-none"
>
{item.label}
</Link>
+36
View File
@@ -22,6 +22,20 @@ export function Footer() {
<p className="text-[#5C5C5C] text-sm leading-relaxed mb-6">
{COMPANY_INFO.description}
</p>
<div className="mt-6 pt-6 border-t border-[#E5E5E5]">
<p className="text-sm text-[#5C5C5C] mb-3 font-medium"></p>
<div className="inline-block bg-white p-3 rounded-lg border border-[#E5E5E5] shadow-sm hover:shadow-md transition-shadow duration-200">
<Image
src="/images/qrcode_for_gh_a297181ff548_258.jpg"
alt="微信公众号二维码"
width={120}
height={120}
className="w-[120px] h-[120px]"
loading="lazy"
/>
</div>
<p className="text-xs text-[#718096] mt-2"></p>
</div>
</div>
<div>
@@ -99,6 +113,28 @@ export function Footer() {
</Link>
</div>
</div>
<div className="text-center mt-4 pt-4 border-t border-[#E5E5E5]">
<div className="flex flex-col sm:flex-row justify-center items-center gap-2 sm:gap-4 text-xs text-[#718096]">
<a
href="https://beian.miit.gov.cn/"
target="_blank"
rel="noopener noreferrer"
className="hover:text-[#C41E3A] transition-colors"
>
{COMPANY_INFO.icp}
</a>
<span className="hidden sm:inline">|</span>
<a
href="http://www.beian.gov.cn/"
target="_blank"
rel="noopener noreferrer"
className="hover:text-[#C41E3A] transition-colors"
>
{COMPANY_INFO.police}
</a>
</div>
</div>
</div>
</div>
</footer>
+19 -1
View File
@@ -290,9 +290,27 @@ function HeaderContent() {
);
}
function HeaderFallback() {
return (
<header className="fixed top-0 left-0 right-0 z-50 bg-transparent">
<div className="container-wide">
<div className="flex items-center justify-between h-16">
<div className="h-8 w-8 bg-gray-200 animate-pulse rounded" />
<nav className="hidden md:flex items-center gap-1">
{[1, 2, 3, 4, 5].map((i) => (
<div key={i} className="h-6 w-16 bg-gray-200 animate-pulse rounded mx-1" />
))}
</nav>
<div className="h-9 w-20 bg-gray-200 animate-pulse rounded" />
</div>
</div>
</header>
);
}
export function Header() {
return (
<Suspense fallback={<div className="h-16" />}>
<Suspense fallback={<HeaderFallback />}>
<HeaderContent />
</Suspense>
);