From a272e58aaaf55dedd7e11756fc1a3c196e161108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Sat, 2 May 2026 08:11:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80=E5=85=A8=E7=AB=99?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E9=A3=8E=E6=A0=BC=E3=80=81=E5=AF=BC=E8=88=AA?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E4=B8=8E=E6=96=87=E6=A1=88=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E8=87=AA=E6=B4=BD=E6=80=A7=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 InkGlowCard 墨韵流光卡片组件,统一全站卡片交互风格 - 新增 PageNav 面包屑组件,统一全站页面导航 - 统一色彩体系、排版层级、间距节奏和动画风格 - 修复 CTA 区品牌名称错误(诺瓦隆→睿新致遠) - 修复 ERP 产品卖点与年费制定价矛盾 - 导航下拉补充 SDS 和 OA 产品 - 统一全站数据指标为 12+年核心团队经验、6自研产品、10+团队成员 - 移除不可靠的 100%客户满意度和 30+行业专家指标 - 修复新闻时间线不合理问题,调整里程碑节奏 - 统一响应承诺为工作日快速响应 - 服务第4项重命名为行业方案实施,厘清概念 - 服务详情页效果数据改为定性描述 - 删除 cases 模块,精简代码库 --- .impeccable.md | 416 ++++-------------- package.json | 2 + public/logo-light.svg | 74 ++++ src/app/(marketing)/about/client.tsx | 412 ++++++++--------- .../(marketing)/cases/[id]/client.test.tsx | 209 --------- src/app/(marketing)/cases/[id]/client.tsx | 286 ------------ src/app/(marketing)/cases/[id]/page.tsx | 52 --- src/app/(marketing)/cases/layout.tsx | 14 - src/app/(marketing)/cases/page.tsx | 255 ----------- src/app/(marketing)/contact/page.tsx | 204 +++++---- src/app/(marketing)/home-content-v2.tsx | 43 -- src/app/(marketing)/layout.tsx | 20 - .../news/[slug]/NewsDetailClient.tsx | 167 ++++--- src/app/(marketing)/news/layout.tsx | 11 + src/app/(marketing)/news/page.tsx | 236 +++++----- src/app/(marketing)/page.tsx | 8 +- src/app/(marketing)/products/[id]/page.tsx | 4 +- src/app/(marketing)/products/layout.tsx | 11 + src/app/(marketing)/products/page.tsx | 4 +- src/app/(marketing)/services/[id]/client.tsx | 58 +-- src/app/(marketing)/services/layout.tsx | 11 + src/app/(marketing)/services/page.tsx | 4 +- src/app/(marketing)/solutions/[id]/client.tsx | 133 ++++++ src/app/(marketing)/solutions/[id]/page.tsx | 38 ++ src/app/(marketing)/solutions/page.tsx | 12 +- src/app/(marketing)/team/client.tsx | 168 +++---- src/app/error.tsx | 2 +- src/app/globals.css | 50 +++ src/app/not-found.tsx | 10 +- src/components/layout/footer.test.tsx | 5 +- src/components/layout/footer.tsx | 21 +- src/components/layout/header.test.tsx | 6 +- src/components/layout/header.tsx | 113 +---- src/components/layout/mega-dropdown.tsx | 17 +- src/components/layout/mobile-tab-bar.tsx | 47 +- src/components/layout/page-nav.tsx | 43 ++ src/components/sections/about-section.tsx | 126 +++--- src/components/sections/cases-section.tsx | 98 ----- src/components/sections/challenge-section.tsx | 16 +- src/components/sections/contact-section.tsx | 67 +-- src/components/sections/cta-section.tsx | 12 +- src/components/sections/hero-section-v2.tsx | 27 +- .../sections/home-solutions-section.tsx | 58 +-- .../sections/methodology-section.tsx | 115 ++--- src/components/sections/news-section.tsx | 102 ++--- .../sections/product-matrix-section.tsx | 10 +- src/components/sections/services-section.tsx | 77 ++-- .../sections/social-proof-section.tsx | 95 ++-- src/components/sections/team-section.tsx | 94 ++-- .../sections/testimonial-section.tsx | 14 +- src/components/ui/challenge-card.tsx | 95 ++-- src/components/ui/ink-glow-card.tsx | 111 +++++ src/components/ui/insight-card.tsx | 38 +- src/components/ui/page-header.tsx | 31 +- src/components/ui/product-card.tsx | 76 +++- src/components/ui/testimonial-block.tsx | 39 +- src/lib/constants.ts | 1 - src/lib/constants/cases.ts | 198 --------- src/lib/constants/index.ts | 4 +- src/lib/constants/navigation.ts | 4 +- src/lib/constants/news.ts | 8 +- src/lib/constants/products.ts | 210 ++++++--- src/lib/constants/services.ts | 4 +- src/lib/constants/stats.ts | 33 +- 64 files changed, 1934 insertions(+), 2995 deletions(-) create mode 100644 public/logo-light.svg delete mode 100644 src/app/(marketing)/cases/[id]/client.test.tsx delete mode 100644 src/app/(marketing)/cases/[id]/client.tsx delete mode 100644 src/app/(marketing)/cases/[id]/page.tsx delete mode 100644 src/app/(marketing)/cases/layout.tsx delete mode 100644 src/app/(marketing)/cases/page.tsx create mode 100644 src/app/(marketing)/news/layout.tsx create mode 100644 src/app/(marketing)/products/layout.tsx create mode 100644 src/app/(marketing)/services/layout.tsx create mode 100644 src/app/(marketing)/solutions/[id]/client.tsx create mode 100644 src/app/(marketing)/solutions/[id]/page.tsx create mode 100644 src/components/layout/page-nav.tsx delete mode 100644 src/components/sections/cases-section.tsx create mode 100644 src/components/ui/ink-glow-card.tsx delete mode 100644 src/lib/constants/cases.ts diff --git a/.impeccable.md b/.impeccable.md index 89ef546..21e5484 100644 --- a/.impeccable.md +++ b/.impeccable.md @@ -1,341 +1,75 @@ -# Novalon Website - 设计上下文 - -> 本文档定义了 Novalon Website 项目的设计原则、品牌定位和视觉规范,确保所有设计决策的一致性和连贯性。 - ---- - -## 设计上下文 - -### 用户画像 - -**主要用户群体:大型企业(500人以上)** - -**用户特征**: -- 企业决策者:CEO、CTO、CIO等高管层 -- 技术负责人:IT总监、技术架构师、项目经理 -- 采购决策者:采购总监、业务部门负责人 - -**决策场景**: -- 数字化转型战略规划阶段 -- 寻找可靠的技术合作伙伴 -- 评估供应商的专业能力和项目经验 -- 关注系统稳定性、安全性、可扩展性 -- 需要详细的技术方案和合规认证 - -**核心需求**: -- 信任感:需要看到专业能力和成功案例 -- 安全感:需要了解技术实力和安全保障 -- 确定性:需要清晰的服务流程和交付标准 -- 创新性:需要前沿的技术视野和解决方案 - ---- - -### 品牌个性 - -**品牌定位**:企业数字化转型服务商 - -**核心口号**:"智连未来,成长伙伴" - -**品牌个性关键词**: -1. **专业** - 展现深厚的技术积累和行业经验 -2. **可靠** - 传递稳定、可信、值得依赖的品牌形象 -3. **创新** - 体现前沿技术视野和持续创新能力 - -**品牌价值观**: -- 不是高高在上的"专家",而是并肩作战的"伙伴" -- 不是做完就跑的"卖家",而是长期陪伴的"同行者" -- 只做一件事:成为客户数字化转型路上信得过的成长伙伴 - -**情感目标**: -- 让用户感受到:专业、可信、有温度 -- 建立信任感:通过案例、数据、流程展示 -- 传递安全感:通过技术实力、安全保障、合规认证 -- 激发信心:通过创新方案、前沿视野、持续进化 - ---- - -### 视觉方向 - -**设计理念**:融合中国传统水墨画元素与现代科技感 - -**核心视觉元素**: - -#### 1. 色彩系统 - -**主色调 - 墨黑系(水墨画主色)**: -```css ---color-primary: #1C1C1C; /* 主色 */ ---color-primary-hover: #0A0A0A; /* 悬停色 */ ---color-primary-light: #3D3D3D; /* 浅色 */ ---color-primary-lighter: #F5F5F5; /* 更浅色 */ -``` - -**品牌色 - 朱砂红(印章红)**: -```css ---color-brand-primary: #C41E3A; /* 品牌主色 */ ---color-brand-primary-hover: #A01830; /* 品牌悬停色 */ ---color-brand-primary-light: #E04A68; /* 品牌浅色 */ ---color-brand-primary-bg: #FEF2F4; /* 品牌背景色 */ -``` - -**背景色系 - 宣纸白**: -```css ---color-bg-primary: #FFFFFF; /* 主背景 */ ---color-bg-secondary: #FFFBF5; /* 次背景(宣纸色) */ ---color-bg-tertiary: #F5F5F5; /* 三级背景 */ ---color-bg-hover: #EFEFEF; /* 悬停背景 */ -``` - -**文字色系 - 墨色层次**: -```css ---color-text-primary: #1C1C1C; /* 主文字 */ ---color-text-secondary: #3D3D3D; /* 次文字 */ ---color-text-tertiary: #4A4A4A; /* 三级文字 */ ---color-text-muted: #6B6B6B; /* 弱化文字 */ -``` - -#### 2. 字体系统 - -**中文字体**: -- **书法字体**:Aoyagi Reisho(青柳凉笙)- 用于品牌名称、标题装饰 -- **正文字体**:Noto Sans SC - 用于正文、UI元素 - -**英文字体**: -- **无衬线字体**:Geist Sans - 用于英文标题、正文 -- **等宽字体**:Geist Mono - 用于代码、技术内容 - -**字体应用原则**: -- 品牌名称"睿新致遠"使用书法字体,传递文化底蕴 -- 正文使用现代无衬线字体,确保可读性 -- 技术内容使用等宽字体,体现专业性 - -#### 3. 视觉特效 - -**水墨元素**: -- 水墨滴装饰(InkDrop) -- 水墨飞溅效果(InkSplash) -- 水墨背景(InkBackground) - -**科技元素**: -- 数据粒子流动(DataParticleFlow) -- 几何图形装饰(GeometricShapes) -- 渐变网格(GradientGrid) -- 科技网格流动(TechGridFlow) - -**动画效果**: -- 页面过渡动画(PageTransitions) -- 滚动动画(ScrollAnimations) -- 微交互效果(Hover、Click、Focus) -- 数字动画(AnimatedNumber) - -#### 4. 设计模式 - -**布局系统**: -- 响应式设计:桌面端、平板、移动端完美适配 -- 容器宽度:container-wide(最大1440px) -- 间距系统:基于4px基准的间距体系 - -**组件风格**: -- 卡片设计:圆角、阴影、边框 -- 按钮样式:填充、描边、幽灵按钮 -- 表单元素:清晰的输入框、下拉菜单 -- 导航系统:顶部导航、面包屑、移动端标签栏 - -**视觉层次**: -- 清晰的信息层次:标题 → 副标题 → 正文 → 辅助信息 -- 合理的视觉权重:通过字号、颜色、间距建立层次 -- 突出重点:使用品牌色、动画效果吸引注意力 - ---- - -### 设计原则 - -#### 1. 专业性优先 - -**原则描述**:所有设计决策必须服务于展现专业能力 - -**实施要点**: -- 使用清晰的信息架构,便于快速定位关键信息 -- 展示详细的技术方案、流程、案例 -- 提供完整的数据支撑(案例数量、客户规模、项目经验) -- 避免过度装饰,保持视觉简洁专业 - -**设计示例**: -- 服务详情页:展示完整的服务流程、技术栈、交付标准 -- 案例展示:包含客户背景、解决方案、实施效果、技术亮点 -- 关于我们:展示团队实力、资质认证、发展历程 - -#### 2. 信任感构建 - -**原则描述**:通过设计元素传递可靠、可信的品牌形象 - -**实施要点**: -- 展示真实案例和客户评价 -- 提供详细的公司信息和联系方式 -- 使用安全标识、认证徽章 -- 清晰的服务承诺和保障条款 - -**设计示例**: -- 首页:突出展示成功案例数量、客户规模 -- 联系页面:完整的公司信息、地址、电话、邮箱 -- 页脚:ICP备案、公安备案、版权信息 - -#### 3. 创新性表达 - -**原则描述**:在保持专业性的同时,展现创新能力和前沿视野 - -**实施要点**: -- 使用现代技术实现流畅的动画效果 -- 融合传统元素(水墨)与现代科技感 -- 展示前沿技术应用(AI、大数据、云计算) -- 持续优化用户体验和交互设计 - -**设计示例**: -- Hero区域:水墨背景 + 数据粒子流动效果 -- 服务介绍:使用3D效果展示技术架构 -- 新闻动态:展示最新的技术趋势和行业洞察 - -#### 4. 可访问性保障 - -**原则描述**:确保所有用户都能无障碍使用网站 - -**实施要点**: -- 遵循 WCAG 2.1 AA 标准 -- 色彩对比度:文本与背景对比度 ≥ 4.5:1 -- 键盘导航:所有交互元素可通过键盘访问 -- 屏幕阅读器支持:提供完整的 ARIA 标签 -- 减少动画:支持 prefers-reduced-motion 媒体查询 - -**设计示例**: -- 所有图片提供 alt 文本 -- 表单元素关联 label -- 焦点状态清晰可见 -- 色彩对比度检查通过 - -#### 5. 响应式优先 - -**原则描述**:确保所有设备上的体验一致性 - -**实施要点**: -- 移动端优先设计 -- 触摸友好的交互元素(最小触摸区域 44x44px) -- 自适应的布局和字体大小 -- 优化的移动端导航(标签栏、汉堡菜单) - -**设计示例**: -- 移动端:底部标签栏导航 -- 平板:侧边导航 + 内容区域 -- 桌面:顶部导航 + 完整布局 - ---- - -### 参考与反参考 - -**正面参考**: -- **阿里云官网**:企业级B2B网站的专业性和信任感 -- **腾讯云官网**:技术能力展示和案例呈现方式 -- **华为官网**:企业品牌形象和文化传递 - -**反参考**: -- 过度炫技的视觉效果(影响加载速度和可读性) -- 过于卡通化的设计风格(不符合企业级定位) -- 信息过载的页面布局(影响用户决策) - ---- - -### 技术实现规范 - -**前端技术栈**: -- Next.js 16(App Router) -- React 19 -- TypeScript -- Tailwind CSS 4 -- Framer Motion(动画) -- Three.js(3D效果) - -**设计工具**: -- Tailwind CSS:样式系统 -- CSS Variables:设计令牌 -- Framer Motion:动画库 -- Lucide React:图标库 - -**性能优化**: -- 图片优化:WebP/AVIF 格式,响应式图片 -- 代码分割:动态导入组件 -- 缓存策略:静态资源长期缓存 -- 预加载:关键资源预加载 - ---- - -### 质量保障 - -**代码质量**: -- ESLint:代码规范检查 -- TypeScript:类型安全 -- Prettier:代码格式化 -- Husky:Git Hooks - -**测试覆盖**: -- 单元测试:Jest -- 集成测试:Testing Library -- E2E测试:Playwright -- 可访问性测试:axe-core - -**CI/CD流水线**: -- 代码质量检查(Lint、Type Check) -- 单元测试和集成测试 -- E2E测试(分层测试) -- 安全扫描 -- Docker镜像构建 -- 自动化部署 - ---- - -## 使用指南 - -### 如何使用本文档 - -1. **新功能开发**:在设计新功能前,先查阅本文档,确保符合设计原则 -2. **设计评审**:使用本文档作为评审标准,检查设计决策是否一致 -3. **团队协作**:新成员加入时,阅读本文档快速了解设计方向 -4. **设计迭代**:定期回顾本文档,根据业务发展更新设计方向 - -### 设计决策流程 - -1. **明确目标**:确定设计目标是否服务于"专业、可靠、创新"的品牌个性 -2. **参考原则**:查阅设计原则,确保符合核心原则 -3. **视觉规范**:使用色彩系统、字体系统、组件库 -4. **技术实现**:遵循技术实现规范,确保性能和可维护性 -5. **质量验证**:通过测试和评审,确保质量达标 - ---- - -## 版本历史 - -| 版本 | 日期 | 变更内容 | 作者 | -|------|------|----------|------| -| 1.0 | 2026-03-27 | 初始版本,建立设计上下文 | 张翔 | - ---- - -## 维护说明 - -本文档是**活文档**,应随着项目发展持续更新: - -- **品牌升级**:更新品牌个性、视觉方向 -- **用户反馈**:根据用户反馈调整设计原则 -- **技术演进**:更新技术实现规范 -- **设计迭代**:记录设计决策的演变过程 - -**更新流程**: -1. 提出设计变更建议 -2. 团队讨论和评审 -3. 更新本文档 -4. 通知所有相关成员 -5. 在实际项目中验证 - ---- - -> **最后更新**:2026-03-27 -> **维护者**:张翔 -> **联系方式**:contact@novalon.cn +## Design Context + +### Users +- 中国企业决策者(CEO/CIO/CTO),寻求数字化转型服务 +- 使用场景:评估技术供应商、了解解决方案、发起咨询 +- 期望感受:专业可信、文化共鸣、技术前沿 + +### Brand Personality +- 沉稳 · 精致 · 可信赖 +- 东方水墨美学 + 现代科技感 +- 不是高高在上的"专家",而是坐下来一起想办法的"同行者" + +### Aesthetic Direction +- **风格**: 水墨雅致 — 以留白和排版取胜,特效点到为止 +- **参考**: Apple 中国官网(极致留白、精准排版、微妙动效) +- **核心视觉**: 墨韵流光(旋转渐变边框 + 鼠标跟随光晕)— 全站统一 +- **反参考**: 过度装饰、花哨动画、多色渐变、拥挤布局 + +### Design Principles + +1. **留白即力量** — 内容呼吸,不拥挤。section 间距 generous,卡片内部留白充足 +2. **墨韵流光统一** — 所有卡片共享 ink-glow-border + mouse-follow 系统,但参数克制 +3. **朱砂点睛** — 品牌红 #C41E3A 仅作点缀,不作为主色调。标题中关键词用 font-calligraphy 突出 +4. **层次分明** — 通过字重和间距建立层级,而非颜色多样性 +5. **克制动效** — 动效服务于信息传达,不炫技。hover 效果统一:translateY(-4px) + shadow + glow + +### Design Tokens + +#### Colors (Strict) +| Token | Value | Usage | +|-------|-------|-------| +| ink | #1C1C1C | 主文字、深色背景 | +| ink-light | #595959 | 次要文字(唯一值) | +| ink-muted | #A3A3A3 | 辅助文字(唯一值) | +| cinnabar | #C41E3A | 品牌强调色,仅点缀 | +| paper | #FFFFFF | 主背景 | +| paper-warm | #FAFAFA | 交替 section 背景 | +| ink-dark | #0A0A0A | CTA 深色背景 | + +#### Typography +| Level | Size | Weight | Usage | +|-------|------|--------|-------| +| H1 | text-5xl sm:text-6xl lg:text-7xl | font-normal (brand) | Hero 品牌名 | +| H2 | text-3xl sm:text-4xl | font-semibold | Section 标题 | +| H3 | text-lg sm:text-xl | font-semibold | Card 标题 | +| Body | text-base | normal | 正文描述 | +| Small | text-sm | normal | 辅助信息 | +| Mono | text-xs font-mono | normal | 编号标签 | + +#### Spacing +| Token | Value | Usage | +|-------|-------|-------| +| section-y | py-20 md:py-28 | Section 纵向间距 | +| card-p | p-6 md:p-8 | 卡片内边距 | +| grid-gap | gap-6 md:gap-8 | 网格间距 | + +#### Card System +- 所有卡片: `ink-glow-border rounded-2xl` + mouse-follow + hover translateY(-4px) +- 鼠标光晕: `radial-gradient(400px circle, rgba(accent, 0.04), transparent 40%)` +- 阴影: hover `0 16px 32px rgba(0,0,0,0.08)` / default `0 1px 3px rgba(0,0,0,0.04)` +- 边框: ink-glow-border 旋转渐变 + +#### Section Backgrounds (Alternating) +1. Hero: paper (#FFFFFF) +2. Social Proof: paper-warm (#FAFAFA) +3. Product Matrix: paper (#FFFFFF) +4. Challenge: paper-warm (#FAFAFA) +5. Services: paper (#FFFFFF) +6. Methodology: paper-warm (#FAFAFA) +7. Home Solutions: paper (#FFFFFF) +8. Testimonials: paper-warm (#FAFAFA) +9. Team: paper (#FFFFFF) +10. About: paper-warm (#FAFAFA) +11. News: paper (#FFFFFF) +12. CTA: ink-dark (#1C1C1C) diff --git a/package.json b/package.json index c955d58..496e98f 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "private": true, "scripts": { "dev": "next dev -p 3000", + "dev:clean": "rm -rf .next dist && next dev -p 3000", "build": "next build", + "build:clean": "rm -rf .next dist && next build", "start": "npx serve dist -p 3000", "lint": "eslint", "type-check": "tsc --noEmit", diff --git a/public/logo-light.svg b/public/logo-light.svg new file mode 100644 index 0000000..f2f64dc --- /dev/null +++ b/public/logo-light.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOVALON + + \ No newline at end of file diff --git a/src/app/(marketing)/about/client.tsx b/src/app/(marketing)/about/client.tsx index be76760..369407b 100644 --- a/src/app/(marketing)/about/client.tsx +++ b/src/app/(marketing)/about/client.tsx @@ -1,267 +1,229 @@ 'use client'; import { motion } from 'framer-motion'; -import { useInView } from 'framer-motion'; -import { useRef, useState, useEffect, useMemo } from 'react'; +import { useState, useEffect, useMemo } from 'react'; import { COMPANY_INFO, STATS } from '@/lib/constants'; -import { Card, CardContent } from '@/components/ui/card'; -import { PageHeader } from '@/components/ui/page-header'; import { FlipClock } from '@/components/ui/flip-clock'; -import { Lightbulb, Users, Target, Award, MapPin, Mail } from 'lucide-react'; +import { PageNav } from '@/components/layout/page-nav'; +import { Users, Target, Award, MapPin, Mail } from 'lucide-react'; +import { differenceInYears, differenceInMonths, differenceInDays, subYears, subMonths } from 'date-fns'; export function AboutClient() { - const contentRef = useRef(null); - const isContentInView = useInView(contentRef, { once: true, margin: '-100px' }); const [operationTime, setOperationTime] = useState({ days: 0, months: 0, years: 0 }); useEffect(() => { const foundingDate = new Date('2026-01-15'); const calculateTime = () => { const now = new Date(); - const diff = now.getTime() - foundingDate.getTime(); - - const days = Math.floor(diff / (1000 * 60 * 60 * 24)); - const years = Math.floor(days / 365); - const months = Math.floor((days % 365) / 30); - + const years = differenceInYears(now, foundingDate); + const afterYears = subYears(now, years); + const months = differenceInMonths(afterYears, foundingDate); + const afterMonths = subMonths(afterYears, months); + const days = differenceInDays(afterMonths, foundingDate); setOperationTime({ days, months, years }); }; - calculateTime(); const timer = setInterval(calculateTime, 60000); - return () => clearInterval(timer); }, []); const values = useMemo(() => [ - { - icon: Lightbulb, - title: '创新驱动', - description: '持续探索前沿技术,以创新思维解决业务挑战,为客户创造差异化价值', - }, - { - icon: Users, - title: '客户至上', - description: '深入理解客户需求,提供个性化解决方案,建立长期合作伙伴关系', - }, - { - icon: Target, - title: '追求卓越', - description: '以最高标准要求自己,持续优化产品和服务质量,超越客户期望', - }, - { - icon: Award, - title: '诚信为本', - description: '坚持透明沟通,信守承诺,以诚信赢得客户信任和尊重', - }, + { icon: Target, title: '务实', description: '不追逐风口,只做真正为客户创造价值的事。' }, + { icon: Users, title: '陪伴', description: '交付只是开始,长期陪跑才是我们的承诺。' }, + { icon: Award, title: '专业', description: '用扎实的工程能力和行业经验赢得信任。' }, ], []); const milestones = useMemo(() => [ - { - date: '2026年1月', - title: '公司成立', - description: '四川睿新致远科技有限公司在成都龙泉驿区正式成立,专注于企业数字化转型解决方案', - }, - { - date: '2026年1月', - title: '团队组建', - description: '核心团队到位,技术团队拥有丰富的行业经验和专业技能', - }, - { - date: '2026年2月', - title: '业务启动', - description: '推出企业数字化转型解决方案,开始服务首批客户', - }, - { - date: '2026年2月', - title: '产品发布', - description: '自主研发的ERP、CRM等产品陆续上线,为客户提供一站式数字化服务', - }, + { date: '2026年1月', title: '公司成立', description: '四川睿新致远科技有限公司在成都龙泉驿区正式成立' }, + { date: '2026年1月', title: '团队组建', description: '核心团队到位,成员来自多个大型传统IT企业,具备扎实的工程能力和规范化交付经验' }, + { date: '2026年2月', title: '业务启动', description: '推出企业数字化转型解决方案,开始服务首批客户' }, + { date: '2026年3月', title: '产品研发', description: '自主研发的ERP、CRM等产品启动研发,逐步构建产品矩阵' }, + { date: '2026年5月', title: '产品上线', description: '首批产品完成开发并上线试运行,形成覆盖企业管理核心场景的产品体系' }, ], []); return (
- - -
- -
-

睿新的选择

-

- 所以我们选择了一条不同的路。 -

- -
-

智连未来

-

- 我们坚持对行业趋势的深度研究,不追逐昙花一现的概念。 -

-

- 每一次方案,都源于对您业务场景的洞察; -

-

- 每一次连接,都为了让技术真正服务于您的未来。 -

-
- -
-

成长伙伴

-

- 我们不把“项目交付”当作终点。 -

-

- 您的业务增长了吗?您的团队能力提升了吗? -

-

- 您下一次遇到难题时,还会第一个想到我们吗? -

-

- 这些问题,比“项目是否按时交付”更让我们在意。 -

-
-
- -
-

品牌承诺

-

- 我们承诺: -

-
    -
  • - - 不卖您用不上的技术 -
  • -
  • - - 不说不懂业务的术语 -
  • -
  • - - 不做路过就忘的“一锤子买卖” -
  • -
-

- 我们只做一件事:成为您数字化转型路上,信得过的成长伙伴。 -

-
- - - +
+
+ - {STATS.map((stat, idx) => ( - - -
{stat.value}
-
{stat.label}
-
-
- ))} +

About

+

+ 关于我们 +

+

+ 企业需要的,不是一个高高在上的专家,也不是一个做完就跑的卖家,而是一个能坐下来、一起想办法的同行者。 +

+
+
- -

核心价值观

-
- {values.map((value, idx) => ( - -
- +
+
+
+ +

关于 睿新致遠

+

智连未来,成长伙伴

+

企业需要的,不是一个高高在上的专家,也不是一个做完就跑的卖家,而是一个能坐下来、一起想办法的同行者。

+ +
+

智连未来

+

我们坚持对行业趋势的深度研究,不追逐昙花一现的概念。

+

每一次方案,都源于对您业务场景的洞察;

+

每一次连接,都为了让技术真正服务于您的未来。

+
+ +
+

成长伙伴

+

我们不把"项目交付"当作终点。

+

您的业务增长了吗?您的团队能力提升了吗?

+

您下一次遇到难题时,还会第一个想到我们吗?

+

这些问题,比"项目是否按时交付"更让我们在意。

+
+
+ + +

品牌承诺

+

我们承诺:

+
    +
  • + + 不卖您用不上的技术 +
  • +
  • + + 不说不懂业务的术语 +
  • +
  • + + 不做路过就忘的"一锤子买卖" +
  • +
+

+ 我们只做一件事:成为您数字化转型路上,信得过的成长伙伴。 +

+
+ + + + + {STATS.map((stat, idx) => ( +
+
{stat.value}
+
{stat.label}
+
+ ))} +
+ + +

核心价值观

+
+ {values.map((value) => ( +
+
+ +
+
+

{value.title}

+

{value.description}

+
+
+ ))} +
+
+ + +

发展历程

+
+ {milestones.map((milestone) => ( +
+
+ {milestone.date} +
+
+

{milestone.title}

+

{milestone.description}

+
+
+ ))} +
+
+ + +

联系我们

+
+
+
+
-

{value.title}

-

{value.description}

+

公司地址

+

{COMPANY_INFO.address}

- - ))} -
- - - -

发展历程

-
- {milestones.map((milestone, idx) => ( - -
- {milestone.date} -
-
-

{milestone.title}

-

{milestone.description}

-
-
- ))} -
-
- - -

联系我们

-
-
-
-
-
-

公司地址

-

{COMPANY_INFO.address}

+
+
+ +
+
+

电子邮箱

+

{COMPANY_INFO.email}

+
-
-
- -
-
-

电子邮箱

-

{COMPANY_INFO.email}

-
-
- -
- - -
+
+
+
+
); } diff --git a/src/app/(marketing)/cases/[id]/client.test.tsx b/src/app/(marketing)/cases/[id]/client.test.tsx deleted file mode 100644 index 58ece32..0000000 --- a/src/app/(marketing)/cases/[id]/client.test.tsx +++ /dev/null @@ -1,209 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import { CaseDetailClient } from './client'; - -interface TestCaseItem { - id: string; - title: string; - excerpt: string; - content: string; - category: string; - slug: string; - date: string; - image?: string; - challenge: string; - solution: string; - keyMoments: { title: string; description: string }[]; - results: { label: string; value: string }[]; - testimonial: { quote: string; author: string; role: string }; - duration: string; -} - -jest.mock('next/navigation', () => ({ - useRouter: () => ({ - push: jest.fn(), - back: jest.fn(), - forward: jest.fn(), - }), -})); - -jest.mock('next/link', () => { - const MockLink = ({ children, href }: { children: React.ReactNode; href: string }) => { - return {children}; - }; - MockLink.displayName = 'MockLink'; - return MockLink; -}); - -const mockCaseItem: TestCaseItem = { - id: 'test-case', - title: '测试案例标题', - excerpt: '这是一个测试案例的描述', - content: '这是测试案例的详细内容', - category: '制造业', - slug: 'test-case', - date: '2026-03-27', - challenge: '这是客户面临的挑战描述', - solution: '这是我们的解决方案描述', - keyMoments: [ - { title: '关键时刻一', description: '关键时刻一的详细描述' }, - { title: '关键时刻二', description: '关键时刻二的详细描述' }, - ], - results: [ - { label: '运营成本', value: '降低25%' }, - { label: '设备故障响应', value: '缩短85%' }, - { label: '排产周期', value: '从1周缩至半天' }, - ], - testimonial: { - quote: '这是客户证言内容', - author: '测试客户', - role: 'CTO', - }, - duration: '2年', -}; - -describe('CaseDetailClient', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('Rendering', () => { - it('should render case detail page', () => { - const { container } = render(); - expect(container.firstChild).toBeInTheDocument(); - }); - - it('should render case title', () => { - render(); - const title = screen.getByRole('heading', { level: 1 }); - expect(title).toBeInTheDocument(); - expect(title).toHaveTextContent('测试案例标题'); - }); - - it('should render case excerpt', () => { - render(); - const excerpts = screen.getAllByText('这是一个测试案例的描述'); - expect(excerpts.length).toBeGreaterThan(0); - }); - - it('should render case industry badge', () => { - render(); - const categories = screen.getAllByText('制造业'); - expect(categories.length).toBeGreaterThan(0); - }); - - it('should render challenge content', () => { - render(); - expect(screen.getByText('这是客户面临的挑战描述')).toBeInTheDocument(); - }); - - it('should render solution content', () => { - render(); - expect(screen.getByText('这是我们的解决方案描述')).toBeInTheDocument(); - }); - - it('should render results data', () => { - render(); - expect(screen.getByText('降低25%')).toBeInTheDocument(); - expect(screen.getByText('缩短85%')).toBeInTheDocument(); - }); - - it('should render testimonial', () => { - render(); - expect(screen.getByText('这是客户证言内容')).toBeInTheDocument(); - const authors = screen.getAllByText('测试客户'); - expect(authors.length).toBeGreaterThan(0); - const roles = screen.getAllByText('CTO'); - expect(roles.length).toBeGreaterThan(0); - }); - - it('should render contact button', () => { - render(); - const contactButton = screen.getByRole('link', { name: /联系我们/i }); - expect(contactButton).toBeInTheDocument(); - }); - - it('should render duration in sidebar', () => { - render(); - expect(screen.getByText('2年')).toBeInTheDocument(); - }); - }); - - describe('Sections', () => { - it('should render customer challenges section', () => { - render(); - const section = screen.getByText('客户遇到的成长瓶颈'); - expect(section).toBeInTheDocument(); - }); - - it('should render solution section', () => { - render(); - const section = screen.getByText('我们如何智连未来'); - expect(section).toBeInTheDocument(); - }); - - it('should render growth story section with key moments', () => { - render(); - const section = screen.getByText('共同成长的故事'); - expect(section).toBeInTheDocument(); - expect(screen.getByText('关键时刻一')).toBeInTheDocument(); - expect(screen.getByText('关键时刻二')).toBeInTheDocument(); - }); - - it('should render achievements section with results', () => { - render(); - const section = screen.getByText('今天,他们走到了哪里'); - expect(section).toBeInTheDocument(); - }); - - it('should render testimonial section', () => { - render(); - const section = screen.getByText('客户证言精选'); - expect(section).toBeInTheDocument(); - }); - }); - - describe('Conditional Rendering', () => { - it('should not render key moments section when empty', () => { - const caseWithoutMoments = { ...mockCaseItem, keyMoments: [] }; - render(); - expect(screen.queryByText('共同成长的故事')).not.toBeInTheDocument(); - }); - - it('should not render results section when empty', () => { - const caseWithoutResults = { ...mockCaseItem, results: [] }; - render(); - expect(screen.queryByText('今天,他们走到了哪里')).not.toBeInTheDocument(); - }); - - it('should not render testimonial section when absent', () => { - const caseWithoutTestimonial = { ...mockCaseItem, testimonial: undefined }; - render(); - expect(screen.queryByText('客户证言精选')).not.toBeInTheDocument(); - }); - }); - - describe('Navigation', () => { - it('should have back button', () => { - render(); - const backButton = screen.getByRole('button', { name: /返回/i }); - expect(backButton).toBeInTheDocument(); - }); - }); - - describe('Accessibility', () => { - it('should have container element', () => { - const { container } = render(); - expect(container.firstChild).toBeInTheDocument(); - }); - - it('should have proper heading hierarchy', () => { - render(); - const h1 = screen.getByRole('heading', { level: 1 }); - expect(h1).toBeInTheDocument(); - - const h2s = screen.getAllByRole('heading', { level: 2 }); - expect(h2s.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/app/(marketing)/cases/[id]/client.tsx b/src/app/(marketing)/cases/[id]/client.tsx deleted file mode 100644 index 5cb6bbd..0000000 --- a/src/app/(marketing)/cases/[id]/client.tsx +++ /dev/null @@ -1,286 +0,0 @@ -'use client'; - -import { useEffect, useRef, useState } from 'react'; -import { StaticLink } from '@/components/ui/static-link'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { BackButton } from '@/components/ui/back-button'; -import { - Target, - Quote, - Clock, - MessageCircle, - Award, - TrendingUp, -} from 'lucide-react'; - -interface CaseKeyMoment { - title: string; - description: string; -} - -interface CaseResult { - label: string; - value: string; -} - -interface CaseTestimonial { - quote: string; - author: string; - role: string; -} - -interface CaseItem { - id: string; - title: string; - excerpt: string; - content: string; - category: string; - slug: string; - date: string; - image?: string; - /** 客户面临的挑战 */ - challenge: string; - /** 我们的解决方案 */ - solution: string; - /** 关键时刻 */ - keyMoments: CaseKeyMoment[]; - /** 成果数据 */ - results: CaseResult[]; - /** 客户证言 */ - testimonial?: CaseTestimonial; - /** 合作时长 */ - duration: string; -} - -interface CaseDetailClientProps { - caseItem: CaseItem; -} - -export function CaseDetailClient({ caseItem }: CaseDetailClientProps) { - const [isVisible, setIsVisible] = useState(false); - const contentRef = useRef(null); - - useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => { - if (entry?.isIntersecting) { - setIsVisible(true); - } - }, - { threshold: 0.1 } - ); - - if (contentRef.current) { - observer.observe(contentRef.current); - } - - return () => observer.disconnect(); - }, []); - - return ( -
-
-
- -
- - {caseItem.category} - -

- {caseItem.title} -

-

{caseItem.excerpt}

-
-
-
- -
-
-
- {/* 客户遇到的成长瓶颈 */} -
-
-
- -
-

- 客户遇到的成长瓶颈 -

-
-

- {caseItem.challenge} -

-
- - {/* 我们如何智连未来 */} -
-
-
- -
-

- 我们如何智连未来 -

-
-
-

- {caseItem.solution} -

-
-
- - {/* 共同成长的故事 */} - {caseItem.keyMoments && caseItem.keyMoments.length > 0 && ( -
-
-
- -
-

- 共同成长的故事 -

-
-
- {caseItem.keyMoments.map((moment, index) => ( -
-
- -
-

- {moment.title} -

-

- {moment.description} -

-
-
-
- ))} -
-
- )} - - {/* 今天,他们走到了哪里 */} - {caseItem.results && caseItem.results.length > 0 && ( -
-
-
- -
-

- 今天,他们走到了哪里 -

-
-
- {caseItem.results.map((result, index) => ( -
- -
- {result.value} -
-
- {result.label} -
-
- ))} -
-
- )} - - {/* 客户证言精选 */} - {caseItem.testimonial && ( -
-
-
- -
-

- 客户证言精选 -

-
-
- -

- {caseItem.testimonial.quote} -

-
-
- -
-
-

- {caseItem.testimonial.author} -

-

- {caseItem.testimonial.role} -

-
-
-
-
- )} -
- - {/* 侧边栏 */} -
-
-

- 项目信息 -

-
-
-
客户名称
-
- {caseItem.testimonial?.author || '客户企业'} -
-
-
-
行业领域
-
- {caseItem.category} -
-
-
-
合作时长
-
- {caseItem.duration || '3年'} -
-
-
-
发布时间
-
{caseItem.date}
-
-
-
- -
-

想要了解更多?

-

- 联系我们的专家团队,获取定制化解决方案 -

- -
-
-
-
-
- ); -} diff --git a/src/app/(marketing)/cases/[id]/page.tsx b/src/app/(marketing)/cases/[id]/page.tsx deleted file mode 100644 index ac81432..0000000 --- a/src/app/(marketing)/cases/[id]/page.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Metadata } from 'next'; -import { notFound } from 'next/navigation'; -import { CASES } from '@/lib/constants'; -import { CaseDetailClient } from './client'; - -export async function generateStaticParams() { - return CASES.map((caseItem) => ({ - id: caseItem.id, - })); -} - -export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise { - const { id } = await params; - const caseItem = CASES.find((c) => c.id === id); - - if (!caseItem) { - return { - title: '案例未找到', - }; - } - - return { - title: `${caseItem.title} - 睿新致远`, - description: caseItem.description, - }; -} - -export default async function CaseDetailPage({ params }: { params: Promise<{ id: string }> }) { - const { id } = await params; - const caseItem = CASES.find((c) => c.id === id); - - if (!caseItem) { - notFound(); - } - - return ; -} diff --git a/src/app/(marketing)/cases/layout.tsx b/src/app/(marketing)/cases/layout.tsx deleted file mode 100644 index 16d6a3f..0000000 --- a/src/app/(marketing)/cases/layout.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Metadata } from 'next'; - -export const metadata: Metadata = { - title: '客户案例 - 睿新致远', - description: '与谁同行,决定能走多远', -}; - -export default function CasesLayout({ - children, -}: { - children: React.ReactNode; -}) { - return <>{children}; -} diff --git a/src/app/(marketing)/cases/page.tsx b/src/app/(marketing)/cases/page.tsx deleted file mode 100644 index f87c740..0000000 --- a/src/app/(marketing)/cases/page.tsx +++ /dev/null @@ -1,255 +0,0 @@ -'use client'; - -import { useState, useMemo, useRef, ChangeEvent } from 'react'; -import { useInView } from 'framer-motion'; -import { CASES } from '@/lib/constants'; -import { Badge } from '@/components/ui/badge'; -import { Input } from '@/components/ui/input'; -import { Button } from '@/components/ui/button'; -import { PageHeader } from '@/components/ui/page-header'; -import { Search, ArrowLeft, Building2, Calendar, TrendingUp, ChevronLeft, ChevronRight, Filter } from 'lucide-react'; -import { StaticLink } from '@/components/ui/static-link'; -import { motion } from 'framer-motion'; - -const industries = ['全部', ...Array.from(new Set(CASES.map((c) => c.industry)))]; -const ITEMS_PER_PAGE = 6; - -export default function CasesPage() { - const [selectedIndustry, setSelectedIndustry] = useState('全部'); - const [searchQuery, setSearchQuery] = useState(''); - const [currentPage, setCurrentPage] = useState(1); - const contentRef = useRef(null); - const isContentInView = useInView(contentRef, { once: true, margin: '-100px' }); - - const filteredCases = useMemo(() => { - return CASES.filter((caseItem) => { - const matchesIndustry = selectedIndustry === '全部' || caseItem.industry === selectedIndustry; - const matchesSearch = - caseItem.title.toLowerCase().includes(searchQuery.toLowerCase()) || - caseItem.description.toLowerCase().includes(searchQuery.toLowerCase()); - return matchesIndustry && matchesSearch; - }); - }, [selectedIndustry, searchQuery]); - - const totalPages = Math.ceil(filteredCases.length / ITEMS_PER_PAGE); - const paginatedCases = useMemo(() => { - const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; - const endIndex = startIndex + ITEMS_PER_PAGE; - return filteredCases.slice(startIndex, endIndex); - }, [filteredCases, currentPage]); - - const handlePageChange = (page: number) => { - setCurrentPage(page); - window.scrollTo({ top: 0, behavior: 'smooth' }); - }; - - const handleIndustryChange = (industry: string) => { - setSelectedIndustry(industry); - setCurrentPage(1); - }; - - const handleSearchChange = (e: ChangeEvent) => { - setSearchQuery(e.target.value); - setCurrentPage(1); - }; - - return ( -
- - -
-
- -
-
- - 行业筛选: -
-
- {industries.map((industry) => ( - - ))} -
-
- -
- - -
-
- - {paginatedCases.length === 0 ? ( -
-

没有找到相关案例

-
- ) : ( - <> -
- {paginatedCases.map((caseItem, index) => ( - - -
-
- -
-
- - {caseItem.industry} - -
-
- -
-
- - {caseItem.client} -
- -

- {caseItem.title} -

- -
- - - {caseItem.duration}合作 - - {caseItem.tags.slice(0, 1).map((tag) => ( - - - {tag} - - ))} -
- -

- {caseItem.description} -

- -
- 查看详情 - -
-
-
-
- ))} -
- - {totalPages > 1 && ( -
- - - {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( - - ))} - - -
- )} - -
- 显示 {paginatedCases.length} 条,共 {filteredCases.length} 条案例 -
- - )} -
-
- - -
-

- 准备开始您的数字化转型之旅? -

-

- 让我们与您同行,共创美好未来 -

-
- - - - - - -
-
-
-
- ); -} diff --git a/src/app/(marketing)/contact/page.tsx b/src/app/(marketing)/contact/page.tsx index df67346..63f3fc7 100644 --- a/src/app/(marketing)/contact/page.tsx +++ b/src/app/(marketing)/contact/page.tsx @@ -1,14 +1,16 @@ 'use client'; -import { useState, useEffect, useRef, Suspense } from 'react'; +import { useState, useEffect, Suspense } from 'react'; import { useSearchParams } from 'next/navigation'; import { z } from 'zod'; +import { motion } from 'framer-motion'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Toast } from '@/components/ui/toast'; import { Mail, MapPin, Send, Loader2, Clock, HeadphonesIcon, CheckCircle2 } from 'lucide-react'; import { COMPANY_INFO } from '@/lib/constants'; +import { PageNav } from '@/components/layout/page-nav'; import { trackContactForm, trackConversion } from '@/lib/analytics'; const contactFormSchema = z.object({ @@ -32,7 +34,6 @@ interface FormErrors { function ContactFormContent() { const searchParams = useSearchParams(); const isSuccessFromRedirect = searchParams.get('success') === 'true'; - const [isVisible, setIsVisible] = useState(false); const [showToast, setShowToast] = useState(isSuccessFromRedirect); const [toastMessage, setToastMessage] = useState( isSuccessFromRedirect ? '表单提交成功!我们会尽快与您联系。' : '' @@ -50,13 +51,12 @@ function ContactFormContent() { message: '', }); const [errors, setErrors] = useState({}); - const sectionRef = useRef(null); useEffect(() => { - requestAnimationFrame(() => { - setIsVisible(true); - }); - }, []); + if (isSuccessFromRedirect) { + setShowToast(true); + } + }, [isSuccessFromRedirect]); const validateField = (field: keyof ContactFormData, value: string) => { try { @@ -85,9 +85,9 @@ function ContactFormContent() { async function handleSubmit(e: React.FormEvent) { e.preventDefault(); - + const result = contactFormSchema.safeParse(formData); - + if (!result.success) { const fieldErrors: FormErrors = {}; result.error.issues.forEach((issue) => { @@ -161,48 +161,46 @@ function ContactFormContent() { onClose={() => setShowToast(false)} /> )} - -
-
-
-
-
-
+
+ + -
-
- 联系我们 -
-

- 开启 合作 +

Contact

+

+ 联系我们

-

+

无论您有任何问题或合作意向,我们都很乐意与您交流

-
+
+
+
+
+
-

联系方式

-
- +
+
-

邮箱

+

邮箱

{COMPANY_INFO.email} @@ -210,31 +208,29 @@ function ContactFormContent() {
-
- +
+
-

地址

+

地址

{COMPANY_INFO.address}

-
+

工作时间

-
-
- 周一至周五 - 9:00 - 18:00 -
+
+ 周一至周五 + 9:00 - 18:00
-
+

我们的承诺

@@ -242,111 +238,111 @@ function ContactFormContent() {
-

工作日 2 小时内快速响应您的咨询

+

工作日 2 小时内快速响应您的咨询

-

提供免费的业务咨询和方案评估服务

+

提供免费的业务咨询和方案评估服务

-

根据您的需求量身定制最优解决方案

+

根据您的需求量身定制最优解决方案

-
+ -
-
-

发送消息

- +
+

发送消息

+ {isSubmitted ? ( -
-
+
+
-

消息已发送

-

感谢您的留言,我们会尽快与您联系!

+

消息已发送

+

感谢您的留言,我们会尽快与您联系!

) : ( -
+
- handleChange('name', e.target.value)} onBlur={(e) => handleBlur('name', e.target.value)} error={errors.name} /> - handleChange('phone', e.target.value)} onBlur={(e) => handleBlur('phone', e.target.value)} error={errors.phone} />
- handleChange('email', e.target.value)} - onBlur={(e) => handleBlur('email', e.target.value)} - error={errors.email} - /> - handleBlur('email', e.target.value)} + error={errors.email} + /> + handleChange('subject', e.target.value)} - onBlur={(e) => handleBlur('subject', e.target.value)} - error={errors.subject} - /> -