manual save(2026-01-05 11:17)
This commit is contained in:
@@ -1,10 +1,13 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>react-template</title>
|
<meta name="description" content="诚裕集团 - 集科技研发、金融服务、产业投资于一体的综合性企业集团" />
|
||||||
|
<meta name="keywords" content="诚裕集团,金融服务,科技研发,产业投资,企业咨询" />
|
||||||
|
<meta name="author" content="诚裕集团" />
|
||||||
|
<title>诚裕集团 - 诚信为本 · 裕及四方</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
41
public/favicon.svg
Normal file
41
public/favicon.svg
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<!-- 背景渐变 -->
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#14385d;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#215d9b;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="accentGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#f59e0b;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#d97706;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- 圆角矩形背景 -->
|
||||||
|
<rect width="64" height="64" rx="12" fill="url(#bgGradient)"/>
|
||||||
|
|
||||||
|
<!-- 建筑图标 -->
|
||||||
|
<g transform="translate(16, 14)">
|
||||||
|
<!-- 主建筑 -->
|
||||||
|
<rect x="4" y="12" width="24" height="24" rx="2" fill="white" opacity="0.95"/>
|
||||||
|
|
||||||
|
<!-- 窗户网格 -->
|
||||||
|
<rect x="8" y="16" width="4" height="4" rx="1" fill="url(#bgGradient)"/>
|
||||||
|
<rect x="14" y="16" width="4" height="4" rx="1" fill="url(#bgGradient)"/>
|
||||||
|
<rect x="20" y="16" width="4" height="4" rx="1" fill="url(#bgGradient)"/>
|
||||||
|
|
||||||
|
<rect x="8" y="22" width="4" height="4" rx="1" fill="url(#bgGradient)"/>
|
||||||
|
<rect x="14" y="22" width="4" height="4" rx="1" fill="url(#bgGradient)"/>
|
||||||
|
<rect x="20" y="22" width="4" height="4" rx="1" fill="url(#bgGradient)"/>
|
||||||
|
|
||||||
|
<rect x="8" y="28" width="4" height="4" rx="1" fill="url(#bgGradient)"/>
|
||||||
|
<rect x="14" y="28" width="4" height="4" rx="1" fill="url(#bgGradient)"/>
|
||||||
|
<rect x="20" y="28" width="4" height="4" rx="1" fill="url(#bgGradient)"/>
|
||||||
|
|
||||||
|
<!-- 门 -->
|
||||||
|
<rect x="12" y="32" width="8" height="4" rx="1" fill="url(#accentGradient)"/>
|
||||||
|
|
||||||
|
<!-- 顶部装饰 -->
|
||||||
|
<path d="M 2 12 L 16 2 L 30 12" stroke="url(#accentGradient)" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -41,11 +41,16 @@ export const Header: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* 顶部信息栏 */}
|
{/* 顶部信息栏 - 玻璃效果 */}
|
||||||
<motion.div
|
<motion.div
|
||||||
className={`fixed top-0 left-0 right-0 z-50 bg-gradient-to-r from-primary-dark to-primary text-white text-xs transition-all duration-300 ${
|
className={`fixed top-0 left-0 right-0 z-50 text-white text-xs transition-all duration-300 border-b border-white/10 ${
|
||||||
isScrolled ? 'h-0 opacity-0 overflow-hidden' : 'h-10'
|
isScrolled ? 'h-0 opacity-0 overflow-hidden' : 'h-10'
|
||||||
}`}
|
}`}
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(to right, rgba(20, 56, 93, 0.85), rgba(33, 93, 155, 0.85))',
|
||||||
|
backdropFilter: 'blur(12px) saturate(150%)',
|
||||||
|
WebkitBackdropFilter: 'blur(12px) saturate(150%)',
|
||||||
|
}}
|
||||||
initial={{ y: -40 }}
|
initial={{ y: -40 }}
|
||||||
animate={{ y: 0 }}
|
animate={{ y: 0 }}
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
@@ -77,12 +82,23 @@ export const Header: React.FC = () => {
|
|||||||
<motion.header
|
<motion.header
|
||||||
className={`fixed left-0 right-0 z-50 transition-all duration-300 ${
|
className={`fixed left-0 right-0 z-50 transition-all duration-300 ${
|
||||||
isScrolled
|
isScrolled
|
||||||
? 'top-0 bg-white/95 backdrop-blur-md shadow-lg'
|
? 'top-0 bg-white/80 backdrop-blur-xl backdrop-saturate-150 shadow-lg border-b border-white/20'
|
||||||
: 'top-10 bg-white shadow-md'
|
: 'top-10 bg-white/60 backdrop-blur-lg backdrop-saturate-120 shadow-md border-b border-white/30'
|
||||||
}`}
|
}`}
|
||||||
initial={{ y: -100 }}
|
initial={{ y: -100 }}
|
||||||
animate={{ y: 0 }}
|
animate={{ y: 0 }}
|
||||||
transition={{ duration: 0.3, delay: 0.1 }}
|
transition={{ duration: 0.3, delay: 0.1 }}
|
||||||
|
style={{
|
||||||
|
background: isScrolled
|
||||||
|
? 'rgba(255, 255, 255, 0.8)'
|
||||||
|
: 'rgba(255, 255, 255, 0.6)',
|
||||||
|
backdropFilter: isScrolled
|
||||||
|
? 'blur(20px) saturate(150%)'
|
||||||
|
: 'blur(16px) saturate(120%)',
|
||||||
|
WebkitBackdropFilter: isScrolled
|
||||||
|
? 'blur(20px) saturate(150%)'
|
||||||
|
: 'blur(16px) saturate(120%)',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div className="flex items-center justify-between h-[80px]">
|
<div className="flex items-center justify-between h-[80px]">
|
||||||
@@ -133,15 +149,6 @@ export const Header: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<span className="relative z-10">{item.label}</span>
|
<span className="relative z-10">{item.label}</span>
|
||||||
|
|
||||||
{/* 活动状态指示器 - 渐变下划线 */}
|
|
||||||
{isActive(item.path) && (
|
|
||||||
<motion.div
|
|
||||||
layoutId="activeIndicator"
|
|
||||||
className="absolute bottom-1 left-1/2 -translate-x-1/2 w-8 h-1 bg-gradient-to-r from-primary to-accent rounded-full"
|
|
||||||
transition={{ type: 'spring', stiffness: 400, damping: 30 }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 悬停背景效果 */}
|
{/* 悬停背景效果 */}
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute inset-0 rounded-xl bg-gradient-to-r from-primary/10 to-accent/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
className="absolute inset-0 rounded-xl bg-gradient-to-r from-primary/10 to-accent/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||||
@@ -214,7 +221,7 @@ export const Header: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 移动端菜单 - 增强版 */}
|
{/* 移动端菜单 - 玻璃效果版 */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{isMobileMenuOpen && (
|
{isMobileMenuOpen && (
|
||||||
<motion.nav
|
<motion.nav
|
||||||
@@ -222,7 +229,12 @@ export const Header: React.FC = () => {
|
|||||||
animate={{ opacity: 1, height: 'auto' }}
|
animate={{ opacity: 1, height: 'auto' }}
|
||||||
exit={{ opacity: 0, height: 0 }}
|
exit={{ opacity: 0, height: 0 }}
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
className="lg:hidden bg-gradient-to-b from-white to-gray-50 border-t border-gray-100 overflow-hidden shadow-xl"
|
className="lg:hidden overflow-hidden shadow-xl border-b border-white/20"
|
||||||
|
style={{
|
||||||
|
background: 'rgba(255, 255, 255, 0.85)',
|
||||||
|
backdropFilter: 'blur(20px) saturate(150%)',
|
||||||
|
WebkitBackdropFilter: 'blur(20px) saturate(150%)',
|
||||||
|
}}
|
||||||
role="navigation"
|
role="navigation"
|
||||||
aria-label="移动端导航"
|
aria-label="移动端导航"
|
||||||
>
|
>
|
||||||
@@ -238,19 +250,13 @@ export const Header: React.FC = () => {
|
|||||||
to={item.path}
|
to={item.path}
|
||||||
className={`block px-5 py-3.5 rounded-xl text-base font-medium transition-all duration-200 ${
|
className={`block px-5 py-3.5 rounded-xl text-base font-medium transition-all duration-200 ${
|
||||||
isActive(item.path)
|
isActive(item.path)
|
||||||
? 'bg-gradient-to-r from-primary/10 to-accent/10 text-primary shadow-sm'
|
? 'bg-gradient-to-r from-primary/15 to-accent/15 text-primary shadow-sm backdrop-blur-sm'
|
||||||
: 'text-gray-700 hover:bg-primary/5 active:scale-95'
|
: 'text-gray-700 hover:bg-white/30 active:scale-95 backdrop-blur-sm'
|
||||||
}`}
|
}`}
|
||||||
aria-current={isActive(item.path) ? 'page' : undefined}
|
aria-current={isActive(item.path) ? 'page' : undefined}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span>{item.label}</span>
|
<span>{item.label}</span>
|
||||||
{isActive(item.path) && (
|
|
||||||
<motion.div
|
|
||||||
className="w-2 h-2 rounded-full bg-gradient-to-r from-primary to-accent"
|
|
||||||
layoutId="mobileActiveIndicator"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -258,21 +264,21 @@ export const Header: React.FC = () => {
|
|||||||
|
|
||||||
{/* 移动端联系方式 */}
|
{/* 移动端联系方式 */}
|
||||||
<motion.div
|
<motion.div
|
||||||
className="pt-4 mt-2 border-t border-gray-200 space-y-3"
|
className="pt-4 mt-2 border-t border-white/20 space-y-3"
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ delay: 0.3 }}
|
transition={{ delay: 0.3 }}
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="tel:400-123-4567"
|
href="tel:400-123-4567"
|
||||||
className="flex items-center gap-3 px-5 py-3 rounded-xl text-gray-700 hover:bg-primary/5 transition-all"
|
className="flex items-center gap-3 px-5 py-3 rounded-xl text-gray-700 hover:bg-white/30 transition-all backdrop-blur-sm"
|
||||||
>
|
>
|
||||||
<Phone size={18} className="text-primary" />
|
<Phone size={18} className="text-primary" />
|
||||||
<span className="text-sm font-medium">400-123-4567</span>
|
<span className="text-sm font-medium">400-123-4567</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<motion.button
|
<motion.button
|
||||||
className="w-full px-5 py-3.5 text-base font-medium text-white rounded-xl bg-gradient-to-r from-primary to-primary-light shadow-lg shadow-primary/30"
|
className="w-full px-5 py-3.5 text-base font-medium text-white rounded-xl bg-gradient-to-r from-primary to-primary-light shadow-lg shadow-primary/30 backdrop-blur-sm"
|
||||||
whileTap={{ scale: 0.98 }}
|
whileTap={{ scale: 0.98 }}
|
||||||
>
|
>
|
||||||
立即咨询
|
立即咨询
|
||||||
|
|||||||
18
src/hooks/usePageTitle.ts
Normal file
18
src/hooks/usePageTitle.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义 Hook - 设置页面标题
|
||||||
|
* @param title 页面标题
|
||||||
|
* @param suffix 标题后缀,默认为 "诚裕集团"
|
||||||
|
*/
|
||||||
|
export const usePageTitle = (title: string, suffix: string = '诚裕集团') => {
|
||||||
|
useEffect(() => {
|
||||||
|
const prevTitle = document.title;
|
||||||
|
document.title = title ? `${title} - ${suffix}` : suffix;
|
||||||
|
|
||||||
|
// 清理函数:组件卸载时恢复默认标题
|
||||||
|
return () => {
|
||||||
|
document.title = prevTitle;
|
||||||
|
};
|
||||||
|
}, [title, suffix]);
|
||||||
|
};
|
||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import { Header } from '../components/Header';
|
import { Header } from '../components/Header';
|
||||||
import { Footer } from '../components/Footer';
|
import { Footer } from '../components/Footer';
|
||||||
import { COMPANY_INFO } from '../lib/constants';
|
import { COMPANY_INFO } from '../lib/constants';
|
||||||
|
import { usePageTitle } from '../hooks/usePageTitle';
|
||||||
|
|
||||||
// 发展历程数据
|
// 发展历程数据
|
||||||
const milestones = [
|
const milestones = [
|
||||||
@@ -127,6 +128,7 @@ const honors = [
|
|||||||
*/
|
*/
|
||||||
export const About: React.FC = () => {
|
export const About: React.FC = () => {
|
||||||
const timelineRef = useRef<HTMLDivElement>(null);
|
const timelineRef = useRef<HTMLDivElement>(null);
|
||||||
|
usePageTitle('关于我们');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Categories } from '../clientsdk/sdk.gen'
|
|||||||
import { createClient } from '../clientsdk/client'
|
import { createClient } from '../clientsdk/client'
|
||||||
import { customQuerySerializer } from '../clientsdk/querySerializer'
|
import { customQuerySerializer } from '../clientsdk/querySerializer'
|
||||||
import { TENANT_SLUG, TENANT_API_KEY, API_URL } from '../config'
|
import { TENANT_SLUG, TENANT_API_KEY, API_URL } from '../config'
|
||||||
|
import { usePageTitle } from '../hooks/usePageTitle'
|
||||||
|
|
||||||
const client = createClient({
|
const client = createClient({
|
||||||
baseUrl: API_URL,
|
baseUrl: API_URL,
|
||||||
@@ -16,6 +17,7 @@ const client = createClient({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export const CategoriesPage: React.FC = () => {
|
export const CategoriesPage: React.FC = () => {
|
||||||
|
usePageTitle('文章分类')
|
||||||
const [categories, setCategories] = useState<any[]>([])
|
const [categories, setCategories] = useState<any[]>([])
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Posts, Categories } from '../clientsdk/sdk.gen'
|
|||||||
import { createClient } from '../clientsdk/client'
|
import { createClient } from '../clientsdk/client'
|
||||||
import { customQuerySerializer } from '../clientsdk/querySerializer'
|
import { customQuerySerializer } from '../clientsdk/querySerializer'
|
||||||
import { TENANT_SLUG, TENANT_API_KEY, API_URL } from '../config'
|
import { TENANT_SLUG, TENANT_API_KEY, API_URL } from '../config'
|
||||||
|
import { usePageTitle } from '../hooks/usePageTitle'
|
||||||
|
|
||||||
const client = createClient({
|
const client = createClient({
|
||||||
baseUrl: API_URL,
|
baseUrl: API_URL,
|
||||||
@@ -20,6 +21,7 @@ const client = createClient({
|
|||||||
|
|
||||||
export const CategoryDetail: React.FC = () => {
|
export const CategoryDetail: React.FC = () => {
|
||||||
const { slug } = useParams<{ slug: string }>()
|
const { slug } = useParams<{ slug: string }>()
|
||||||
|
usePageTitle('分类详情')
|
||||||
const [posts, setPosts] = useState<any[]>([])
|
const [posts, setPosts] = useState<any[]>([])
|
||||||
const [category, setCategory] = useState<any>(null)
|
const [category, setCategory] = useState<any>(null)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
import { Header } from '../components/Header';
|
import { Header } from '../components/Header';
|
||||||
import { Footer } from '../components/Footer';
|
import { Footer } from '../components/Footer';
|
||||||
import { COMPANY_INFO, CONTACT_INFO } from '../lib/constants';
|
import { COMPANY_INFO, CONTACT_INFO } from '../lib/constants';
|
||||||
|
import { usePageTitle } from '../hooks/usePageTitle';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contact 组件 - 联系我们页面
|
* Contact 组件 - 联系我们页面
|
||||||
@@ -76,6 +77,7 @@ interface FormErrors {
|
|||||||
* Contact 组件
|
* Contact 组件
|
||||||
*/
|
*/
|
||||||
export const Contact: React.FC = () => {
|
export const Contact: React.FC = () => {
|
||||||
|
usePageTitle('联系我们');
|
||||||
const [formData, setFormData] = useState<ContactFormData>({
|
const [formData, setFormData] = useState<ContactFormData>({
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ import { Hero } from '../components/Hero';
|
|||||||
import { AboutSection } from '../components/Home/AboutSection';
|
import { AboutSection } from '../components/Home/AboutSection';
|
||||||
import { ServicesSection } from '../components/Home/ServicesSection';
|
import { ServicesSection } from '../components/Home/ServicesSection';
|
||||||
import { NewsSection } from '../components/Home/NewsSection';
|
import { NewsSection } from '../components/Home/NewsSection';
|
||||||
|
import { usePageTitle } from '../hooks/usePageTitle';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Home 组件 - 企业官网首页
|
* Home 组件 - 企业官网首页
|
||||||
*/
|
*/
|
||||||
export const Home: React.FC = () => {
|
export const Home: React.FC = () => {
|
||||||
|
usePageTitle('首页');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
className="min-h-screen bg-background"
|
className="min-h-screen bg-background"
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { Header } from '../components/Header';
|
|||||||
import { Footer } from '../components/Footer';
|
import { Footer } from '../components/Footer';
|
||||||
import { formatDate } from '../lib/utils';
|
import { formatDate } from '../lib/utils';
|
||||||
import { NEWS_CATEGORIES } from '../lib/constants';
|
import { NEWS_CATEGORIES } from '../lib/constants';
|
||||||
|
import { usePageTitle } from '../hooks/usePageTitle';
|
||||||
|
|
||||||
// 模拟新闻数据
|
// 模拟新闻数据
|
||||||
const allNews = [
|
const allNews = [
|
||||||
@@ -453,6 +454,7 @@ const DigitalHumanVideo: React.FC = () => {
|
|||||||
* News 组件 - 新闻资讯页面
|
* News 组件 - 新闻资讯页面
|
||||||
*/
|
*/
|
||||||
export const News: React.FC = () => {
|
export const News: React.FC = () => {
|
||||||
|
usePageTitle('新闻资讯');
|
||||||
const [activeCategory, setActiveCategory] = useState('all');
|
const [activeCategory, setActiveCategory] = useState('all');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Posts } from '../clientsdk/sdk.gen'
|
|||||||
import { createClient } from '../clientsdk/client'
|
import { createClient } from '../clientsdk/client'
|
||||||
import { customQuerySerializer } from '../clientsdk/querySerializer'
|
import { customQuerySerializer } from '../clientsdk/querySerializer'
|
||||||
import { TENANT_SLUG, TENANT_API_KEY, API_URL } from '../config'
|
import { TENANT_SLUG, TENANT_API_KEY, API_URL } from '../config'
|
||||||
|
import { usePageTitle } from '../hooks/usePageTitle'
|
||||||
|
|
||||||
const client = createClient({
|
const client = createClient({
|
||||||
baseUrl: API_URL,
|
baseUrl: API_URL,
|
||||||
@@ -18,6 +19,7 @@ const client = createClient({
|
|||||||
|
|
||||||
export const PostDetail: React.FC = () => {
|
export const PostDetail: React.FC = () => {
|
||||||
const { slug } = useParams<{ slug: string }>()
|
const { slug } = useParams<{ slug: string }>()
|
||||||
|
usePageTitle('文章详情')
|
||||||
const [post, setPost] = useState<any>(null)
|
const [post, setPost] = useState<any>(null)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
import { Header } from '../components/Header';
|
import { Header } from '../components/Header';
|
||||||
import { Footer } from '../components/Footer';
|
import { Footer } from '../components/Footer';
|
||||||
import { SERVICES } from '../lib/constants';
|
import { SERVICES } from '../lib/constants';
|
||||||
|
import { usePageTitle } from '../hooks/usePageTitle';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Services 组件 - 产品服务页面
|
* Services 组件 - 产品服务页面
|
||||||
@@ -195,6 +196,7 @@ const CaseCard: React.FC<{ case: typeof cases[0]; index: number }> = ({ case: ca
|
|||||||
* Services 组件
|
* Services 组件
|
||||||
*/
|
*/
|
||||||
export const Services: React.FC = () => {
|
export const Services: React.FC = () => {
|
||||||
|
usePageTitle('产品服务');
|
||||||
const [activeCategory, setActiveCategory] = useState('all');
|
const [activeCategory, setActiveCategory] = useState('all');
|
||||||
|
|
||||||
const filteredCases = activeCategory === 'all'
|
const filteredCases = activeCategory === 'all'
|
||||||
|
|||||||
Reference in New Issue
Block a user