Browse Source

优化网站底部

master
科技小王子 3 months ago
parent
commit
4d3644599d
  1. 35
      package-lock.json
  2. 11
      package.json
  3. 7
      src/api/cms/cmsWebsite/model/index.ts
  4. 34
      src/api/layout/index.ts
  5. 129
      src/app/page.tsx
  6. 196
      src/components/layout/Footer.tsx
  7. 159
      src/components/layout/Header.tsx
  8. 165
      src/components/layout/Header_bak.tsx

35
package-lock.json

@ -10,7 +10,8 @@
"dependencies": {
"next": "15.3.5",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"zustand": "^5.0.6"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@ -1222,7 +1223,7 @@
"version": "19.1.8",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
"integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
"dev": true,
"devOptional": true,
"dependencies": {
"csstype": "^3.0.2"
}
@ -2234,7 +2235,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true
"devOptional": true
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
@ -5758,6 +5759,34 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zustand": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.6.tgz",
"integrity": "sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A==",
"engines": {
"node": ">=12.20.0"
},
"peerDependencies": {
"@types/react": ">=18.0.0",
"immer": ">=9.0.6",
"react": ">=18.0.0",
"use-sync-external-store": ">=1.2.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
},
"use-sync-external-store": {
"optional": true
}
}
}
}
}

11
package.json

@ -9,19 +9,20 @@
"lint": "next lint"
},
"dependencies": {
"next": "15.3.5",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.3.5"
"zustand": "^5.0.6"
},
"devDependencies": {
"typescript": "^5",
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.3.5",
"@eslint/eslintrc": "^3"
"tailwindcss": "^4",
"typescript": "^5"
}
}

7
src/api/cms/cmsWebsite/model/index.ts

@ -1,5 +1,6 @@
import type { PageParam } from '@/api';
import {CmsWebsiteSetting} from "@/api/cms/cmsWebsiteSetting/model";
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
/**
*
@ -119,6 +120,12 @@ export interface CmsWebsite {
username?: string;
// 网站配置
setting?: CmsWebsiteSetting;
// 导航菜单列表
navigations?: CmsNavigation[];
// 顶部导航菜单
topNavs?: CmsNavigation[];
// 底部导航菜单
bottomNavs?: CmsNavigation[];
}
/**

34
src/api/layout/index.ts

@ -6,6 +6,7 @@ import {MODULES_API_URL, SERVER_API_URL} from '@/config/setting';
import { Company } from '@/api/system/company/model';
import { CmsWebsite } from '@/api/cms/cmsWebsite/model';
import {Menu} from "@/api/system/menu/model";
import { CmsNavigation } from '@/api/cms/cmsNavigation/model';
/**
*
@ -33,6 +34,39 @@ export async function getSiteInfo(): Promise<CmsWebsite> {
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function getNavigations(position?: number): Promise<CmsNavigation[]> {
const res = await request.get<ApiResult<CmsNavigation[]>>(
MODULES_API_URL + '/cms/cms-navigation',
{
params: {
position, // 0不限 1顶部 2底部
status: 0 // 只获取正常状态的菜单
}
}
);
if (res.data.code === 0 && res.data.data) {
return res.data.data as unknown as CmsNavigation[];
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function getBottomNavigations(): Promise<CmsNavigation[]> {
return getNavigations(2); // position: 2 表示底部菜单
}
/**
*
*/
export async function getTopNavigations(): Promise<CmsNavigation[]> {
return getNavigations(1); // position: 1 表示顶部菜单
}
/**
*
*/

129
src/app/page.tsx

@ -1,27 +1,120 @@
// import HeroSection from '@/components/sections/HeroSection';
// import FeaturesSection from '@/components/sections/FeaturesSection';
// import CasesSection from '@/components/sections/CasesSection';
// import PartnersSection from '@/components/sections/PartnersSection';
// import ContactSection from '@/components/sections/ContactSection';
import SiteInfoDisplay from '@/components/sections/SiteInfoDisplay';
import NavigationDisplay from '@/components/sections/NavigationDisplay';
export default function Home() {
return (
<main>
<div className={'bg-gray-200'}>Main</div>
{/* 英雄区域 */}
{/*<HeroSection />*/}
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div className="text-center mb-12">
<h1 className="text-4xl font-bold text-gray-900 mb-4">
Zustand
</h1>
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
使 Zustand
</p>
</div>
{/* 产品服务 */}
{/*<FeaturesSection />*/}
<div className="space-y-12">
{/* 站点信息展示 */}
<div>
<h2 className="text-2xl font-semibold text-gray-900 mb-4">
</h2>
<SiteInfoDisplay />
</div>
{/* 客户案例 */}
{/*<CasesSection />*/}
{/* 导航菜单展示 */}
<div>
<h2 className="text-2xl font-semibold text-gray-900 mb-4">
</h2>
<NavigationDisplay />
</div>
{/* 合作伙伴 */}
{/*<PartnersSection />*/}
{/* 功能说明 */}
<div>
<h2 className="text-2xl font-semibold text-gray-900 mb-4">
</h2>
<div className="bg-white border border-gray-200 p-6 rounded-lg shadow-sm">
<ul className="space-y-4">
<li className="flex items-start">
<div className="flex-shrink-0 w-6 h-6 bg-green-100 rounded-full flex items-center justify-center mt-0.5">
<svg className="w-4 h-4 text-green-600" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-gray-900"></h3>
<p className="text-sm text-gray-600">30</p>
</div>
</li>
{/* 联系我们 */}
{/*<ContactSection />*/}
</main>
<li className="flex items-start">
<div className="flex-shrink-0 w-6 h-6 bg-green-100 rounded-full flex items-center justify-center mt-0.5">
<svg className="w-4 h-4 text-green-600" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-gray-900"></h3>
<p className="text-sm text-gray-600"> sessionStorage </p>
</div>
</li>
<li className="flex items-start">
<div className="flex-shrink-0 w-6 h-6 bg-green-100 rounded-full flex items-center justify-center mt-0.5">
<svg className="w-4 h-4 text-green-600" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-gray-900"></h3>
<p className="text-sm text-gray-600">访</p>
</div>
</li>
<li className="flex items-start">
<div className="flex-shrink-0 w-6 h-6 bg-green-100 rounded-full flex items-center justify-center mt-0.5">
<svg className="w-4 h-4 text-green-600" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-gray-900"></h3>
<p className="text-sm text-gray-600"></p>
</div>
</li>
<li className="flex items-start">
<div className="flex-shrink-0 w-6 h-6 bg-green-100 rounded-full flex items-center justify-center mt-0.5">
<svg className="w-4 h-4 text-green-600" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-gray-900"></h3>
<p className="text-sm text-gray-600"></p>
</div>
</li>
<li className="flex items-start">
<div className="flex-shrink-0 w-6 h-6 bg-green-100 rounded-full flex items-center justify-center mt-0.5">
<svg className="w-4 h-4 text-green-600" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-gray-900">TypeScript </h3>
<p className="text-sm text-gray-600"></p>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
);
}

196
src/components/layout/Footer.tsx

@ -1,39 +1,189 @@
'use client';
import Link from 'next/link';
import Image from 'next/image';
import { useSiteInfoValue } from '@/hooks/useSiteInfo';
import { CmsNavigation } from '@/api/cms/cmsNavigation/model';
const Footer = () => {
const { siteInfo, bottomNavs, isLoaded, isNavsLoaded } = useSiteInfoValue();
const currentYear = new Date().getFullYear();
console.log(bottomNavs,'bottomNavs')
const footerLinks = {
products: [
{ name: '网站建设', href: '/products/website' },
{ name: '移动应用', href: '/products/mobile' },
{ name: '系统开发', href: '/products/system' },
{ name: '电商平台', href: '/products/ecommerce' },
// 按父级分组底部导航
const groupedNavs = bottomNavs.reduce((groups: Record<string, CmsNavigation[]>, nav) => {
if (nav.parentId === 0) {
// 顶级菜单作为分组标题
groups[nav.title || 'default'] = bottomNavs.filter(child => child.parentId === nav.navigationId);
}
return groups;
}, {});
// 如果没有底部导航数据,显示默认的静态菜单
const defaultFooterLinks = {
'产品服务': [
{ title: '网站建设', path: '/products/website' },
{ title: '移动应用', path: '/products/mobile' },
{ title: '系统开发', path: '/products/system' },
{ title: '电商平台', path: '/products/ecommerce' },
],
solutions: [
{ name: '企业数字化', href: '/solutions/digital' },
{ name: '教育信息化', href: '/solutions/education' },
{ name: '智慧城市', href: '/solutions/smart-city' },
{ name: '工业互联网', href: '/solutions/industry' },
'解决方案': [
{ title: '企业数字化', path: '/solutions/digital' },
{ title: '教育信息化', path: '/solutions/education' },
{ title: '智慧城市', path: '/solutions/smart-city' },
{ title: '工业互联网', path: '/solutions/industry' },
],
support: [
{ name: '技术文档', href: '/docs' },
{ name: '开发指南', href: '/guide' },
{ name: 'API参考', href: '/api' },
{ name: '常见问题', href: '/faq' },
'技术支持': [
{ title: '技术文档', path: '/docs' },
{ title: '开发指南', path: '/guide' },
{ title: 'API参考', path: '/api' },
{ title: '常见问题', path: '/faq' },
],
company: [
{ name: '关于我们', href: '/about' },
{ name: '新闻动态', href: '/news' },
{ name: '招聘信息', href: '/careers' },
{ name: '联系我们', href: '/contact' },
'关于我们': [
{ title: '公司介绍', path: '/about' },
{ title: '新闻动态', path: '/news' },
{ title: '招聘信息', path: '/careers' },
{ title: '联系我们', path: '/contact' },
],
};
// 使用接口数据或默认数据
const footerLinks = Object.keys(groupedNavs).length > 0 ? groupedNavs : defaultFooterLinks;
console.log(footerLinks,'footerLinks')
return (
<footer className="bg-gray-900 text-white py-12">
Footer
<footer className="bg-gray-900 text-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{/* 网站信息 */}
<div className="lg:col-span-1">
<div className="flex items-center space-x-2 mb-4">
{siteInfo?.websiteLogo && (
<Image
src={siteInfo.websiteLogo}
alt={siteInfo.websiteName || '网站Logo'}
width={107}
height={24}
className="h-8 w-auto filter brightness-0 invert"
/>
)}
{/*<span className="text-xl font-bold">*/}
{/* {siteInfo?.websiteName || '网站名称'}*/}
{/*</span>*/}
</div>
{siteInfo?.comments && (
<p className="text-gray-400 text-sm mb-4 leading-relaxed">
{siteInfo.comments}
</p>
)}
<div className="space-y-2 text-sm text-gray-400">
{siteInfo?.phone && (
<div className="flex items-center space-x-2">
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" />
</svg>
<span>{siteInfo.phone}</span>
</div>
)}
{siteInfo?.email && (
<div className="flex items-center space-x-2">
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
</svg>
<span>{siteInfo.email}</span>
</div>
)}
{siteInfo?.address && (
<div className="flex items-start space-x-2">
<svg className="w-4 h-4 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clipRule="evenodd" />
</svg>
<span className="leading-relaxed">{siteInfo.address}</span>
</div>
)}
</div>
</div>
{/* 动态底部导航菜单 */}
{Object.entries(footerLinks).map(([groupTitle, links]) => (
<div key={groupTitle}>
<h3 className="text-lg font-semibold mb-4">{groupTitle}</h3>
<ul className="space-y-2">
{links.map((link: any, index: number) => (
<li key={index}>
<Link
href={link.path || link.href || '#'}
className="text-gray-400 hover:text-white transition-colors duration-200 text-sm"
target={link.target === '_blank' ? '_blank' : undefined}
rel={link.target === '_blank' ? 'noopener noreferrer' : undefined}
>
{link.title || link.name}
</Link>
</li>
))}
</ul>
</div>
))}
</div>
{/* 底部版权信息 */}
<div className="border-t border-gray-800 mt-8 pt-8">
<div className="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
<div className="text-sm text-gray-400">
<p>
© {currentYear} {siteInfo?.websiteName || '网站名称'}. .
</p>
{siteInfo?.icpNo && (
<p className="mt-1">
<a
href="https://beian.miit.gov.cn/"
target="_blank"
rel="noopener noreferrer"
className="hover:text-white transition-colors"
>
{siteInfo.icpNo}
</a>
</p>
)}
{siteInfo?.policeNo && (
<p className="mt-1">
<a
href="http://www.beian.gov.cn/"
target="_blank"
rel="noopener noreferrer"
className="hover:text-white transition-colors"
>
{siteInfo.policeNo}
</a>
</p>
)}
</div>
{/* 社交媒体链接 */}
<div className="flex space-x-4">
<a href="#" className="text-gray-400 hover:text-white transition-colors">
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
</svg>
</a>
<a href="#" className="text-gray-400 hover:text-white transition-colors">
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M22.46 6c-.77.35-1.6.58-2.46.69.88-.53 1.56-1.37 1.88-2.38-.83.5-1.75.85-2.72 1.05C18.37 4.5 17.26 4 16 4c-2.35 0-4.27 1.92-4.27 4.29 0 .34.04.67.11.98C8.28 9.09 5.11 7.38 3 4.79c-.37.63-.58 1.37-.58 2.15 0 1.49.75 2.81 1.91 3.56-.71 0-1.37-.2-1.95-.5v.03c0 2.08 1.48 3.82 3.44 4.21a4.22 4.22 0 0 1-1.93.07 4.28 4.28 0 0 0 4 2.98 8.521 8.521 0 0 1-5.33 1.84c-.34 0-.68-.02-1.02-.06C3.44 20.29 5.7 21 8.12 21 16 21 20.33 14.46 20.33 8.79c0-.19 0-.37-.01-.56.84-.6 1.56-1.36 2.14-2.23z"/>
</svg>
</a>
<a href="#" className="text-gray-400 hover:text-white transition-colors">
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
</svg>
</a>
</div>
</div>
</div>
</div>
</footer>
);
};

159
src/components/layout/Header.tsx

@ -1,49 +1,124 @@
'use client';
import { useState, useEffect } from 'react';
import {useState} from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import {getSiteInfo} from "@/api/layout";
import {CmsWebsite} from "@/api/cms/cmsWebsite/model";
import Image from 'next/image';
import {usePathname} from 'next/navigation';
import {useSiteInfo} from '@/hooks/useSiteInfo';
const Header = () => {
const [config, setConfig] = useState<CmsWebsite>({})
const [isMenuOpen, setIsMenuOpen] = useState(false);
const pathname = usePathname();
const navigation = [
{ name: '首页', href: '/' },
{ name: '产品', href: '/products' },
{ name: '解决方案', href: '/solutions' },
{ name: '案例', href: '/cases' },
{ name: '关于', href: '/about' },
{ name: '联系', href: '/contact' },
];
const isActive = (href: string) => {
if (href === '/') {
return pathname === '/';
}
return pathname.startsWith(href);
};
const reload = () => {
getSiteInfo().then(data => {
if(data){
setConfig(data);
}
})
}
useEffect(() => {
reload();
}, []);
return (
<header className="bg-red-100 text-2xl shadow-sm border-b border-gray-100 sticky top-0 z-50">
Top
</header>
);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const pathname = usePathname();
// 使用全局状态管理的站点信息
const {siteInfo, loading, error, refresh} = useSiteInfo({forceRefresh: true});
const navigation = [
{name: '首页', href: '/'},
{name: '产品', href: '/products'},
{name: '解决方案', href: '/solutions'},
{name: '案例', href: '/cases'},
{name: '关于', href: '/about'},
{name: '联系', href: '/contact'},
];
const isActive = (href: string) => {
if (href === '/') {
return pathname === '/';
}
return pathname.startsWith(href);
};
return (
<header className="bg-white shadow-sm border-b border-gray-100 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
{/* Logo 和站点名称 */}
<div className="flex items-center">
<Link href="/" className="flex items-center space-x-2">
{siteInfo?.websiteLogo && (
<Image
src={siteInfo.websiteLogo}
alt={siteInfo.websiteName || '网站Logo'}
width={107}
height={24}
className="h-8 w-auto"
/>
)}
{/*<span className="text-xl font-bold text-gray-900">*/}
{/* {siteInfo?.websiteName || '加载中...'}*/}
{/*</span>*/}
</Link>
</div>
{/* 导航菜单 */}
<nav className="hidden md:flex space-x-8">
{navigation.map((item) => (
<Link
key={item.name}
href={item.href}
className={`px-3 py-2 text-sm font-medium transition-colors ${
isActive(item.href)
? 'text-blue-600 border-b-2 border-blue-600'
: 'text-gray-700 hover:text-blue-600'
}`}
>
{item.name}
</Link>
))}
</nav>
{/* 加载状态和错误处理 */}
<div className="flex items-center space-x-4">
{loading && (
<div className="text-sm text-gray-500">...</div>
)}
{error && (
<button
onClick={refresh}
className="text-sm text-red-600 hover:text-red-800"
title="点击重试"
>
</button>
)}
{/* 移动端菜单按钮 */}
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="md:hidden p-2 rounded-md text-gray-700 hover:text-blue-600"
>
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"/>
</svg>
</button>
</div>
</div>
{/* 移动端菜单 */}
{isMenuOpen && (
<div className="md:hidden">
<div className="px-2 pt-2 pb-3 space-y-1 sm:px-3 border-t border-gray-200">
{navigation.map((item) => (
<Link
key={item.name}
href={item.href}
className={`block px-3 py-2 text-base font-medium transition-colors ${
isActive(item.href)
? 'text-blue-600 bg-blue-50'
: 'text-gray-700 hover:text-blue-600 hover:bg-gray-50'
}`}
onClick={() => setIsMenuOpen(false)}
>
{item.name}
</Link>
))}
</div>
</div>
)}
</div>
</header>
);
};
export default Header;

165
src/components/layout/Header_bak.tsx

@ -1,165 +0,0 @@
'use client';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import {getSiteInfo} from "@/api/layout";
import {CmsWebsite} from "@/api/cms/cmsWebsite/model";
const Header = () => {
const [config, setConfig] = useState<CmsWebsite>({})
const [isMenuOpen, setIsMenuOpen] = useState(false);
const pathname = usePathname();
const navigation = [
{ name: '首页', href: '/' },
{ name: '产品', href: '/products' },
{ name: '解决方案', href: '/solutions' },
{ name: '案例', href: '/cases' },
{ name: '关于', href: '/about' },
{ name: '联系', href: '/contact' },
];
const isActive = (href: string) => {
if (href === '/') {
return pathname === '/';
}
return pathname.startsWith(href);
};
const reload = () => {
getSiteInfo().then(data => {
console.log(data,'data')
if(data){
setConfig(data);
}
})
}
useEffect(() => {
reload();
}, []);
return (
<header className="bg-white shadow-sm border-b border-gray-100 sticky top-0 z-50">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
{/* Logo */}
<div className="flex-shrink-0">
<Link href="/" className="text-xl font-bold text-gray-800">
= {config?.websiteName} =
</Link>
</div>
{/* Desktop Navigation */}
<nav className="hidden md:block">
<div className="flex items-center space-x-8">
{navigation.map((item) => (
<Link
key={item.name}
href={item.href}
className={`text-sm font-medium transition-colors duration-300 ${
isActive(item.href)
? 'text-blue-600'
: 'text-gray-700 hover:text-blue-600'
}`}
>
{item.name}
</Link>
))}
</div>
</nav>
{/* Language Switch & CTA */}
<div className="hidden md:flex items-center space-x-4">
<div className="flex items-center space-x-2 text-sm text-gray-600">
<span className="cursor-pointer hover:text-blue-600"></span>
<span>|</span>
<span className="cursor-pointer hover:text-blue-600">EN</span>
</div>
<Link href="/contact" className="btn btn-primary text-sm">
</Link>
</div>
{/* Mobile menu button */}
<div className="md:hidden">
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500"
aria-expanded="false"
>
<span className="sr-only"></span>
{!isMenuOpen ? (
<svg
className="block h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
) : (
<svg
className="block h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
)}
</button>
</div>
</div>
{/* Mobile Navigation */}
{isMenuOpen && (
<div className="md:hidden">
<div className="px-2 pt-2 pb-3 space-y-1 sm:px-3 bg-white border-t border-gray-200">
{navigation.map((item) => (
<Link
key={item.name}
href={item.href}
className={`block px-3 py-2 rounded-md text-base font-medium transition-colors duration-300 ${
isActive(item.href)
? 'text-blue-600 bg-blue-50'
: 'text-gray-700 hover:text-blue-600 hover:bg-gray-50'
}`}
onClick={() => setIsMenuOpen(false)}
>
{item.name}
</Link>
))}
<div className="px-3 py-2">
<Link
href="/contact"
className="btn btn-primary w-full"
onClick={() => setIsMenuOpen(false)}
>
</Link>
</div>
</div>
</div>
)}
</div>
</header>
);
};
export default Header;
Loading…
Cancel
Save