diff --git a/prisma/seed.ts b/prisma/seed.ts index cfe2954..8043b4f 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -8,6 +8,8 @@ import { SERVICES } from '../src/lib/constants/services'; import { PRODUCTS } from '../src/lib/constants/products'; import { SOLUTIONS } from '../src/lib/constants/solutions'; import { STATS } from '../src/lib/constants/stats'; +import { COMPANY_INFO } from '../src/lib/constants/company'; +import { NAVIGATION_V2, MEGA_DROPDOWN_DATA } from '../src/lib/constants/navigation'; const prisma = new PrismaClient(); @@ -30,7 +32,7 @@ async function seedItems( const item = items[i]!; const slug = String(item[slugField] || item.id || `${modelCode}-${i}`); const title = String(item[titleField] || `未命名 ${i + 1}`); - const { id, slug: _s, title: _t, ...data } = item as Record; + const { id: _id, slug: _s, title: _t, ...data } = item as Record; await prisma.contentItem.upsert({ where: { modelCode_slug: { modelCode, slug } }, @@ -216,6 +218,49 @@ async function main() { ], }, }, + // === site-config === + { + model: { + code: 'site-config', + name: '站点配置', + description: '公司基本信息、SEO 元数据等全局配置', + isPageType: false, + urlPattern: '', + hasVersions: false, + hasDraft: true, + icon: 'settings', + fields: [ + { name: 'name', type: 'text', label: '公司全称', required: true }, + { name: 'shortName', type: 'text', label: '公司简称', required: true }, + { name: 'displayName', type: 'text', label: '展示名称', required: true }, + { name: 'slogan', type: 'text', label: '品牌标语', required: true }, + { name: 'description', type: 'textarea', label: '公司简介', required: true }, + { name: 'founded', type: 'text', label: '成立年份' }, + { name: 'location', type: 'text', label: '所在地' }, + { name: 'email', type: 'text', label: '联系邮箱', required: true }, + { name: 'address', type: 'text', label: '办公地址', required: true }, + { name: 'icp', type: 'text', label: 'ICP 备案号' }, + { name: 'police', type: 'text', label: '公安备案号' }, + ], + }, + }, + // === navigation === + { + model: { + code: 'navigation', + name: '导航菜单', + description: '主导航项和下拉菜单内容', + isPageType: false, + urlPattern: '', + hasVersions: false, + hasDraft: true, + icon: 'menu', + fields: [ + { name: 'mainNav', type: 'json', label: '主导航项', required: true }, + { name: 'megaDropdown', type: 'json', label: '下拉菜单数据', required: true }, + ], + }, + }, // === legal-page === { model: { @@ -300,7 +345,7 @@ async function main() { const s = statList[i] as Record; const slug = `stat-${i}`; const title = String(s.label || `指标 ${i + 1}`); - const { id, ...data } = s; + const { id: _id, ...data } = s; await prisma.contentItem.upsert({ where: { modelCode_slug: { modelCode: 'stat-item', slug } }, update: { @@ -495,6 +540,37 @@ async function main() { ]); console.log(' ✅ 法律页面'); + console.log('📝 导入站点配置...'); + await seedItems('site-config', [ + { + slug: 'default', + title: '站点配置', + name: COMPANY_INFO.name, + shortName: COMPANY_INFO.shortName, + displayName: COMPANY_INFO.displayName, + slogan: COMPANY_INFO.slogan, + description: COMPANY_INFO.description, + founded: COMPANY_INFO.founded, + location: COMPANY_INFO.location, + email: COMPANY_INFO.email, + address: COMPANY_INFO.address, + icp: COMPANY_INFO.icp, + police: COMPANY_INFO.police, + }, + ]); + console.log(' ✅ 站点配置'); + + console.log('📝 导入导航菜单...'); + await seedItems('navigation', [ + { + slug: 'default', + title: '导航菜单', + mainNav: NAVIGATION_V2, + megaDropdown: MEGA_DROPDOWN_DATA, + }, + ]); + console.log(' ✅ 导航菜单'); + // ============ 5. 创建默认内容区域 ============ console.log('📝 创建默认内容区域...'); const defaultZones = [ diff --git a/src/app/(marketing)/cases/[slug]/client.tsx b/src/app/(marketing)/cases/[slug]/client.tsx index 36b4158..b285e57 100644 --- a/src/app/(marketing)/cases/[slug]/client.tsx +++ b/src/app/(marketing)/cases/[slug]/client.tsx @@ -4,7 +4,7 @@ import { notFound } from 'next/navigation'; import CaseDetailPage from '@/components/sections/case-detail-page'; import { ScrollProgress } from '@/components/ui/scroll-progress'; import type { ContentItem } from '@/lib/cms/types'; -import type { CaseStudyData } from '@/lib/cms/mock-data'; +import type { CaseStudyData } from '@/lib/cms/types'; interface CaseDetailClientProps { item: ContentItem; diff --git a/src/app/(marketing)/cases/[slug]/page.tsx b/src/app/(marketing)/cases/[slug]/page.tsx index b2c7808..3df13ae 100644 --- a/src/app/(marketing)/cases/[slug]/page.tsx +++ b/src/app/(marketing)/cases/[slug]/page.tsx @@ -2,7 +2,7 @@ import { notFound } from 'next/navigation'; import { CaseDetailClient } from './client'; import { getPublishedItemBySlug, getAllPublishedSlugs } from '@/lib/cms/data-server'; import { COMPANY_INFO } from '@/lib/constants/company'; -import type { CaseStudyData } from '@/lib/cms/mock-data'; +import type { CaseStudyData } from '@/lib/cms/types'; import type { Metadata } from 'next'; interface CaseDetailPageProps { diff --git a/src/app/(marketing)/cms-studio/page.tsx b/src/app/(marketing)/cms-studio/page.tsx deleted file mode 100644 index d8a75fa..0000000 --- a/src/app/(marketing)/cms-studio/page.tsx +++ /dev/null @@ -1,153 +0,0 @@ -'use client'; - -import { useState, useEffect } from 'react'; -import { initCms } from '@/lib/cms'; -import { ZoneManager } from '@/components/cms/ZoneManager'; -import ContentEditor from '@/components/cms/ContentEditor'; - -type ViewMode = 'content' | 'zones'; - -export default function CmsStudioPage() { - const [ready, setReady] = useState(false); - const [view, setView] = useState('zones'); - - useEffect(() => { - initCms(); - setReady(true); - }, []); - - if (!ready) { - return ( -
-
加载中...
-
- ); - } - - return ( -
- - -
-
-
-

- {view === 'zones' ? '内容区管理' : '内容管理'} -

- - {view === 'zones' ? '配置页面内容区块' : '管理所有内容条目'} - -
-
-
- - -
- -
-
- -
- {view === 'zones' ? : } -
-
-
- ); -} diff --git a/src/app/(marketing)/home-content-cms.tsx b/src/app/(marketing)/home-content-cms.tsx deleted file mode 100644 index 081ef52..0000000 --- a/src/app/(marketing)/home-content-cms.tsx +++ /dev/null @@ -1,195 +0,0 @@ -'use client'; - -import { useEffect, useState, useCallback, useMemo, createElement } from 'react'; -import { - initCms, - getItemRenderer, - ContentZoneRenderer, - getContentZone, - getContentItems, -} from '@/lib/cms'; -import { getMockHomeHeroBanner } from '@/lib/cms/mock-home'; -import type { ContentItem, ContentZone } from '@/lib/cms'; - -function SectionHeading({ eyebrow, title, description, align = 'center' }: { - eyebrow?: string; - title: string; - description?: string; - align?: 'center' | 'left'; -}) { - return ( -
- {eyebrow && ( -
- - {eyebrow} -
- )} -

- {title} -

- {description && ( -

- {description} -

- )} -
- ); -} - -export default function HomeContentCms() { - const [ready, setReady] = useState(false); - const [hero, setHero] = useState(null); - const [statsZone, setStatsZone] = useState(null); - const [servicesZone, setServicesZone] = useState(null); - const [solutionsZone, setSolutionsZone] = useState(null); - const [casesZone, setCasesZone] = useState(null); - const [newsZone, setNewsZone] = useState(null); - - const loadData = useCallback(() => { - const heroItems = getContentItems('hero-banner'); - setHero(heroItems.length > 0 ? heroItems[0]! : getMockHomeHeroBanner()); - setStatsZone(getContentZone('home-stats')); - setServicesZone(getContentZone('home-services')); - setSolutionsZone(getContentZone('home-solutions')); - setCasesZone(getContentZone('home-cases')); - setNewsZone(getContentZone('home-news')); - }, []); - - useEffect(() => { - initCms(); - loadData(); - setReady(true); - - const handleStorage = (e: StorageEvent) => { - if (e.key === 'novalon-cms-data') { - loadData(); - } - }; - window.addEventListener('storage', handleStorage); - - const handleCmsUpdate = () => loadData(); - window.addEventListener('cms-updated', handleCmsUpdate); - - return () => { - window.removeEventListener('storage', handleStorage); - window.removeEventListener('cms-updated', handleCmsUpdate); - }; - }, [loadData]); - - const HeroRenderer = useMemo( - () => (hero ? getItemRenderer(hero.modelCode) : null), - [hero] - ); - - if (!ready) { - return ( -
-
加载中...
-
- ); - } - - return ( -
- {hero && HeroRenderer && createElement(HeroRenderer, { item: hero })} - -
-
- - {statsZone && } -
-
- -
-
- - {servicesZone && } -
-
- -
-
- - {solutionsZone && } -
-
- -
-
- - {casesZone && } -
-
- -
-
- - {newsZone && } -
-
- -
- -
-
- ); -} diff --git a/src/app/(marketing)/news/news-content-cms.tsx b/src/app/(marketing)/news/news-content-cms.tsx deleted file mode 100644 index d1bd3f6..0000000 --- a/src/app/(marketing)/news/news-content-cms.tsx +++ /dev/null @@ -1,183 +0,0 @@ -'use client'; - -import { useState, useMemo, useEffect } from 'react'; -import { initCms, getItemRenderer } from '@/lib/cms'; -import type { ContentItem } from '@/lib/cms'; -import { NEWS } from '@/lib/constants'; - -const categories = ['全部', '公司新闻', '研发动态']; -const ITEMS_PER_PAGE = 9; - -function newsToContentItem(news: unknown, index: number): ContentItem { - const n = news as Record; - return { - id: String(n.id), - modelId: 'news', - modelCode: 'news', - title: String(n.title), - slug: String(n.id), - status: 'published', - version: 1, - sortOrder: index, - data: n, - createdAt: '2024-01-01T00:00:00Z', - updatedAt: '2024-01-01T00:00:00Z', - publishedAt: '2024-01-01T00:00:00Z', - createdBy: 'system', - updatedBy: 'system', - }; -} - -export default function NewsContentCms() { - const [ready, setReady] = useState(false); - const [items, setItems] = useState([]); - const [selectedCategory, setSelectedCategory] = useState('全部'); - const [searchQuery, setSearchQuery] = useState(''); - const [currentPage, setCurrentPage] = useState(1); - - useEffect(() => { - initCms(); - setItems(NEWS.map(newsToContentItem)); - setReady(true); - }, []); - - const filteredItems = useMemo(() => { - return items.filter((item) => { - const data = item.data as Record; - const category = (data.category as string) || ''; - const matchesCategory = selectedCategory === '全部' || category === selectedCategory; - const matchesSearch = - !searchQuery || - item.title.toLowerCase().includes(searchQuery.toLowerCase()) || - ((data.excerpt as string) || '').toLowerCase().includes(searchQuery.toLowerCase()); - return matchesCategory && matchesSearch; - }); - }, [items, selectedCategory, searchQuery]); - - const totalPages = Math.ceil(filteredItems.length / ITEMS_PER_PAGE); - const paginatedItems = filteredItems.slice( - (currentPage - 1) * ITEMS_PER_PAGE, - currentPage * ITEMS_PER_PAGE - ); - - useEffect(() => { - setCurrentPage(1); - }, [selectedCategory, searchQuery]); - - if (!ready) { - return ( -
-
加载中...
-
- ); - } - - const NewsCardRenderer = getItemRenderer('news'); - - return ( -
-
-
-
-
-
-
- - 新闻动态 -
-

- 最新动态 -

-

- 了解公司最新动态、行业洞察和技术分享,与我们一起成长。 -

-
-
- -
-
-
-
- {categories.map((cat) => ( - - ))} -
-
- setSearchQuery(e.target.value)} - className="w-full pl-10 pr-4 py-2.5 bg-white/[0.05] border border-white/[0.1] rounded-xl text-white text-sm placeholder-white/30 focus:outline-none focus:border-brand/50 focus:ring-1 focus:ring-brand/30 transition-colors" - /> - - - -
-
- - {paginatedItems.length > 0 ? ( -
- {paginatedItems.map((item, i) => - NewsCardRenderer ? ( - - ) : ( -
- {item.title} -
- ) - )} -
- ) : ( -
-
📭
-
暂无相关新闻
-
- )} - - {totalPages > 1 && ( -
- - {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( - - ))} - -
- )} -
-
-
- ); -} diff --git a/src/app/(marketing)/news/news-content-v2.tsx b/src/app/(marketing)/news/news-content-v2.tsx deleted file mode 100644 index 867cc29..0000000 --- a/src/app/(marketing)/news/news-content-v2.tsx +++ /dev/null @@ -1,268 +0,0 @@ -'use client'; - -import { useState, useMemo, ChangeEvent } from 'react'; -import { motion } from 'framer-motion'; -import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Input } from '@/components/ui/input'; -import { Search, Calendar, ArrowRight, Newspaper, ChevronLeft, ChevronRight } from 'lucide-react'; -import { COMPANY_INFO } from '@/lib/constants'; -import { type NewsItem } from '@/lib/constants/news'; -import { getMockItems } from '@/lib/cms/mock-data'; - -// CMS 数据源:优先使用 prop,回退到 mock-data -function getNewsData(newsProp?: NewsItem[]): NewsItem[] { - if (newsProp?.length) return newsProp; - return getMockItems('news').map( - (item) => ({ ...item.data, title: item.data.title || item.title } as unknown as NewsItem), - ); -} -import { SectionLabel, EASE_OUT } from '@/components/ui/page-decoration'; - -const categories = ['全部', '公司新闻', '研发动态']; -const ITEMS_PER_PAGE = 9; - -function NewsCard({ newsItem, index }: { newsItem: NewsItem; index: number }) { - return ( - - -
- - {newsItem.image ? ( -
- {newsItem.title} -
- ) : ( -
- -
- )} - -
-
- - {newsItem.category} - -
- - {newsItem.date} -
-
- -

- {newsItem.title} -

- -

- {newsItem.excerpt} -

- -
- 阅读全文 - -
-
-
- - ); -} - -export default function NewsContentV2({ news }: { news?: NewsItem[] }) { - const NEWS = getNewsData(news); - const [selectedCategory, setSelectedCategory] = useState('全部'); - const [searchQuery, setSearchQuery] = useState(''); - const [currentPage, setCurrentPage] = useState(1); - - const filteredNews = useMemo(() => { - return NEWS.filter((newsItem) => { - const matchesCategory = selectedCategory === '全部' || newsItem.category === selectedCategory; - const matchesSearch = - newsItem.title.toLowerCase().includes(searchQuery.toLowerCase()) || - newsItem.excerpt.toLowerCase().includes(searchQuery.toLowerCase()); - return matchesCategory && matchesSearch; - }); - }, [selectedCategory, searchQuery]); - - const totalPages = Math.ceil(filteredNews.length / ITEMS_PER_PAGE); - const paginatedNews = useMemo(() => { - const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; - return filteredNews.slice(startIndex, startIndex + ITEMS_PER_PAGE); - }, [filteredNews, currentPage]); - - const handlePageChange = (page: number) => { - setCurrentPage(page); - window.scrollTo({ top: 0, behavior: 'smooth' }); - }; - - const handleCategoryChange = (category: string) => { - setSelectedCategory(category); - setCurrentPage(1); - }; - - const handleSearchChange = (e: ChangeEvent) => { - setSearchQuery(e.target.value); - setCurrentPage(1); - }; - - return ( -
- {/* Hero Section */} -
-
-
- - News & Insights - - - -

- 新闻与 - 洞察 -

-
- - -

- {COMPANY_INFO.displayName}最新动态、产品更新与行业洞察。了解数字化趋势,把握技术脉搏。 -

-
-
-
-
- - {/* Filter + News List Section */} -
-
- -
- {/* Filters */} - -
- {categories.map((category) => ( - - ))} -
- -
- - -
-
- - {/* News Grid */} - {paginatedNews.length === 0 ? ( - -
- -

没有找到相关新闻

-

试试其他关键词或分类

-
-
- ) : ( - <> - - {paginatedNews.map((newsItem, index) => ( - - ))} - - - {/* Pagination */} - {totalPages > 1 && ( - -
- - {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( - - ))} - -
-
- )} - - )} -
-
- - {/* CTA Section */} -
-
- -
- - Get in Touch -

- 想了解更多? -

-

- 无论您有合作意向还是想探讨技术趋势,我们都欢迎与您交流。 -

- -
-
-
-
- ); -} diff --git a/src/app/(marketing)/products/[id]/page.test.tsx b/src/app/(marketing)/products/[id]/page.test.tsx index 74d8edb..1a3ae1d 100644 --- a/src/app/(marketing)/products/[id]/page.test.tsx +++ b/src/app/(marketing)/products/[id]/page.test.tsx @@ -67,48 +67,6 @@ jest.mock('@/lib/constants', () => ({ ], })); -jest.mock('@/lib/cms/mock-data', () => { - const testProduct = { - id: 'test-product', - title: '测试产品', - category: '企业旗舰系列', - categoryId: 'enterprise', - tags: ['测试', '示例'], - status: '研发中', - description: '这是测试产品描述', - overview: '这是测试产品概述', - features: ['功能1', '功能2'], - benefits: ['优势1', '优势2'], - process: ['步骤1', '步骤2'], - specs: ['规格1', '规格2'], - }; - const testItem = { - id: 'test-product', - modelId: 'model-product', - modelCode: 'product', - title: '测试产品', - slug: 'test-product', - status: 'published', - version: 1, - sortOrder: 0, - data: testProduct, - createdAt: '2024-01-01T00:00:00Z', - updatedAt: '2024-06-01T00:00:00Z', - publishedAt: '2024-06-01T00:00:00Z', - createdBy: 'system', - updatedBy: 'system', - }; - return { - getMockItems: jest.fn(() => [testItem]), - getMockItemById: jest.fn((_modelCode: string, id: string) => - id === 'test-product' ? testItem : undefined, - ), - getMockItemBySlug: jest.fn((_modelCode: string, slug: string) => - slug === 'test-product' ? testItem : undefined, - ), - }; -}); - describe('ProductDetailPage', () => { beforeEach(() => { jest.clearAllMocks(); diff --git a/src/app/(marketing)/products/products-content-cms.tsx b/src/app/(marketing)/products/products-content-cms.tsx deleted file mode 100644 index d3c2ec3..0000000 --- a/src/app/(marketing)/products/products-content-cms.tsx +++ /dev/null @@ -1,120 +0,0 @@ -'use client'; - -import { useState, useMemo, useEffect } from 'react'; -import { initCms, getItemRenderer } from '@/lib/cms'; -import type { ContentItem } from '@/lib/cms'; -import { PRODUCTS, PRODUCT_CATEGORIES } from '@/lib/constants'; - -function productToContentItem(product: unknown, index: number): ContentItem { - const p = product as Record; - return { - id: String(p.id), - modelId: 'product', - modelCode: 'product', - title: String(p.title), - slug: String(p.id), - status: 'published', - version: 1, - sortOrder: index, - data: p, - createdAt: '2024-01-01T00:00:00Z', - updatedAt: '2024-01-01T00:00:00Z', - publishedAt: '2024-01-01T00:00:00Z', - createdBy: 'system', - updatedBy: 'system', - }; -} - -export default function ProductsContentCms() { - const [ready, setReady] = useState(false); - const [items, setItems] = useState([]); - const [selectedCategory, setSelectedCategory] = useState('all'); - - useEffect(() => { - initCms(); - setItems(PRODUCTS.map(productToContentItem)); - setReady(true); - }, []); - - const filteredItems = useMemo(() => { - if (selectedCategory === 'all') return items; - return items.filter((item) => { - const data = item.data as Record; - return data.categoryId === selectedCategory; - }); - }, [items, selectedCategory]); - - if (!ready) { - return ( -
-
加载中...
-
- ); - } - - const ProductCardRenderer = getItemRenderer('product'); - - return ( -
-
-
-
-
-
-
- - 产品矩阵 -
-

- 产品中心 -

-

- 自主研发的企业级产品矩阵,覆盖数字化转型全场景需求。 -

-
-
- -
-
-
- - {PRODUCT_CATEGORIES.map((cat) => ( - - ))} -
- -
- {filteredItems.map((item, i) => - ProductCardRenderer ? ( - - ) : ( -
- {item.title} -
- ) - )} -
-
-
-
- ); -} diff --git a/src/app/(marketing)/products/products-content-v2.tsx b/src/app/(marketing)/products/products-content-v2.tsx deleted file mode 100644 index ddd4fef..0000000 --- a/src/app/(marketing)/products/products-content-v2.tsx +++ /dev/null @@ -1,484 +0,0 @@ -'use client'; - -import { useRef, useState, useEffect } from 'react'; -import { motion, useInView } from 'framer-motion'; -import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; -import { SectionLabel, EASE_OUT } from '@/components/ui/page-decoration'; -import { Button } from '@/components/ui/button'; -import { ArrowRight, Package, Cpu, BarChart3, Users, Settings, FileText, CheckCircle2 } from 'lucide-react'; -import { PRODUCT_CATEGORIES, type Product } from '@/lib/constants/products'; -import { getMockItems } from '@/lib/cms/mock-data'; - -// CMS 数据源:从 mock-data 读取产品数据 -const FALLBACK_PRODUCTS: Product[] = getMockItems('product').map( - (item) => item.data as unknown as Product, -); -import { useReducedMotion } from '@/hooks/use-reduced-motion'; -import { cn } from '@/lib/utils'; - -const EASE_SPRING = { type: 'spring', stiffness: 300, damping: 30 } as const; - -const PRODUCT_ICONS: Record = { - '睿新ERP管理系统': , - '睿视商业智能分析平台': , - '睿新客户关系管理系统': , - '睿新协同办公平台': , - '睿新内容管理系统': , - '睿新供应链数字化平台': , -}; - -function useCountUp(target: number, start: boolean, duration: number = 2000) { - const [count, setCount] = useState(0); - const prefersReducedMotion = useReducedMotion(); - - useEffect(() => { - if (!start || prefersReducedMotion) { - setCount(target); - return; - } - - const startTime = performance.now(); - let animationId: number; - - const animate = (currentTime: number) => { - const elapsed = currentTime - startTime; - const progress = Math.min(elapsed / duration, 1); - const easeOutQuart = 1 - Math.pow(1 - progress, 4); - setCount(Math.floor(target * easeOutQuart)); - - if (progress < 1) { - animationId = requestAnimationFrame(animate); - } else { - setCount(target); - } - }; - - animationId = requestAnimationFrame(animate); - return () => cancelAnimationFrame(animationId); - }, [target, start, duration, prefersReducedMotion]); - - return count; -} - -function StatItem({ value, suffix, label, start, delay }: { value: number; suffix: string; label: string; start: boolean; delay: number }) { - const count = useCountUp(value, start, 2000); - const shouldReduceMotion = useReducedMotion(); - - return ( - -
- {count}{suffix} -
-
{label}
-
- ); -} - -function HeroSection() { - const statsRef = useRef(null); - const isInView = useInView(statsRef, { once: true, margin: '-100px' }); - - return ( -
-
-
- - Product Matrix - - - -
覆盖企业数字化
-
- - 全场景的 - - -
-
产品矩阵
-
- - - 从数据智能到业务协同,每一款产品都经过实战验证。 - 6 大核心产品互为补充,常以组合形式出现在行业解决方案中。 - - - - - - - -
- - -
-
- 全 -
-
技术覆盖
-
-
-
-
-
- ); -} - -function ProductCard({ product, index }: { product: Product; index: number }) { - const productIcon = PRODUCT_ICONS[product.title] || ; - - const colorMap: Record = { - enterprise: { - color: '#C41E3A', - bg: 'bg-brand-soft', - border: 'group-hover:border-brand/30', - }, - specialized: { - color: '#3b82f6', - bg: 'bg-accent-blue-soft', - border: 'group-hover:border-accent-blue/30', - }, - }; - - const colors = colorMap[product.categoryId] ?? colorMap.enterprise!; - - return ( - -
- -
-
- - {productIcon} - - - {String(index + 1).padStart(2, '0')} - -
- -
- {product.tags.slice(0, 2).map(tag => ( - - {tag} - - ))} -
- -

- {product.title} -

- -

- {product.description} -

- -
- {product.features.slice(0, 3).map((feature) => ( - - - {feature} - - ))} -
- - - 了解详情 - - -
-
- ); -} - -function EnterpriseProductsSection({ products }: { products: Product[] }) { - const enterpriseProducts = products.filter(p => p.categoryId === 'enterprise'); - const category = PRODUCT_CATEGORIES.find(c => c.id === 'enterprise'); - - return ( -
-
-
- -
-
- -
-
- -
-
- Enterprise Suite -

- 企业套装 -

-
-
-

- {category?.description} -

-
-
- - - {enterpriseProducts.map((product, index) => ( - - ))} - -
-
- ); -} - -function SpecializedProductsSection({ products }: { products: Product[] }) { - const specializedProducts = products.filter(p => p.categoryId === 'specialized'); - const category = PRODUCT_CATEGORIES.find(c => c.id === 'specialized'); - - if (specializedProducts.length === 0) return null; - - return ( -
-
-
- -
-
- -
-
- -
-
- Specialized Products -

- 专业产品 -

-
-
-

- {category?.description} -

-
-
- - - {specializedProducts.map((product, index) => ( - - ))} - -
-
- ); -} - -function SuiteCombosSection({ products }: { products: Product[] }) { - const SUITE_COMBOS = [ - { products: ['erp', 'bi'], label: 'ERP + BI 数据驱动组合', description: '打通业务数据,实现数据驱动决策' }, - { products: ['crm', 'bi'], label: 'CRM + BI 客户洞察组合', description: '深度客户洞察,提升销售转化' }, - { products: ['erp', 'sds'], label: 'ERP + SDS 供应链优化组合', description: '供应链全链路数字化,降本增效' }, - { products: ['cms', 'oa'], label: 'CMS + OA 协同运营组合', description: '内容管理与办公协同一体化' }, - ]; - - return ( -
-
-
- -
- -
-
-
- - Recommended Combos - -
-
-
-

- 推荐产品组合 -

-

- 经过实战验证的产品组合,发挥 1+1 > 2 的协同效应 -

- - - - {SUITE_COMBOS.map((combo, idx) => { - const comboProducts = combo.products - .map(pid => products.find(p => p.id === pid)) - .filter(Boolean); - - return ( - - -
- ); -} - -function CTASection() { - return ( -
-
-
- -
- -
-
-
- - Get Started - -
-
-
- -

- 找到适合你的
- 数字化方案 -

- -

- 无论你是需要单一产品,还是完整的数字化转型方案, - 我们的专家团队都能为你量身定制最佳路径。 -

- - - -
-
- ); -} - -export default function ProductsContentV2({ products: productsProp }: { products?: Product[] }) { - const products = productsProp ?? FALLBACK_PRODUCTS; - - return ( -
- - - - - -
- ); -} diff --git a/src/app/(marketing)/services/services-content-cms.tsx b/src/app/(marketing)/services/services-content-cms.tsx deleted file mode 100644 index 22f1ed2..0000000 --- a/src/app/(marketing)/services/services-content-cms.tsx +++ /dev/null @@ -1,85 +0,0 @@ -'use client'; - -import { useState, useEffect } from 'react'; -import { initCms, getItemRenderer } from '@/lib/cms'; -import type { ContentItem } from '@/lib/cms'; -import { SERVICES } from '@/lib/constants'; - -function serviceToContentItem(service: unknown, index: number): ContentItem { - const s = service as Record; - return { - id: String(s.id), - modelId: 'service', - modelCode: 'service', - title: String(s.title), - slug: String(s.id), - status: 'published', - version: 1, - sortOrder: index, - data: s, - createdAt: '2024-01-01T00:00:00Z', - updatedAt: '2024-01-01T00:00:00Z', - publishedAt: '2024-01-01T00:00:00Z', - createdBy: 'system', - updatedBy: 'system', - }; -} - -export default function ServicesContentCms() { - const [ready, setReady] = useState(false); - const [items, setItems] = useState([]); - - useEffect(() => { - initCms(); - setItems(SERVICES.map(serviceToContentItem)); - setReady(true); - }, []); - - if (!ready) { - return ( -
-
加载中...
-
- ); - } - - const ServiceCardRenderer = getItemRenderer('service'); - - return ( -
-
-
-
-
-
-
- - 服务能力 -
-

- 服务体系 -

-

- 端到端的数字化服务能力,从战略咨询到落地实施,陪伴企业成长每一步。 -

-
-
- -
-
-
- {items.map((item, i) => - ServiceCardRenderer ? ( - - ) : ( -
- {item.title} -
- ) - )} -
-
-
-
- ); -} diff --git a/src/app/(marketing)/services/services-content-v1.tsx b/src/app/(marketing)/services/services-content-v1.tsx deleted file mode 100644 index 6e5b268..0000000 --- a/src/app/(marketing)/services/services-content-v1.tsx +++ /dev/null @@ -1,510 +0,0 @@ -'use client'; - -import { useRef } from 'react'; -import { motion } from 'framer-motion'; -import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; -import { Button } from '@/components/ui/button'; -import { ArrowRight, Code, BarChart3, Lightbulb, Puzzle, ClipboardList, PencilRuler, Rocket, HeartHandshake, Briefcase, RefreshCcw, Handshake, CheckCircle2 } from 'lucide-react'; -import { cn } from '@/lib/utils'; - -const EASE_OUT = [0.22, 1, 0.36, 1] as const; - -const SERVICES = [ - { - number: '01', - title: '软件开发', - subtitle: 'Software Development', - desc: '从需求分析到上线运维,全栈定制化开发。用扎实的工程能力交付高质量软件,确保每个项目都能创造真实业务价值。', - href: '/services/software', - icon: , - colorClass: 'text-brand', - bgClass: 'bg-brand-soft', - highlights: ['定制化开发', '全栈技术栈', '敏捷开发', '质量保证', '持续支持'], - metrics: [ - { value: '95%+', label: '准时交付率' }, - { value: 'A+', label: '代码质量' }, - ], - }, - { - number: '02', - title: '数据分析', - subtitle: 'Data Analytics', - desc: '让数据从"存着"变成"用着"。多源数据整合、可视化看板、预测模型,为经营决策提供可量化的洞察支撑。', - href: '/services/data', - icon: , - colorClass: 'text-accent-blue', - bgClass: 'bg-accent-blue-soft', - highlights: ['数据整合', '可视化分析', '预测模型', '实时分析', '洞察挖掘'], - metrics: [ - { value: '100+', label: '分析模型' }, - { value: '80%+', label: '洞察转化率' }, - ], - }, - { - number: '03', - title: '技术咨询', - subtitle: 'Tech Consulting', - desc: 'IT 战略规划、技术选型评估、数字化转型路线图——帮您理清方向,规避技术风险,减少试错成本。', - href: '/services/consulting', - icon: , - colorClass: 'text-accent-teal', - bgClass: 'bg-accent-teal-soft', - highlights: ['IT战略规划', '技术选型评估', '转型路线图', '架构评审', '团队建设'], - metrics: [ - { value: '90%+', label: '方案落地率' }, - { value: '50+', label: '咨询项目' }, - ], - }, - { - number: '04', - title: '行业方案实施', - subtitle: 'Industry Solutions', - desc: '深耕制造、零售、金融、医疗等行业,提供从咨询到落地的端到端交付。确保方案不只是PPT。', - href: '/services/solutions', - icon: , - colorClass: 'text-accent-amber', - bgClass: 'bg-accent-amber-soft', - highlights: ['行业深耕', '场景化方案', '快速交付', '生态整合', '持续迭代'], - metrics: [ - { value: '30+', label: '行业方案' }, - { value: '85%', label: '客户续约率' }, - ], - }, -]; - -const SERVICE_PROCESS = [ - { icon: ClipboardList, step: '01', title: '需求分析', desc: '深入了解业务场景与真实痛点,而非凭空假设' }, - { icon: PencilRuler, step: '02', title: '方案设计', desc: '量身定制技术路线,拒绝千篇一律的模板' }, - { icon: Rocket, step: '03', title: '敏捷交付', desc: '快速迭代,每个冲刺周期都有可交付成果' }, - { icon: HeartHandshake, step: '04', title: '持续支持', desc: '上线不是终点,长期陪伴才是真正的承诺' }, -]; - -const SERVICE_MODES = [ - { - icon: Briefcase, - title: '项目制', - description: '针对明确的需求和交付目标,以项目为单位进行合作。适合有清晰边界的一次性建设类项目。', - highlight: '固定范围 · 固定周期', - }, - { - icon: RefreshCcw, - title: '订阅制', - description: '按月或按年持续提供技术支持和迭代优化。适合需要长期维护、持续演进的产品和系统。', - highlight: '持续迭代 · 弹性调整', - }, - { - icon: Handshake, - title: '长期陪跑', - description: '以合作伙伴身份深度参与您的数字化进程,从规划到执行全程陪伴。适合正在系统性转型的企业。', - highlight: '深度参与 · 共同成长', - }, -]; - -function GrainOverlay() { - return ( -
- ); -} - -function SectionLabel({ children, className = '' }: { children: React.ReactNode; className?: string }) { - return ( -
-
- - {children} - -
- ); -} - -function HeroSection() { - const containerRef = useRef(null); - - return ( -
- - -
-
- -
- -
-
- -
-
- - Professional Services - - - -
从规划到运维的
-
- - 全程陪伴 - - -
-
- - - 不只是技术供应商,更是您数字化转型的同行者。 - 12 年行业深耕,我们深知每个项目背后都是真实的业务诉求。从第一次沟通到系统稳定运行,我们始终在您身边。 - - - - - - -
- - -
-
4
-
服务领域
-
-
-
3
-
合作模式
-
-
-
流程
-
服务保障
-
-
-
-
- ); -} - -function ServicesGridSection() { - return ( -
- - -
- -
-
- - Service Capabilities -

- 四大核心服务领域 -

-
- -

- 覆盖企业数字化全生命周期的专业能力,每一步都有资深专家陪伴。 -

-
-
- - - {SERVICES.map((service, i) => ( - - -
- ); -} - -function ProcessSection() { - return ( -
- - -
- - Our Process -

- 我们的做事方式 -

-

- 没有复杂的流程,只有清晰的四步:听懂需求、想好方案、快速交付、长期陪伴。 -

-
- -
-
- - - {SERVICE_PROCESS.map((item) => { - const Icon = item.icon; - return ( -
-
-
-
- -
-
- - - {item.step} - -
-

{item.title}

-

{item.desc}

-
-
- ); - })} - -
-
-
- ); -} - -function ModesSection() { - return ( -
- - -
- -
-
- - Engagement Models -

- 灵活的合作方式 -

-
- -

- 不同企业有不同节奏,我们提供三种合作模式,找到最适合您的那一种。 -

-
-
- - - {SERVICE_MODES.map((mode) => { - const Icon = mode.icon; - return ( -
-
- -
-
- -
- -

{mode.title}

- -

{mode.description}

- -
- - {mode.highlight} -
-
-
- ); - })} - -
-
- ); -} - -function CTASection() { - return ( -
- - -
-
-
- -
- - -
-
- - Get Started - -
-
- - -

- 聊聊您的需求 -

- -

- 无论是一个明确的项目,还是一个模糊的想法,我们都愿意坐下来和您一起理清楚。 - 首次咨询免费,没有任何销售压力。 -

- - - -
-
- ); -} - -export default function ServicesContentV1() { - return ( -
- - - - - -
- ); -} diff --git a/src/app/(marketing)/services/services-content-v2.tsx b/src/app/(marketing)/services/services-content-v2.tsx deleted file mode 100644 index dea3c0a..0000000 --- a/src/app/(marketing)/services/services-content-v2.tsx +++ /dev/null @@ -1,529 +0,0 @@ -'use client'; - -import { motion } from 'framer-motion'; -import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; -import { Button } from '@/components/ui/button'; -import { ArrowRight, Code, BarChart3, Lightbulb, Puzzle, ClipboardList, PencilRuler, Rocket, HeartHandshake, Briefcase, RefreshCcw, Handshake, CheckCircle2 } from 'lucide-react'; -import { GrainOverlay, FloatingInkParticles, SectionLabel, EASE_OUT } from '@/components/ui/page-decoration'; -import { cn } from '@/lib/utils'; - -const EASE_SPRING = { type: 'spring', stiffness: 300, damping: 30 } as const; - -const SERVICES_PARTICLES = [ - { x: 12, y: 35, scale: 0.8, duration: 10, delay: 0, size: 3 }, - { x: 85, y: 55, scale: 0.6, duration: 12, delay: 1.5, size: 2 }, - { x: 48, y: 45, scale: 1.0, duration: 9, delay: 0.8, size: 4 }, - { x: 70, y: 28, scale: 0.7, duration: 11, delay: 2.2, size: 2.5 }, - { x: 30, y: 78, scale: 0.9, duration: 8.5, delay: 1.0, size: 3.5 }, - { x: 65, y: 68, scale: 0.55, duration: 13, delay: 2.8, size: 2 }, -]; - -const SERVICES = [ - { - number: '01', - title: '软件开发', - subtitle: 'Software Development', - desc: '从需求分析到上线运维,全栈定制化开发。用扎实的工程能力交付高质量软件,确保每个项目都能创造真实业务价值。', - href: '/services/software', - icon: , - color: '#C41E3A', - colorClass: 'text-brand', - bgClass: 'bg-brand-soft', - highlights: ['定制化开发', '全栈技术栈', '敏捷开发', '质量保证', '持续支持'], - metrics: [ - { value: '95%+', label: '准时交付率' }, - { value: 'A+', label: '代码质量' }, - ], - }, - { - number: '02', - title: '数据分析', - subtitle: 'Data Analytics', - desc: '让数据从"存着"变成"用着"。多源数据整合、可视化看板、预测模型,为经营决策提供可量化的洞察支撑。', - href: '/services/data', - icon: , - color: '#3b82f6', - colorClass: 'text-accent-blue', - bgClass: 'bg-accent-blue-soft', - highlights: ['数据整合', '可视化分析', '预测模型', '实时分析', '洞察挖掘'], - metrics: [ - { value: '100+', label: '分析模型' }, - { value: '80%+', label: '洞察转化率' }, - ], - }, - { - number: '03', - title: '技术咨询', - subtitle: 'Tech Consulting', - desc: 'IT 战略规划、技术选型评估、数字化转型路线图——帮您理清方向,规避技术风险,减少试错成本。', - href: '/services/consulting', - icon: , - color: '#14b8a6', - colorClass: 'text-accent-teal', - bgClass: 'bg-accent-teal-soft', - highlights: ['IT战略规划', '技术选型评估', '转型路线图', '架构评审', '团队建设'], - metrics: [ - { value: '90%+', label: '方案落地率' }, - { value: '50+', label: '咨询项目' }, - ], - }, - { - number: '04', - title: '行业方案实施', - subtitle: 'Industry Solutions', - desc: '深耕制造、零售、金融、医疗等行业,提供从咨询到落地的端到端交付。确保方案不只是PPT。', - href: '/services/solutions', - icon: , - color: '#f59e0b', - colorClass: 'text-accent-amber', - bgClass: 'bg-accent-amber-soft', - highlights: ['行业深耕', '场景化方案', '快速交付', '生态整合', '持续迭代'], - metrics: [ - { value: '30+', label: '行业方案' }, - { value: '85%', label: '客户续约率' }, - ], - }, -]; - -const SERVICE_PROCESS = [ - { icon: ClipboardList, step: '01', title: '需求分析', desc: '深入了解业务场景与真实痛点,而非凭空假设' }, - { icon: PencilRuler, step: '02', title: '方案设计', desc: '量身定制技术路线,拒绝千篇一律的模板' }, - { icon: Rocket, step: '03', title: '敏捷交付', desc: '快速迭代,每个冲刺周期都有可交付成果' }, - { icon: HeartHandshake, step: '04', title: '持续支持', desc: '上线不是终点,长期陪伴才是真正的承诺' }, -]; - -const SERVICE_MODES = [ - { - icon: Briefcase, - title: '项目制', - description: '针对明确的需求和交付目标,以项目为单位进行合作。适合有清晰边界的一次性建设类项目。', - highlight: '固定范围 · 固定周期', - color: '#C41E3A', - }, - { - icon: RefreshCcw, - title: '订阅制', - description: '按月或按年持续提供技术支持和迭代优化。适合需要长期维护、持续演进的产品和系统。', - highlight: '持续迭代 · 弹性调整', - color: '#3b82f6', - }, - { - icon: Handshake, - title: '长期陪跑', - description: '以合作伙伴身份深度参与您的数字化进程,从规划到执行全程陪伴。适合正在系统性转型的企业。', - highlight: '深度参与 · 共同成长', - color: '#14b8a6', - }, -]; - - - -function HeroSection() { - return ( -
- - - -
-
- -
- -
- -
-
-
- -
-
- - Professional Services - - - -
从规划到运维的
-
- - 全程陪伴 - - -
-
- - - 不只是技术供应商,更是您数字化转型的同行者。 - 我们深知每个项目背后都是真实的业务诉求。从第一次沟通到系统稳定运行,我们始终在您身边。 - - - - - - - -
-
-
- 4 -
-
服务领域
-
-
-
- 3 -
-
合作模式
-
-
-
- 全流程 -
-
服务保障
-
-
-
-
-
- ); -} - -function ServicesGridSection() { - return ( -
- - -
-
- -
-
- - Service Capabilities -

- 四大核心服务领域 -

-
- -

- 覆盖企业数字化全生命周期的专业能力,每一步都有资深专家陪伴。 -

-
-
- - - {SERVICES.map((service, i) => ( - -
- ); -} - -function ProcessSection() { - return ( -
- - -
-
- -
- - Our Process -

- 我们的做事方式 -

-

- 没有复杂的流程,只有清晰的四步:听懂需求、想好方案、快速交付、长期陪伴。 -

-
- -
-
- - - {SERVICE_PROCESS.map((item) => { - const Icon = item.icon; - return ( -
-
-
-
- -
-
- - - {item.step} - -
-

{item.title}

-

{item.desc}

-
-
- ); - })} - -
-
-
- ); -} - -function ModesSection() { - return ( -
- - -
-
- -
-
- - Engagement Models -

- 灵活的合作方式 -

-
- -

- 不同企业有不同节奏,我们提供三种合作模式,找到最适合您的那一种。 -

-
-
- - - {SERVICE_MODES.map((mode) => { - const Icon = mode.icon; - return ( -
-
- -
- -
-
- -
- -

{mode.title}

- -

{mode.description}

- -
- - {mode.highlight} -
-
-
- ); - })} - -
-
- ); -} - -function CTASection() { - return ( -
- - - -
-
-
- -
-
-
- -
- -
-
-
- - Get Started - -
-
-
- -

- 聊聊您的
- 需求 -

- -

- 无论是一个明确的项目,还是一个模糊的想法,我们都愿意坐下来和您一起理清楚。 - 首次咨询免费,没有任何销售压力。 -

- - - -
-
- ); -} - -export default function ServicesContentV2() { - return ( -
- - - - - -
- ); -} diff --git a/src/app/(marketing)/solutions/solutions-content-cms.tsx b/src/app/(marketing)/solutions/solutions-content-cms.tsx deleted file mode 100644 index 7f57ecf..0000000 --- a/src/app/(marketing)/solutions/solutions-content-cms.tsx +++ /dev/null @@ -1,85 +0,0 @@ -'use client'; - -import { useState, useEffect } from 'react'; -import { initCms, getItemRenderer } from '@/lib/cms'; -import type { ContentItem } from '@/lib/cms'; -import { SOLUTIONS } from '@/lib/constants/solutions'; - -function solutionToContentItem(solution: unknown, index: number): ContentItem { - const s = solution as Record; - return { - id: String(s.id), - modelId: 'solution', - modelCode: 'solution', - title: String(s.title), - slug: String(s.id), - status: 'published', - version: 1, - sortOrder: index, - data: s, - createdAt: '2024-01-01T00:00:00Z', - updatedAt: '2024-01-01T00:00:00Z', - publishedAt: '2024-01-01T00:00:00Z', - createdBy: 'system', - updatedBy: 'system', - }; -} - -export default function SolutionsContentCms() { - const [ready, setReady] = useState(false); - const [items, setItems] = useState([]); - - useEffect(() => { - initCms(); - setItems(SOLUTIONS.map(solutionToContentItem)); - setReady(true); - }, []); - - if (!ready) { - return ( -
-
加载中...
-
- ); - } - - const SolutionCardRenderer = getItemRenderer('solution'); - - return ( -
-
-
-
-
-
-
- - 解决方案 -
-

- 行业解决方案 -

-

- 深入理解不同行业的业务特性,提供量身定制的数字化解决方案。 -

-
-
- -
-
-
- {items.map((item, i) => - SolutionCardRenderer ? ( - - ) : ( -
- {item.title} -
- ) - )} -
-
-
-
- ); -} diff --git a/src/app/(marketing)/solutions/solutions-content-v2.tsx b/src/app/(marketing)/solutions/solutions-content-v2.tsx deleted file mode 100644 index d168536..0000000 --- a/src/app/(marketing)/solutions/solutions-content-v2.tsx +++ /dev/null @@ -1,398 +0,0 @@ -'use client'; - -import { useRef, useState, useEffect } from 'react'; -import { motion, useInView } from 'framer-motion'; -import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; -import { GrainOverlay, FloatingInkParticles, SectionLabel, EASE_OUT } from '@/components/ui/page-decoration'; -import { Button } from '@/components/ui/button'; -import { ArrowRight, Factory, ShoppingCart, Heart, GraduationCap, Sparkles } from 'lucide-react'; -import { type Solution } from '@/lib/constants/solutions'; -import { PRODUCTS } from '@/lib/constants/products'; -import { getMockItems } from '@/lib/cms/mock-data'; - -// CMS 数据源:从 mock-data 读取解决方案数据 -const SOLUTIONS: Solution[] = getMockItems('solution').map( - (item) => item.data as unknown as Solution, -); -import { useReducedMotion } from '@/hooks/use-reduced-motion'; -import { cn } from '@/lib/utils'; - -const EASE_SPRING = { type: 'spring', stiffness: 300, damping: 30 } as const; - -const INDUSTRY_ICONS: Record = { - 制造业: , - 零售业: , - 医疗: , - 教育: , -}; - -const INDUSTRY_COLORS: Record = { - 制造业: { color: '#C41E3A', bg: 'bg-brand-soft', soft: 'rgba(196, 30, 58, 0.08)' }, - 零售业: { color: '#3b82f6', bg: 'bg-accent-blue-soft', soft: 'rgba(59, 130, 246, 0.08)' }, - 医疗: { color: '#14b8a6', bg: 'bg-accent-teal-soft', soft: 'rgba(20, 184, 166, 0.08)' }, - 教育: { color: '#f59e0b', bg: 'bg-accent-amber-soft', soft: 'rgba(245, 158, 11, 0.08)' }, -}; - -function useCountUp(target: number, start: boolean, duration: number = 2000) { - const [count, setCount] = useState(0); - const prefersReducedMotion = useReducedMotion(); - - useEffect(() => { - if (!start || prefersReducedMotion) { - setCount(target); - return; - } - - const startTime = performance.now(); - let animationId: number; - - const animate = (currentTime: number) => { - const elapsed = currentTime - startTime; - const progress = Math.min(elapsed / duration, 1); - const easeOutQuart = 1 - Math.pow(1 - progress, 4); - setCount(Math.floor(target * easeOutQuart)); - - if (progress < 1) { - animationId = requestAnimationFrame(animate); - } else { - setCount(target); - } - }; - - animationId = requestAnimationFrame(animate); - return () => cancelAnimationFrame(animationId); - }, [target, start, duration, prefersReducedMotion]); - - return count; -} - -const SOLUTION_PARTICLES = [ - { x: 18, y: 28, scale: 0.8, duration: 10, delay: 0, size: 3 }, - { x: 78, y: 62, scale: 0.6, duration: 12, delay: 1.5, size: 2 }, - { x: 50, y: 48, scale: 1.0, duration: 9, delay: 0.8, size: 4 }, - { x: 72, y: 30, scale: 0.7, duration: 11, delay: 2.2, size: 2.5 }, - { x: 28, y: 72, scale: 0.9, duration: 8.5, delay: 1.0, size: 3.5 }, - { x: 62, y: 70, scale: 0.55, duration: 13, delay: 2.8, size: 2 }, -]; - -function HeroSection() { - const statsRef = useRef(null); - const isInView = useInView(statsRef, { once: true, margin: '-100px' }); - const count4 = useCountUp(4, isInView, 1500); - - return ( -
- - - -
-
- -
- -
- -
-
-
- -
-
- - Industry Solutions - - - -
深耕行业场景的
-
- - 解决方案 - - -
-
- - - 端到端交付,推荐套装组合 + 配套服务包。 - 基于自研产品矩阵,为制造业、零售业、教育、医疗等行业量身定制最佳实践。 - - - - - - - -
-
-
- {count4}+ -
-
行业覆盖
-
-
-
- 端到 -
-
交付模式
-
-
-
- 定制 -
-
方案特点
-
-
-
-
-
- ); -} - -function SolutionCard({ solution, index }: { solution: Solution; index: number }) { - const industryIcon = INDUSTRY_ICONS[solution.industry] || ; - const colors = INDUSTRY_COLORS[solution.industry] ?? INDUSTRY_COLORS['制造业']!; - - return ( - -
- -
- -
-
-
- - {industryIcon} - -
- - {String(index + 1).padStart(2, '0')} - -
- -
- - {solution.industry} - -
- {solution.subtitle} -
-

- {solution.title} -

-
- -

- {solution.description} -

- -
- {solution.challenges.slice(0, 2).map((challenge, idx) => ( -
- - {challenge} -
- ))} -
- -
-

- - 推荐产品组合 -

-
- {solution.suiteCombination.primaryProducts.map(pid => { - const prod = PRODUCTS.find(p => p.id === pid); - return prod ? ( - - {prod.title.replace('睿新', '').replace('睿视 ', '')} - - ) : null; - })} - - + 配套服务 - -
-

- {solution.suiteCombination.rationale} -

-
- - - 了解详情 - - -
-
- ); -} - -function SolutionsGridSection() { - return ( -
- - -
-
- -
-
- - Industry Expertise -

- 行业解决方案 -

-
- -

- 每个方案都明确推荐产品组合和服务包,确保落地效果 -

-
-
- - - {SOLUTIONS.map((solution, index) => ( - - ))} - -
-
- ); -} - -function CTASection() { - return ( -
- - - -
-
-
- -
-
-
- -
- -
-
-
- - Get Started - -
-
-
- -

- 告诉我们您的
- 行业场景 -

- -

- 无论您处于哪个行业、哪个数字化阶段,我们都能为您推荐最合适的产品组合和服务包。 -

- - - -
-
- ); -} - -export default function SolutionsContentV2() { - return ( -
- - - -
- ); -} diff --git a/src/app/api/cms/revalidate/route.ts b/src/app/api/cms/revalidate/route.ts index e603b4a..8660654 100644 --- a/src/app/api/cms/revalidate/route.ts +++ b/src/app/api/cms/revalidate/route.ts @@ -1,9 +1,14 @@ import { NextRequest, NextResponse } from 'next/server'; import { revalidatePath, revalidateTag } from 'next/cache'; -import { - getContentTypeConfig, - getAllContentTypeConfigs, -} from '@/lib/cms/registry'; +import { CONTENT_TYPE_CONFIGS } from '@/lib/cms/content-types'; + +function getContentTypeConfig(code: string) { + return CONTENT_TYPE_CONFIGS[code as keyof typeof CONTENT_TYPE_CONFIGS] ?? null; +} + +function getAllContentTypeConfigs() { + return Object.values(CONTENT_TYPE_CONFIGS); +} interface RevalidateRequestBody { event?: string; diff --git a/src/components/cms/ContentEditor.tsx b/src/components/cms/ContentEditor.tsx deleted file mode 100644 index 702b29c..0000000 --- a/src/components/cms/ContentEditor.tsx +++ /dev/null @@ -1,473 +0,0 @@ -'use client'; - -import { useState, useMemo, useEffect, useCallback, createElement } from 'react'; -import { - getAllContentModels, - getItemRenderer, - ContentZoneRenderer, - getContentItems, - saveContentItem, - getContentZone, -} from '@/lib/cms'; -import type { ContentModel, ContentItem, FieldDefinition } from '@/lib/cms'; -import { RichTextEditor } from './RichTextEditor'; - -const PREVIEW_ZONES: { key: string; name: string }[] = [ - { key: 'home-stats', name: '首页 · 数据指标区' }, - { key: 'home-services', name: '首页 · 核心服务区' }, - { key: 'home-solutions', name: '首页 · 解决方案区' }, - { key: 'home-cases', name: '首页 · 客户案例区' }, - { key: 'home-news', name: '首页 · 新闻动态区' }, -]; - -function FieldEditor({ field, value, onChange }: { - field: FieldDefinition; - value: unknown; - onChange: (val: unknown) => void; -}) { - const baseClass = 'w-full px-3 py-2 bg-white/[0.05] border border-white/[0.1] rounded-lg text-white text-sm placeholder-white/30 focus:outline-none focus:border-brand/50 focus:ring-1 focus:ring-brand/30 transition-colors'; - - switch (field.type) { - case 'text': - return ( - onChange(e.target.value)} - /> - ); - case 'textarea': - return ( -