fix(seo): 修复页面标题公司名重复与品牌名繁简体不一致问题
- 新增 COMPANY_INFO.displayName 属性用于页面标题和SEO元数据 - 统一所有页面 metadata 使用 displayName(简体"睿新致远") - 视觉展示元素保留 shortName(繁体"睿新致遠"配合青柳隷書字体) - 修复关于/联系/团队页面标题中公司名重复出现的问题 - 修复新闻ID从数字改为SEO友好slug - 更新结构化数据使用完整公司名 - 修复ESLint报错:引号转义、组件displayName、any类型替换
This commit is contained in:
@@ -73,7 +73,7 @@ export function AboutClient() {
|
|||||||
transition={{ duration: 0.5 }}
|
transition={{ duration: 0.5 }}
|
||||||
className="p-8 rounded-xl border border-[#E5E5E5]"
|
className="p-8 rounded-xl border border-[#E5E5E5]"
|
||||||
>
|
>
|
||||||
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6">关于 睿新致遠</h2>
|
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6">关于 {COMPANY_INFO.shortName}</h2>
|
||||||
<p className="text-xl font-semibold text-[#1C1C1C] mb-4">智连未来,成长伙伴</p>
|
<p className="text-xl font-semibold text-[#1C1C1C] mb-4">智连未来,成长伙伴</p>
|
||||||
<p className="text-[#595959] mb-6 leading-relaxed">企业需要的,不是一个高高在上的专家,也不是一个做完就跑的卖家,而是一个能坐下来、一起想办法的同行者。</p>
|
<p className="text-[#595959] mb-6 leading-relaxed">企业需要的,不是一个高高在上的专家,也不是一个做完就跑的卖家,而是一个能坐下来、一起想办法的同行者。</p>
|
||||||
|
|
||||||
@@ -86,10 +86,10 @@ export function AboutClient() {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-3">成长伙伴</h3>
|
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-3">成长伙伴</h3>
|
||||||
<p className="text-[#595959] mb-2 leading-relaxed">我们不把"项目交付"当作终点。</p>
|
<p className="text-[#595959] mb-2 leading-relaxed">我们不把“项目交付”当作终点。</p>
|
||||||
<p className="text-[#595959] mb-2 leading-relaxed">您的业务增长了吗?您的团队能力提升了吗?</p>
|
<p className="text-[#595959] mb-2 leading-relaxed">您的业务增长了吗?您的团队能力提升了吗?</p>
|
||||||
<p className="text-[#595959] mb-2 leading-relaxed">您下一次遇到难题时,还会第一个想到我们吗?</p>
|
<p className="text-[#595959] mb-2 leading-relaxed">您下一次遇到难题时,还会第一个想到我们吗?</p>
|
||||||
<p className="text-[#595959] mt-3 leading-relaxed">这些问题,比"项目是否按时交付"更让我们在意。</p>
|
<p className="text-[#595959] mt-3 leading-relaxed">这些问题,比“项目是否按时交付”更让我们在意。</p>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ export function AboutClient() {
|
|||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-3">
|
<li className="flex items-start gap-3">
|
||||||
<span className="text-[#C41E3A] font-bold">✓</span>
|
<span className="text-[#C41E3A] font-bold">✓</span>
|
||||||
<span className="text-[#595959]">不做路过就忘的"一锤子买卖"</span>
|
<span className="text-[#595959]">不做路过就忘的“一锤子买卖”</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p className="text-[#1C1C1C] leading-relaxed font-medium">
|
<p className="text-[#1C1C1C] leading-relaxed font-medium">
|
||||||
|
|||||||
@@ -2,40 +2,42 @@ import { describe, it, expect, jest } from '@jest/globals';
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
|
|
||||||
|
type MotionProps = { children: React.ReactNode; className?: string; [key: string]: unknown };
|
||||||
|
|
||||||
jest.mock('framer-motion', () => ({
|
jest.mock('framer-motion', () => ({
|
||||||
motion: {
|
motion: {
|
||||||
div: ({ children, className, ...props }: any) => (
|
div: ({ children, className, ...props }: MotionProps) => (
|
||||||
<div className={className} {...props}>
|
<div className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
section: ({ children, className, ...props }: any) => (
|
section: ({ children, className, ...props }: MotionProps) => (
|
||||||
<section className={className} {...props}>
|
<section className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</section>
|
</section>
|
||||||
),
|
),
|
||||||
span: ({ children, className, ...props }: any) => (
|
span: ({ children, className, ...props }: MotionProps) => (
|
||||||
<span className={className} {...props}>
|
<span className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
h1: ({ children, className, ...props }: any) => (
|
h1: ({ children, className, ...props }: MotionProps) => (
|
||||||
<h1 className={className} {...props}>
|
<h1 className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</h1>
|
</h1>
|
||||||
),
|
),
|
||||||
h2: ({ children, className, ...props }: any) => (
|
h2: ({ children, className, ...props }: MotionProps) => (
|
||||||
<h2 className={className} {...props}>
|
<h2 className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</h2>
|
</h2>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
AnimatePresence: ({ children }: any) => <>{children}</>,
|
AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
||||||
useInView: () => [null, true],
|
useInView: () => [null, true],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('next/link', () => {
|
jest.mock('next/link', () => {
|
||||||
const MockLink = ({ children, href, ...props }: any) => (
|
const MockLink = ({ children, href, ...props }: { children: React.ReactNode; href: string; [key: string]: unknown }) => (
|
||||||
<a href={href} {...props}>
|
<a href={href} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
@@ -54,40 +56,48 @@ jest.mock('lucide-react', () => ({
|
|||||||
Phone: () => <span data-testid="phone-icon" />,
|
Phone: () => <span data-testid="phone-icon" />,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@/components/ui/card', () => ({
|
jest.mock('@/components/ui/card', () => {
|
||||||
Card: ({ children, className, ...props }: any) => (
|
const Card = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
|
||||||
<div className={className} {...props}>
|
<div className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
),
|
);
|
||||||
CardContent: ({ children, className, ...props }: any) => (
|
const CardContent = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
|
||||||
<div className={className} {...props}>
|
<div className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
),
|
);
|
||||||
}));
|
Card.displayName = 'Card';
|
||||||
|
CardContent.displayName = 'CardContent';
|
||||||
|
return { Card, CardContent };
|
||||||
|
});
|
||||||
|
|
||||||
jest.mock('@/components/ui/page-header', () => ({
|
jest.mock('@/components/ui/page-header', () => {
|
||||||
PageHeader: ({ title, description }: any) => (
|
const PageHeader = ({ title, description }: { title: string; description?: string }) => (
|
||||||
<header>
|
<header>
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
<p>{description}</p>
|
<p>{description}</p>
|
||||||
</header>
|
</header>
|
||||||
),
|
);
|
||||||
}));
|
PageHeader.displayName = 'PageHeader';
|
||||||
|
return { PageHeader };
|
||||||
|
});
|
||||||
|
|
||||||
jest.mock('@/components/ui/flip-clock', () => ({
|
jest.mock('@/components/ui/flip-clock', () => {
|
||||||
FlipClock: ({ years, months, days }: any) => (
|
const FlipClock = ({ years, months, days }: { years: number; months: number; days: number }) => (
|
||||||
<div data-testid="flip-clock">
|
<div data-testid="flip-clock">
|
||||||
{years}年 {months}月 {days}天
|
{years}年 {months}月 {days}天
|
||||||
</div>
|
</div>
|
||||||
),
|
);
|
||||||
}));
|
FlipClock.displayName = 'FlipClock';
|
||||||
|
return { FlipClock };
|
||||||
|
});
|
||||||
|
|
||||||
jest.mock('@/lib/constants', () => ({
|
jest.mock('@/lib/constants', () => ({
|
||||||
COMPANY_INFO: {
|
COMPANY_INFO: {
|
||||||
name: '四川睿新致远科技有限公司',
|
name: '四川睿新致远科技有限公司',
|
||||||
shortName: '睿新致遠',
|
shortName: '睿新致遠',
|
||||||
|
displayName: '睿新致远',
|
||||||
description: '以智慧连接数字趋势,以伙伴身份陪您成长',
|
description: '以智慧连接数字趋势,以伙伴身份陪您成长',
|
||||||
address: '四川省成都市龙泉驿区',
|
address: '四川省成都市龙泉驿区',
|
||||||
email: 'contact@ruixin.com',
|
email: 'contact@ruixin.com',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { COMPANY_INFO } from '@/lib/constants';
|
|||||||
import { AboutClient } from './client';
|
import { AboutClient } from './client';
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: `关于我们 - ${COMPANY_INFO.name}`,
|
title: `关于我们 - ${COMPANY_INFO.displayName}`,
|
||||||
description: `了解${COMPANY_INFO.name}的品牌故事。我们不只是技术供应商,更是您数字化转型的成长伙伴。以智慧连接数字趋势,以伙伴身份陪您成长。`,
|
description: `了解${COMPANY_INFO.name}的品牌故事。我们不只是技术供应商,更是您数字化转型的成长伙伴。以智慧连接数字趋势,以伙伴身份陪您成长。`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: '联系我们 - 四川睿新致远科技有限公司',
|
title: `联系我们 - ${COMPANY_INFO.displayName}`,
|
||||||
description: '无论您有任何问题或合作意向,我们都很乐意与您交流',
|
description: '无论您有任何问题或合作意向,我们都很乐意与您交流',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { NEWS } from '@/lib/constants';
|
import { NEWS, COMPANY_INFO } from '@/lib/constants';
|
||||||
import { NewsDetailClient } from './NewsDetailClient';
|
import { NewsDetailClient } from './NewsDetailClient';
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
@@ -19,7 +19,7 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: `${news.title} - 睿新致远`,
|
title: `${news.title} - ${COMPANY_INFO.displayName}`,
|
||||||
description: news.excerpt,
|
description: news.excerpt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Metadata } from 'next';
|
|||||||
import { COMPANY_INFO } from '@/lib/constants';
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: `新闻动态 - ${COMPANY_INFO.shortName}`,
|
title: `新闻动态 - ${COMPANY_INFO.displayName}`,
|
||||||
description: `了解${COMPANY_INFO.shortName}最新动态,把握行业发展脉搏。`,
|
description: `了解${COMPANY_INFO.displayName}最新动态,把握行业发展脉搏。`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function NewsLayout({ children }: { children: React.ReactNode }) {
|
export default function NewsLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useState, useMemo, ChangeEvent } from 'react';
|
import { useState, useMemo, ChangeEvent } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { NEWS } from '@/lib/constants';
|
import { NEWS, COMPANY_INFO } from '@/lib/constants';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -65,7 +65,7 @@ export default function NewsListPage() {
|
|||||||
新闻动态
|
新闻动态
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-lg text-[#595959] leading-relaxed">
|
<p className="text-lg text-[#595959] leading-relaxed">
|
||||||
了解睿新致远最新动态,把握行业发展脉搏
|
了解{COMPANY_INFO.displayName}最新动态,把握行业发展脉搏
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { StaticLink } from '@/components/ui/static-link';
|
import { StaticLink } from '@/components/ui/static-link';
|
||||||
import { PRODUCTS } from '@/lib/constants';
|
import { PRODUCTS, COMPANY_INFO } from '@/lib/constants';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { PageNav } from '@/components/layout/page-nav';
|
import { PageNav } from '@/components/layout/page-nav';
|
||||||
import { CheckCircle2, Zap, Target, Layers, ArrowRight } from 'lucide-react';
|
import { CheckCircle2, Zap, Target, Layers, ArrowRight } from 'lucide-react';
|
||||||
@@ -20,7 +20,7 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: `${product.title} - 睿新致远`,
|
title: `${product.title} - ${COMPANY_INFO.displayName}`,
|
||||||
description: product.description,
|
description: product.description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Metadata } from 'next';
|
|||||||
import { COMPANY_INFO } from '@/lib/constants';
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: `产品 - ${COMPANY_INFO.shortName}`,
|
title: `产品 - ${COMPANY_INFO.displayName}`,
|
||||||
description: `自主研发的企业级产品,助力企业高效运营,实现数字化转型。${COMPANY_INFO.shortName}提供ERP、CRM、BI、CMS等产品。`,
|
description: `自主研发的企业级产品,助力企业高效运营,实现数字化转型。${COMPANY_INFO.displayName}提供ERP、CRM、BI、CMS等产品。`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ProductsLayout({ children }: { children: React.ReactNode }) {
|
export default function ProductsLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { SERVICES } from '@/lib/constants';
|
import { SERVICES, COMPANY_INFO } from '@/lib/constants';
|
||||||
import { ServiceDetailClient } from './client';
|
import { ServiceDetailClient } from './client';
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
@@ -20,7 +20,7 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: `${service.title} - 睿新致远`,
|
title: `${service.title} - ${COMPANY_INFO.displayName}`,
|
||||||
description: service.description,
|
description: service.description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Metadata } from 'next';
|
|||||||
import { COMPANY_INFO } from '@/lib/constants';
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: `服务 - ${COMPANY_INFO.shortName}`,
|
title: `服务 - ${COMPANY_INFO.displayName}`,
|
||||||
description: `专业技术团队,为您提供全方位的数字化解决方案。${COMPANY_INFO.shortName}涵盖软件开发、数据分析、咨询服务等核心业务。`,
|
description: `专业技术团队,为您提供全方位的数字化解决方案。${COMPANY_INFO.displayName}涵盖软件开发、数据分析、咨询服务等核心业务。`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ServicesLayout({ children }: { children: React.ReactNode }) {
|
export default function ServicesLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Metadata } from 'next';
|
|||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { SOLUTIONS } from '@/lib/constants/solutions';
|
import { SOLUTIONS } from '@/lib/constants/solutions';
|
||||||
import { PRODUCTS } from '@/lib/constants/products';
|
import { PRODUCTS } from '@/lib/constants/products';
|
||||||
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
import { SolutionDetailClient } from './client';
|
import { SolutionDetailClient } from './client';
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
@@ -19,7 +20,7 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: `${solution.title} - 睿新致远`,
|
title: `${solution.title} - ${COMPANY_INFO.displayName}`,
|
||||||
description: solution.description,
|
description: solution.description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: '解决方案 - 睿新致远',
|
title: `解决方案 - ${COMPANY_INFO.displayName}`,
|
||||||
description: '三种角色,一种身份——您的成长伙伴',
|
description: '三种角色,一种身份——您的成长伙伴',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const modules = [
|
|||||||
values: ['行业趋势洞察报告', '数字化转型成熟度评估', '个性化实施路径规划'],
|
values: ['行业趋势洞察报告', '数字化转型成熟度评估', '个性化实施路径规划'],
|
||||||
cta: '预约一次免费诊断',
|
cta: '预约一次免费诊断',
|
||||||
ctaVariant: 'default' as const,
|
ctaVariant: 'default' as const,
|
||||||
|
ctaHref: '/contact',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: Cpu,
|
icon: Cpu,
|
||||||
@@ -33,6 +34,7 @@ const modules = [
|
|||||||
values: ['业务场景深度调研', '技术方案定制开发', '敏捷交付快速迭代'],
|
values: ['业务场景深度调研', '技术方案定制开发', '敏捷交付快速迭代'],
|
||||||
cta: '了解技术方案',
|
cta: '了解技术方案',
|
||||||
ctaVariant: 'outline' as const,
|
ctaVariant: 'outline' as const,
|
||||||
|
ctaHref: '/products',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: Users,
|
icon: Users,
|
||||||
@@ -46,6 +48,7 @@ const modules = [
|
|||||||
values: ['专属客户成功经理', '季度业务复盘会', '7×24小时响应通道'],
|
values: ['专属客户成功经理', '季度业务复盘会', '7×24小时响应通道'],
|
||||||
cta: '了解陪跑服务',
|
cta: '了解陪跑服务',
|
||||||
ctaVariant: 'default' as const,
|
ctaVariant: 'default' as const,
|
||||||
|
ctaHref: '/services',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -127,7 +130,7 @@ export default function SolutionsPage() {
|
|||||||
className={module.ctaVariant === 'default' ? 'bg-[#C41E3A] hover:bg-[#A01830] text-white' : 'border-[#C41E3A] text-[#C41E3A] hover:bg-[#C41E3A] hover:text-white'}
|
className={module.ctaVariant === 'default' ? 'bg-[#C41E3A] hover:bg-[#A01830] text-white' : 'border-[#C41E3A] text-[#C41E3A] hover:bg-[#C41E3A] hover:text-white'}
|
||||||
asChild
|
asChild
|
||||||
>
|
>
|
||||||
<StaticLink href="/contact">
|
<StaticLink href={module.ctaHref}>
|
||||||
{module.cta}
|
{module.cta}
|
||||||
<ArrowRight className="ml-2 w-4 h-4" />
|
<ArrowRight className="ml-2 w-4 h-4" />
|
||||||
</StaticLink>
|
</StaticLink>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { COMPANY_INFO } from '@/lib/constants';
|
|||||||
import { TeamClient } from './client';
|
import { TeamClient } from './client';
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: `核心团队 - ${COMPANY_INFO.name}`,
|
title: `核心团队 - ${COMPANY_INFO.displayName}`,
|
||||||
description: `了解${COMPANY_INFO.name}的核心团队。我们的团队成员拥有丰富的行业经验和技术专长,致力于为客户提供专业的数字化转型服务。`,
|
description: `了解${COMPANY_INFO.name}的核心团队。我们的团队成员拥有丰富的行业经验和技术专长,致力于为客户提供专业的数字化转型服务。`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { StaticLink } from '@/components/ui/static-link';
|
import { StaticLink } from '@/components/ui/static-link';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Home, ArrowLeft, Search } from 'lucide-react';
|
import { Home, ArrowLeft, Search } from 'lucide-react';
|
||||||
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
@@ -62,7 +63,7 @@ export default function NotFound() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<div className="font-semibold text-[#1C1C1C]">关于我们</div>
|
<div className="font-semibold text-[#1C1C1C]">关于我们</div>
|
||||||
<div className="text-sm text-[#5C5C5C]">了解睿新致远</div>
|
<div className="text-sm text-[#5C5C5C]">了解{COMPANY_INFO.displayName}</div>
|
||||||
</div>
|
</div>
|
||||||
</StaticLink>
|
</StaticLink>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: '隐私政策 - 睿新致远',
|
title: `隐私政策 - ${COMPANY_INFO.displayName}`,
|
||||||
description: '四川睿新致远科技有限公司隐私政策',
|
description: `${COMPANY_INFO.name}隐私政策`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PrivacyPolicyPage() {
|
export default function PrivacyPolicyPage() {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: '服务条款 - 睿新致远',
|
title: `服务条款 - ${COMPANY_INFO.displayName}`,
|
||||||
description: '四川睿新致远科技有限公司服务条款',
|
description: `${COMPANY_INFO.name}服务条款`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function TermsOfServicePage() {
|
export default function TermsOfServicePage() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
updateConsentDetailed,
|
updateConsentDetailed,
|
||||||
trackButtonClick,
|
trackButtonClick,
|
||||||
@@ -13,19 +13,37 @@ import { motion, AnimatePresence } from 'framer-motion';
|
|||||||
|
|
||||||
const LEGACY_CONSENT_KEY = 'ga_consent';
|
const LEGACY_CONSENT_KEY = 'ga_consent';
|
||||||
|
|
||||||
|
let hasConsentBeenHandled = false;
|
||||||
|
|
||||||
|
function getInitialShowConsent(): boolean {
|
||||||
|
if (typeof window === 'undefined') {return false;}
|
||||||
|
if (hasConsentBeenHandled) {return false;}
|
||||||
|
const stored = getStoredPreferences();
|
||||||
|
if (stored) {return false;}
|
||||||
|
const legacyConsent = localStorage.getItem(LEGACY_CONSENT_KEY);
|
||||||
|
if (legacyConsent) {return false;}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
export function CookieConsent() {
|
export function CookieConsent() {
|
||||||
const [showConsent, setShowConsent] = useState(false);
|
const [showConsent, setShowConsent] = useState(getInitialShowConsent);
|
||||||
const [showSettings, setShowSettings] = useState(false);
|
const [showSettings, setShowSettings] = useState(false);
|
||||||
const [isAnimating, setIsAnimating] = useState(false);
|
const [isAnimating, setIsAnimating] = useState(false);
|
||||||
const [preferences, setPreferences] = useState<CookiePreferences>(getDefaultPreferences());
|
const [preferences, setPreferences] = useState<CookiePreferences>(getDefaultPreferences());
|
||||||
|
const consentCheckedRef = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (consentCheckedRef.current) {return;}
|
||||||
|
consentCheckedRef.current = true;
|
||||||
|
|
||||||
const stored = getStoredPreferences();
|
const stored = getStoredPreferences();
|
||||||
if (stored) {
|
if (stored) {
|
||||||
|
hasConsentBeenHandled = true;
|
||||||
updateConsentDetailed(stored);
|
updateConsentDetailed(stored);
|
||||||
} else {
|
} else {
|
||||||
const legacyConsent = localStorage.getItem(LEGACY_CONSENT_KEY);
|
const legacyConsent = localStorage.getItem(LEGACY_CONSENT_KEY);
|
||||||
if (legacyConsent) {
|
if (legacyConsent) {
|
||||||
|
hasConsentBeenHandled = true;
|
||||||
const migratedPrefs: CookiePreferences = {
|
const migratedPrefs: CookiePreferences = {
|
||||||
necessary: true,
|
necessary: true,
|
||||||
analytics: legacyConsent === 'granted',
|
analytics: legacyConsent === 'granted',
|
||||||
@@ -45,8 +63,18 @@ export function CookieConsent() {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleOpenSettings = () => {
|
||||||
|
setShowSettings(true);
|
||||||
|
setShowConsent(true);
|
||||||
|
};
|
||||||
|
window.addEventListener('open-cookie-settings', handleOpenSettings);
|
||||||
|
return () => window.removeEventListener('open-cookie-settings', handleOpenSettings);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleSavePreferences = useCallback((prefs: CookiePreferences) => {
|
const handleSavePreferences = useCallback((prefs: CookiePreferences) => {
|
||||||
setIsAnimating(true);
|
setIsAnimating(true);
|
||||||
|
hasConsentBeenHandled = true;
|
||||||
const finalPrefs = { ...prefs, timestamp: Date.now() };
|
const finalPrefs = { ...prefs, timestamp: Date.now() };
|
||||||
storePreferences(finalPrefs);
|
storePreferences(finalPrefs);
|
||||||
updateConsentDetailed(finalPrefs);
|
updateConsentDetailed(finalPrefs);
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ jest.mock('lucide-react', () => ({
|
|||||||
jest.mock('@/lib/constants', () => ({
|
jest.mock('@/lib/constants', () => ({
|
||||||
COMPANY_INFO: {
|
COMPANY_INFO: {
|
||||||
name: '四川睿新致远科技有限公司',
|
name: '四川睿新致远科技有限公司',
|
||||||
|
shortName: '睿新致遠',
|
||||||
|
displayName: '睿新致远',
|
||||||
description: '以智慧连接数字趋势,以伙伴身份陪您成长',
|
description: '以智慧连接数字趋势,以伙伴身份陪您成长',
|
||||||
email: 'contact@novalon.cn',
|
email: 'contact@novalon.cn',
|
||||||
phone: '028-88888888',
|
phone: '028-88888888',
|
||||||
|
|||||||
@@ -15,28 +15,32 @@ jest.mock('next/navigation', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('next/link', () => {
|
jest.mock('next/link', () => {
|
||||||
return ({ children, href, onClick, ...props }: any) => (
|
const MockLink = ({ children, href, onClick, ...props }: { children: React.ReactNode; href: string; onClick?: () => void; [key: string]: unknown }) => (
|
||||||
<a href={href} onClick={onClick} {...props}>
|
<a href={href} onClick={onClick} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
MockLink.displayName = 'MockLink';
|
||||||
|
return MockLink;
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mock('next/image', () => {
|
jest.mock('next/image', () => {
|
||||||
return ({ src, alt, width, height, className, ...props }: any) => (
|
const MockImage = ({ src, alt, width, height, className, ...props }: { src: string; alt: string; width: number; height: number; className?: string; [key: string]: unknown }) => (
|
||||||
<img src={src} alt={alt} width={width} height={height} className={className} {...props} />
|
<img src={src} alt={alt} width={width} height={height} className={className} {...props} />
|
||||||
);
|
);
|
||||||
|
MockImage.displayName = 'MockImage';
|
||||||
|
return MockImage;
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mock('framer-motion', () => ({
|
jest.mock('framer-motion', () => ({
|
||||||
motion: {
|
motion: {
|
||||||
div: ({ children, className, ...props }: any) => (
|
div: ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
|
||||||
<div className={className} {...props}>
|
<div className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
AnimatePresence: ({ children }: any) => <>{children}</>,
|
AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('lucide-react', () => ({
|
jest.mock('lucide-react', () => ({
|
||||||
@@ -44,18 +48,21 @@ jest.mock('lucide-react', () => ({
|
|||||||
X: () => <span data-testid="x-icon" />,
|
X: () => <span data-testid="x-icon" />,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@/components/ui/button', () => ({
|
jest.mock('@/components/ui/button', () => {
|
||||||
Button: ({ children, className, asChild, ...props }: any) => (
|
const MockButton = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
|
||||||
<button className={className} {...props}>
|
<button className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
),
|
);
|
||||||
}));
|
MockButton.displayName = 'MockButton';
|
||||||
|
return { Button: MockButton };
|
||||||
|
});
|
||||||
|
|
||||||
jest.mock('@/lib/constants', () => ({
|
jest.mock('@/lib/constants', () => ({
|
||||||
COMPANY_INFO: {
|
COMPANY_INFO: {
|
||||||
name: '四川睿新致远科技有限公司',
|
name: '四川睿新致远科技有限公司',
|
||||||
shortName: '睿新致遠',
|
shortName: '睿新致遠',
|
||||||
|
displayName: '睿新致远',
|
||||||
},
|
},
|
||||||
NAVIGATION: [
|
NAVIGATION: [
|
||||||
{ id: 'home', label: '首页', href: '/' },
|
{ id: 'home', label: '首页', href: '/' },
|
||||||
|
|||||||
@@ -108,7 +108,11 @@ function HeaderContent() {
|
|||||||
label={item.label}
|
label={item.label}
|
||||||
items={MEGA_DROPDOWN_DATA[item.dropdownKey!] ?? []}
|
items={MEGA_DROPDOWN_DATA[item.dropdownKey!] ?? []}
|
||||||
isOpen={openDropdown === item.id}
|
isOpen={openDropdown === item.id}
|
||||||
onToggle={() => setOpenDropdown(openDropdown === item.id ? null : item.id)}
|
onToggle={() => {
|
||||||
|
setOpenDropdown((prev) => prev === item.id ? null : item.id);
|
||||||
|
}}
|
||||||
|
onOpen={() => setOpenDropdown(item.id)}
|
||||||
|
onClose={() => setOpenDropdown((prev) => prev === item.id ? null : prev)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<StaticLink
|
<StaticLink
|
||||||
@@ -224,7 +228,7 @@ function HeaderContent() {
|
|||||||
size="lg"
|
size="lg"
|
||||||
>
|
>
|
||||||
<StaticLink href="/contact" onClick={() => setIsOpen(false)}>
|
<StaticLink href="/contact" onClick={() => setIsOpen(false)}>
|
||||||
联系我们
|
立即咨询
|
||||||
</StaticLink>
|
</StaticLink>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,10 +11,15 @@ interface MegaDropdownProps {
|
|||||||
items: MegaDropdownItem[];
|
items: MegaDropdownItem[];
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onToggle: () => void;
|
onToggle: () => void;
|
||||||
|
onOpen?: () => void;
|
||||||
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MegaDropdown({ label, items, isOpen, onToggle }: MegaDropdownProps) {
|
const HOVER_DELAY = 150;
|
||||||
|
|
||||||
|
export function MegaDropdown({ label, items, isOpen, onToggle, onOpen, onClose }: MegaDropdownProps) {
|
||||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
const hoverTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleClickOutside(event: MouseEvent) {
|
function handleClickOutside(event: MouseEvent) {
|
||||||
@@ -26,8 +31,42 @@ export function MegaDropdown({ label, items, isOpen, onToggle }: MegaDropdownPro
|
|||||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||||
}, [isOpen, onToggle]);
|
}, [isOpen, onToggle]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (hoverTimeoutRef.current) {
|
||||||
|
clearTimeout(hoverTimeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleMouseEnter = () => {
|
||||||
|
if (hoverTimeoutRef.current) {
|
||||||
|
clearTimeout(hoverTimeoutRef.current);
|
||||||
|
hoverTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
if (!isOpen) {
|
||||||
|
if (onOpen) {
|
||||||
|
onOpen();
|
||||||
|
} else {
|
||||||
|
onToggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseLeave = () => {
|
||||||
|
hoverTimeoutRef.current = setTimeout(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
if (onClose) {
|
||||||
|
onClose();
|
||||||
|
} else {
|
||||||
|
onToggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, HOVER_DELAY);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={dropdownRef} className="relative">
|
<div ref={dropdownRef} className="relative" onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
|
||||||
<button
|
<button
|
||||||
onClick={onToggle}
|
onClick={onToggle}
|
||||||
className={`
|
className={`
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { motion } from 'framer-motion';
|
|||||||
import { StaticLink } from '@/components/ui/static-link';
|
import { StaticLink } from '@/components/ui/static-link';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { ArrowRight } from 'lucide-react';
|
import { ArrowRight } from 'lucide-react';
|
||||||
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
interface CTASectionProps {
|
interface CTASectionProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -16,7 +17,7 @@ interface CTASectionProps {
|
|||||||
|
|
||||||
export function CTASection({
|
export function CTASection({
|
||||||
title = '开启您的数字化转型之旅',
|
title = '开启您的数字化转型之旅',
|
||||||
description = '与睿新致遠一起,让技术成为您业务增长的核心引擎',
|
description = `与${COMPANY_INFO.shortName}一起,让技术成为您业务增长的核心引擎`,
|
||||||
primaryLabel = '立即咨询',
|
primaryLabel = '立即咨询',
|
||||||
primaryHref = '/contact',
|
primaryHref = '/contact',
|
||||||
secondaryLabel = '了解方案',
|
secondaryLabel = '了解方案',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { OrganizationSchema, WebsiteSchema } from './structured-data';
|
import { OrganizationSchema, WebsiteSchema } from './structured-data';
|
||||||
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
describe('StructuredData', () => {
|
describe('StructuredData', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -18,7 +19,7 @@ describe('StructuredData', () => {
|
|||||||
const { container } = render(<OrganizationSchema />);
|
const { container } = render(<OrganizationSchema />);
|
||||||
const script = container.querySelector('script[type="application/ld+json"]');
|
const script = container.querySelector('script[type="application/ld+json"]');
|
||||||
const schema = JSON.parse(script?.textContent || '{}');
|
const schema = JSON.parse(script?.textContent || '{}');
|
||||||
expect(schema.name).toBe('四川睿新致远科技有限公司');
|
expect(schema.name).toBe(COMPANY_INFO.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain organization type', () => {
|
it('should contain organization type', () => {
|
||||||
@@ -54,7 +55,7 @@ describe('StructuredData', () => {
|
|||||||
const { container } = render(<WebsiteSchema />);
|
const { container } = render(<WebsiteSchema />);
|
||||||
const script = container.querySelector('script[type="application/ld+json"]');
|
const script = container.querySelector('script[type="application/ld+json"]');
|
||||||
const schema = JSON.parse(script?.textContent || '{}');
|
const schema = JSON.parse(script?.textContent || '{}');
|
||||||
expect(schema.name).toBe('四川睿新致远科技有限公司');
|
expect(schema.name).toBe(COMPANY_INFO.name);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import { COMPANY_INFO } from '@/lib/constants';
|
||||||
|
|
||||||
export function OrganizationSchema() {
|
export function OrganizationSchema() {
|
||||||
const schema = {
|
const schema = {
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "Organization",
|
"@type": "Organization",
|
||||||
"name": "四川睿新致远科技有限公司",
|
"name": COMPANY_INFO.name,
|
||||||
"alternateName": "诺瓦隆",
|
"alternateName": "诺瓦隆",
|
||||||
"url": "https://www.novalon.cn",
|
"url": "https://www.novalon.cn",
|
||||||
"logo": "https://www.novalon.cn/logo.svg",
|
"logo": "https://www.novalon.cn/logo.svg",
|
||||||
@@ -32,7 +34,7 @@ export function WebsiteSchema() {
|
|||||||
const schema = {
|
const schema = {
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "WebSite",
|
"@type": "WebSite",
|
||||||
"name": "四川睿新致远科技有限公司",
|
"name": COMPANY_INFO.name,
|
||||||
"url": "https://www.novalon.cn",
|
"url": "https://www.novalon.cn",
|
||||||
"potentialAction": {
|
"potentialAction": {
|
||||||
"@type": "SearchAction",
|
"@type": "SearchAction",
|
||||||
@@ -56,7 +58,7 @@ export function ServiceSchema() {
|
|||||||
"serviceType": "企业数字化转型服务",
|
"serviceType": "企业数字化转型服务",
|
||||||
"provider": {
|
"provider": {
|
||||||
"@type": "Organization",
|
"@type": "Organization",
|
||||||
"name": "四川睿新致远科技有限公司"
|
"name": COMPANY_INFO.name
|
||||||
},
|
},
|
||||||
"description": "提供软件开发、云计算、数据分析、信息安全等一站式数字化转型解决方案",
|
"description": "提供软件开发、云计算、数据分析、信息安全等一站式数字化转型解决方案",
|
||||||
"areaServed": {
|
"areaServed": {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useRef, useState, useCallback, type ReactNode } from 'react';
|
import { useRef, useState, useCallback, type ReactNode } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
import { StaticLink } from '@/components/ui/static-link';
|
||||||
|
|
||||||
interface InkGlowCardProps {
|
interface InkGlowCardProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -33,7 +34,7 @@ export function InkGlowCard({
|
|||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
const handleMouseMove = useCallback((e: React.MouseEvent) => {
|
const handleMouseMove = useCallback((e: React.MouseEvent) => {
|
||||||
if (!cardRef.current) return;
|
if (!cardRef.current) {return;}
|
||||||
const rect = cardRef.current.getBoundingClientRect();
|
const rect = cardRef.current.getBoundingClientRect();
|
||||||
setMousePos({
|
setMousePos({
|
||||||
x: e.clientX - rect.left,
|
x: e.clientX - rect.left,
|
||||||
@@ -72,7 +73,7 @@ export function InkGlowCard({
|
|||||||
} as React.CSSProperties}
|
} as React.CSSProperties}
|
||||||
>
|
>
|
||||||
{href ? (
|
{href ? (
|
||||||
<a
|
<StaticLink
|
||||||
href={href}
|
href={href}
|
||||||
className="relative block rounded-2xl bg-white overflow-hidden transition-all duration-500"
|
className="relative block rounded-2xl bg-white overflow-hidden transition-all duration-500"
|
||||||
style={{
|
style={{
|
||||||
@@ -86,7 +87,7 @@ export function InkGlowCard({
|
|||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</a>
|
</StaticLink>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="relative rounded-2xl bg-white overflow-hidden transition-all duration-500"
|
className="relative rounded-2xl bg-white overflow-hidden transition-all duration-500"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export const COMPANY_INFO = {
|
export const COMPANY_INFO = {
|
||||||
name: '四川睿新致远科技有限公司',
|
name: '四川睿新致远科技有限公司',
|
||||||
shortName: '睿新致遠',
|
shortName: '睿新致遠',
|
||||||
|
displayName: '睿新致远',
|
||||||
slogan: '智连未来,成长伙伴',
|
slogan: '智连未来,成长伙伴',
|
||||||
description: '以智慧连接数字趋势,以伙伴身份陪您成长——您的数字化转型同行者',
|
description: '以智慧连接数字趋势,以伙伴身份陪您成长——您的数字化转型同行者',
|
||||||
founded: '2026',
|
founded: '2026',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export interface NewsItem {
|
|||||||
|
|
||||||
export const NEWS: NewsItem[] = [
|
export const NEWS: NewsItem[] = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: 'company-founded',
|
||||||
title: '四川睿新致远科技有限公司正式成立',
|
title: '四川睿新致远科技有限公司正式成立',
|
||||||
excerpt: '2026年1月15日,四川睿新致远科技有限公司在成都龙泉驿区正式成立,标志着公司在科技创新领域迈出了坚实的第一步。',
|
excerpt: '2026年1月15日,四川睿新致远科技有限公司在成都龙泉驿区正式成立,标志着公司在科技创新领域迈出了坚实的第一步。',
|
||||||
date: '2026-01-15',
|
date: '2026-01-15',
|
||||||
@@ -27,7 +27,7 @@ export const NEWS: NewsItem[] = [
|
|||||||
公司正积极拓展业务合作,业务范围涵盖软件开发、云服务、数据分析、信息安全等多个领域。`,
|
公司正积极拓展业务合作,业务范围涵盖软件开发、云服务、数据分析、信息安全等多个领域。`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: 'digital-transformation-solution',
|
||||||
title: '公司推出企业数字化转型解决方案',
|
title: '公司推出企业数字化转型解决方案',
|
||||||
excerpt: '针对中小企业数字化转型需求,公司推出一站式数字化转型解决方案,帮助企业快速实现数字化升级。',
|
excerpt: '针对中小企业数字化转型需求,公司推出一站式数字化转型解决方案,帮助企业快速实现数字化升级。',
|
||||||
date: '2026-02-20',
|
date: '2026-02-20',
|
||||||
|
|||||||
Reference in New Issue
Block a user