diff --git a/prisma/migrations/20260704024232_init/migration.sql b/prisma/migrations/20260704024232_init/migration.sql new file mode 100644 index 0000000..c4a8037 --- /dev/null +++ b/prisma/migrations/20260704024232_init/migration.sql @@ -0,0 +1,126 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL PRIMARY KEY, + "username" TEXT NOT NULL, + "password" TEXT NOT NULL, + "nickname" TEXT NOT NULL DEFAULT '', + "avatar" TEXT NOT NULL DEFAULT '', + "email" TEXT NOT NULL DEFAULT '', + "phone" TEXT NOT NULL DEFAULT '', + "status" INTEGER NOT NULL DEFAULT 1, + "role" TEXT NOT NULL DEFAULT 'admin', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "ContentModel" ( + "id" TEXT NOT NULL PRIMARY KEY, + "code" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL DEFAULT '', + "fields" TEXT NOT NULL, + "isPageType" BOOLEAN NOT NULL DEFAULT false, + "urlPattern" TEXT NOT NULL DEFAULT '', + "hasVersions" BOOLEAN NOT NULL DEFAULT false, + "hasDraft" BOOLEAN NOT NULL DEFAULT false, + "icon" TEXT NOT NULL DEFAULT '', + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "ContentItem" ( + "id" TEXT NOT NULL PRIMARY KEY, + "modelId" TEXT NOT NULL, + "modelCode" TEXT NOT NULL, + "title" TEXT NOT NULL, + "slug" TEXT NOT NULL DEFAULT '', + "status" TEXT NOT NULL DEFAULT 'draft', + "data" TEXT NOT NULL, + "version" INTEGER NOT NULL DEFAULT 1, + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "publishedAt" DATETIME, + "createdBy" TEXT NOT NULL DEFAULT 'system', + "updatedBy" TEXT NOT NULL DEFAULT 'system', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "ContentItem_modelId_fkey" FOREIGN KEY ("modelId") REFERENCES "ContentModel" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "ContentZone" ( + "id" TEXT NOT NULL PRIMARY KEY, + "code" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL DEFAULT '', + "pageCode" TEXT NOT NULL DEFAULT '', + "zoneKey" TEXT NOT NULL DEFAULT '', + "allowedModels" TEXT NOT NULL DEFAULT '[]', + "items" TEXT NOT NULL DEFAULT '[]', + "settings" TEXT NOT NULL DEFAULT '{}', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "MediaAsset" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "path" TEXT NOT NULL, + "url" TEXT NOT NULL, + "mimeType" TEXT NOT NULL, + "size" INTEGER NOT NULL DEFAULT 0, + "width" INTEGER NOT NULL DEFAULT 0, + "height" INTEGER NOT NULL DEFAULT 0, + "alt" TEXT NOT NULL DEFAULT '', + "storageType" TEXT NOT NULL DEFAULT 'local', + "createdBy" TEXT NOT NULL DEFAULT 'system', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "AuditLog" ( + "id" TEXT NOT NULL PRIMARY KEY, + "module" TEXT NOT NULL, + "targetId" TEXT NOT NULL, + "action" TEXT NOT NULL, + "operator" TEXT NOT NULL, + "beforeData" TEXT NOT NULL DEFAULT '{}', + "afterData" TEXT NOT NULL DEFAULT '{}', + "ip" TEXT NOT NULL DEFAULT '', + "userAgent" TEXT NOT NULL DEFAULT '', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); + +-- CreateIndex +CREATE UNIQUE INDEX "ContentModel_code_key" ON "ContentModel"("code"); + +-- CreateIndex +CREATE INDEX "ContentItem_modelCode_idx" ON "ContentItem"("modelCode"); + +-- CreateIndex +CREATE INDEX "ContentItem_status_idx" ON "ContentItem"("status"); + +-- CreateIndex +CREATE UNIQUE INDEX "ContentItem_modelCode_slug_key" ON "ContentItem"("modelCode", "slug"); + +-- CreateIndex +CREATE UNIQUE INDEX "ContentZone_code_key" ON "ContentZone"("code"); + +-- CreateIndex +CREATE INDEX "ContentZone_pageCode_idx" ON "ContentZone"("pageCode"); + +-- CreateIndex +CREATE INDEX "AuditLog_module_idx" ON "AuditLog"("module"); + +-- CreateIndex +CREATE INDEX "AuditLog_targetId_idx" ON "AuditLog"("targetId"); + +-- CreateIndex +CREATE INDEX "AuditLog_createdAt_idx" ON "AuditLog"("createdAt"); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..2a5a444 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "sqlite" diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..d927b2d --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,125 @@ +generator client { + provider = "prisma-client" + output = "../src/generated/prisma" +} + +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +// ============ 用户与认证 ============ + +model User { + id String @id @default(cuid()) + username String @unique + password String + nickname String @default("") + avatar String @default("") + email String @default("") + phone String @default("") + status Int @default(1) // 1=正常, 0=禁用 + role String @default("admin") // admin | editor + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +// ============ 内容模型定义 ============ + +model ContentModel { + id String @id @default(cuid()) + code String @unique + name String + description String @default("") + fields String // JSON: FieldDefinition[] + isPageType Boolean @default(false) + urlPattern String @default("") + hasVersions Boolean @default(false) + hasDraft Boolean @default(false) + icon String @default("") + sortOrder Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + items ContentItem[] +} + +// ============ 内容条目 ============ + +model ContentItem { + id String @id @default(cuid()) + modelId String + modelCode String + title String + slug String @default("") + status String @default("draft") // draft | published | archived + data String // JSON: Record + version Int @default(1) + sortOrder Int @default(0) + publishedAt DateTime? + createdBy String @default("system") + updatedBy String @default("system") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + model ContentModel @relation(fields: [modelId], references: [id]) + + @@unique([modelCode, slug]) + @@index([modelCode]) + @@index([status]) +} + +// ============ 内容区域 ============ + +model ContentZone { + id String @id @default(cuid()) + code String @unique + name String + description String @default("") + pageCode String @default("") + zoneKey String @default("") + allowedModels String @default("[]") // JSON: string[] + items String @default("[]") // JSON: ContentZoneItem[] + settings String @default("{}") // JSON: ContentZoneSettings + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([pageCode]) +} + +// ============ 媒体资源 ============ + +model MediaAsset { + id String @id @default(cuid()) + name String + path String + url String + mimeType String + size Int @default(0) + width Int @default(0) + height Int @default(0) + alt String @default("") + storageType String @default("local") // local | oss | s3 + createdBy String @default("system") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +// ============ 操作日志 ============ + +model AuditLog { + id String @id @default(cuid()) + module String // model | item | zone | theme | media + targetId String + action String // create | update | delete | publish | unpublish + operator String + beforeData String @default("{}") // JSON + afterData String @default("{}") // JSON + ip String @default("") + userAgent String @default("") + createdAt DateTime @default(now()) + + @@index([module]) + @@index([targetId]) + @@index([createdAt]) +} \ No newline at end of file diff --git a/prisma/seed.ts b/prisma/seed.ts new file mode 100644 index 0000000..cfe2954 --- /dev/null +++ b/prisma/seed.ts @@ -0,0 +1,601 @@ +import 'dotenv/config'; +import { PrismaClient } from '../src/generated/prisma/client'; +import { hashPassword } from '../src/lib/auth'; +import { CONTENT_TYPE_CONFIGS } from '../src/lib/cms/content-types'; +import { CASE_STUDIES } from '../src/lib/constants/cases'; +import { NEWS } from '../src/lib/constants/news'; +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'; + +const prisma = new PrismaClient(); + +async function getModelId(code: string): Promise { + const model = await prisma.contentModel.findUnique({ where: { code } }); + if (!model) throw new Error(`模型 ${code} 未找到`); + return model.id; +} + +async function seedItems( + modelCode: string, + items: Record[], + options: { slugField?: string; titleField?: string } = {}, +) { + const modelId = await getModelId(modelCode); + const slugField = options.slugField || 'slug'; + const titleField = options.titleField || 'title'; + + for (let i = 0; i < items.length; i++) { + 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; + + await prisma.contentItem.upsert({ + where: { modelCode_slug: { modelCode, slug } }, + update: { + title, + data: JSON.stringify(data), + sortOrder: i, + status: 'published', + publishedAt: new Date(), + }, + create: { + modelId, + modelCode, + title, + slug, + data: JSON.stringify(data), + sortOrder: i, + status: 'published', + publishedAt: new Date(), + createdBy: 'system', + updatedBy: 'system', + }, + }); + } +} + +async function main() { + console.log('🌱 开始数据库初始化...'); + + // ============ 1. 创建管理员用户 ============ + console.log('📝 创建管理员用户...'); + const adminPassword = await hashPassword('admin123'); + await prisma.user.upsert({ + where: { username: 'admin' }, + update: {}, + create: { + username: 'admin', + password: adminPassword, + nickname: '管理员', + role: 'admin', + status: 1, + }, + }); + console.log(' ✅ 管理员: admin / admin123'); + + // ============ 2. 创建内容模型 ============ + console.log('📝 创建内容模型...'); + const modelConfigs = [ + ...Object.values(CONTENT_TYPE_CONFIGS), + { + model: { + code: 'case-study', + name: '案例研究', + description: '客户成功案例与实践分享', + isPageType: true, + urlPattern: '/cases/{slug}', + hasVersions: true, + hasDraft: true, + icon: 'briefcase', + fields: [ + { name: 'client', type: 'text', label: '客户名称', required: true }, + { name: 'industry', type: 'text', label: '所属行业', required: true }, + { name: 'companySize', type: 'text', label: '公司规模', required: true }, + { name: 'subtitle', type: 'text', label: '副标题', required: true }, + { name: 'challenge', type: 'textarea', label: '挑战', required: true }, + { name: 'solution', type: 'textarea', label: '解决方案', required: true }, + { name: 'result', type: 'textarea', label: '实施成果', required: true }, + { name: 'color', type: 'text', label: '主题色', defaultValue: 'brand' }, + { name: 'featured', type: 'boolean', label: '精选案例', defaultValue: false }, + ], + }, + }, + // === about-page === + { + model: { + code: 'about-page', + name: '关于我们', + description: '公司介绍、核心价值、发展历程与资质认证信息', + isPageType: true, + urlPattern: '/about', + hasVersions: true, + hasDraft: true, + icon: 'building2', + fields: [ + { name: 'heroSubtitle', type: 'text', label: 'Hero 标签', required: true }, + { name: 'heroTitle', type: 'text', label: 'Hero 主标题', required: true }, + { name: 'heroDescription', type: 'textarea', label: 'Hero 描述', required: true }, + { name: 'whoWeAreTitle', type: 'text', label: '"我们是谁"标题' }, + { name: 'whoWeAreDescription', type: 'textarea', label: '"我们是谁"描述' }, + { name: 'promise', type: 'textarea', label: '承诺标语', required: true }, + { name: 'coreValues', type: 'array', label: '核心价值观', fields: [ + { name: 'number', type: 'text', label: '编号' }, + { name: 'title', type: 'text', label: '标题' }, + { name: 'description', type: 'textarea', label: '描述' }, + ]}, + { name: 'milestones', type: 'array', label: '发展历程', fields: [ + { name: 'year', type: 'text', label: '年份' }, + { name: 'title', type: 'text', label: '标题' }, + { name: 'description', type: 'textarea', label: '描述' }, + { name: 'highlight', type: 'text', label: '亮点' }, + ]}, + { name: 'keyMetrics', type: 'array', label: '关键指标', fields: [ + { name: 'value', type: 'text', label: '数值' }, + { name: 'label', type: 'text', label: '标签' }, + { name: 'sub', type: 'text', label: '副文本' }, + ]}, + { name: 'certifications', type: 'array', label: '资质认证', fields: [ + { name: 'name', type: 'text', label: '名称' }, + { name: 'desc', type: 'text', label: '描述' }, + { name: 'icon', type: 'text', label: '图标名称' }, + ]}, + { name: 'partners', type: 'array', label: '合作伙伴', fields: [ + { name: 'name', type: 'text', label: '名称' }, + ]}, + { name: 'ctaTitle', type: 'text', label: 'CTA 标题' }, + { name: 'ctaDescription', type: 'textarea', label: 'CTA 描述' }, + ], + }, + }, + // === team-page === + { + model: { + code: 'team-page', + name: '团队介绍', + description: '团队实力、文化氛围、数据指标', + isPageType: true, + urlPattern: '/team', + hasVersions: true, + hasDraft: true, + icon: 'users', + fields: [ + { name: 'heroSubtitle', type: 'text', label: 'Hero 标签', required: true }, + { name: 'heroTitle', type: 'textarea', label: 'Hero 主标题', required: true }, + { name: 'heroDescription', type: 'textarea', label: 'Hero 描述', required: true }, + { name: 'stats', type: 'array', label: '统计指标', fields: [ + { name: 'value', type: 'number', label: '数值' }, + { name: 'suffix', type: 'text', label: '后缀' }, + { name: 'label', type: 'text', label: '标签' }, + ]}, + { name: 'strengths', type: 'array', label: '团队优势', fields: [ + { name: 'icon', type: 'text', label: '图标名称' }, + { name: 'number', type: 'text', label: '编号' }, + { name: 'title', type: 'text', label: '标题' }, + { name: 'description', type: 'textarea', label: '描述' }, + ]}, + { name: 'culture', type: 'array', label: '团队文化', fields: [ + { name: 'icon', type: 'text', label: '图标名称' }, + { name: 'number', type: 'text', label: '编号' }, + { name: 'title', type: 'text', label: '标题' }, + { name: 'description', type: 'textarea', label: '描述' }, + ]}, + { name: 'teamAboutTitle', type: 'text', label: '团队介绍标题' }, + { name: 'ctaTitle', type: 'text', label: 'CTA 标题' }, + { name: 'ctaDescription', type: 'textarea', label: 'CTA 描述' }, + ], + }, + }, + // === contact-page === + { + model: { + code: 'contact-page', + name: '联系我们', + description: '联系信息、工作时间与服务承诺', + isPageType: true, + urlPattern: '/contact', + hasVersions: true, + hasDraft: true, + icon: 'phone', + fields: [ + { name: 'heroSubtitle', type: 'text', label: 'Hero 标签', required: true }, + { name: 'heroTitle', type: 'textarea', label: 'Hero 主标题', required: true }, + { name: 'heroDescription', type: 'textarea', label: 'Hero 描述', required: true }, + { name: 'companyName', type: 'text', label: '公司名称', required: true }, + { name: 'companyEmail', type: 'text', label: '联系邮箱', required: true }, + { name: 'companyAddress', type: 'text', label: '公司地址', required: true }, + { name: 'workHoursWeekday', type: 'text', label: '工作日描述' }, + { name: 'workHoursTime', type: 'text', label: '工作时间段' }, + { name: 'promises', type: 'array', label: '服务承诺', fields: [ + { name: 'text', type: 'textarea', label: '承诺内容' }, + ]}, + { name: 'ctaTitle', type: 'text', label: 'CTA 标题' }, + { name: 'ctaDescription', type: 'textarea', label: 'CTA 描述' }, + ], + }, + }, + // === legal-page === + { + model: { + code: 'legal-page', + name: '法律条款', + description: '隐私政策和服务条款的页面内容', + isPageType: true, + urlPattern: '/legal/{slug}', + hasVersions: true, + hasDraft: true, + icon: 'file-text', + fields: [ + { name: 'pageTitle', type: 'text', label: '页面标题', required: true }, + { name: 'pageSubtitle', type: 'text', label: '页面副标题' }, + { name: 'sections', type: 'array', label: '内容章节', fields: [ + { name: 'title', type: 'text', label: '章节标题' }, + { name: 'content', type: 'textarea', label: '章节内容' }, + ]}, + { name: 'lastUpdated', type: 'text', label: '最后更新日期' }, + ], + }, + }, + ]; + + for (let i = 0; i < modelConfigs.length; i++) { + const config = modelConfigs[i]!; + await prisma.contentModel.upsert({ + where: { code: config.model.code }, + update: { + name: config.model.name, + description: config.model.description, + fields: JSON.stringify(config.model.fields), + isPageType: config.model.isPageType, + urlPattern: config.model.urlPattern || '', + hasVersions: config.model.hasVersions, + hasDraft: config.model.hasDraft, + icon: config.model.icon || '', + sortOrder: i, + }, + create: { + code: config.model.code, + name: config.model.name, + description: config.model.description, + fields: JSON.stringify(config.model.fields), + isPageType: config.model.isPageType, + urlPattern: config.model.urlPattern || '', + hasVersions: config.model.hasVersions, + hasDraft: config.model.hasDraft, + icon: config.model.icon || '', + sortOrder: i, + }, + }); + console.log(` ✅ 模型: ${config.model.name} (${config.model.code})`); + } + + // ============ 3. 导入内容数据 ============ + console.log('📝 导入案例研究...'); + await seedItems('case-study', CASE_STUDIES as unknown as Record[]); + console.log(` ✅ ${CASE_STUDIES.length} 个案例`); + + console.log('📝 导入新闻...'); + await seedItems('news', NEWS as unknown as Record[]); + console.log(` ✅ ${NEWS.length} 条新闻`); + + console.log('📝 导入服务...'); + await seedItems('service', SERVICES as unknown as Record[]); + console.log(` ✅ ${SERVICES.length} 个服务`); + + console.log('📝 导入产品...'); + await seedItems('product', PRODUCTS as unknown as Record[]); + console.log(` ✅ ${PRODUCTS.length} 个产品`); + + console.log('📝 导入解决方案...'); + await seedItems('solution', SOLUTIONS as unknown as Record[]); + console.log(` ✅ ${SOLUTIONS.length} 个解决方案`); + + // 数据指标需要特殊处理 + console.log('📝 导入数据指标...'); + const statList = Array.isArray(STATS) ? STATS : (STATS as { stats?: unknown[] }).stats || []; + const statModelId = await getModelId('stat-item'); + for (let i = 0; i < statList.length; i++) { + const s = statList[i] as Record; + const slug = `stat-${i}`; + const title = String(s.label || `指标 ${i + 1}`); + const { id, ...data } = s; + await prisma.contentItem.upsert({ + where: { modelCode_slug: { modelCode: 'stat-item', slug } }, + update: { + title, + data: JSON.stringify({ ...data, accentColor: s.color }), + sortOrder: i, + status: 'published', + publishedAt: new Date(), + }, + create: { + modelId: statModelId, + modelCode: 'stat-item', + title, + slug, + data: JSON.stringify({ ...data, accentColor: s.color }), + sortOrder: i, + status: 'published', + publishedAt: new Date(), + createdBy: 'system', + updatedBy: 'system', + }, + }); + } + console.log(` ✅ ${statList.length} 个数据指标`); + + // ============ 4. 导入页面内容 ============ + console.log('📝 导入关于页面...'); + await seedItems('about-page', [ + { + slug: 'default', + title: '关于我们', + heroSubtitle: 'About Us', + heroTitle: '12 年深耕\n500+ 企业\n的转型伙伴', + heroDescription: '我们不把项目完成当作终点。您的业务增长了吗?您的团队能力提升了吗?\n这才是我们真正在意的事。', + heroCtaText: '预约咨询', + heroCtaLink: '/contact', + heroSecondaryCtaText: '了解服务', + heroSecondaryCtaLink: '/services', + whoWeAreTitle: '我们是谁', + whoWeAreDescription: '我们坚持对行业趋势的深度研究,不追逐昙花一现的概念。每一次方案,都源于对您业务场景的洞察;每一次连接,都为了让技术真正服务于您的未来。', + promise: '不卖您用不上的技术,不说不懂业务的术语,不做路过就忘的一锤子买卖。', + coreValues: [ + { number: '01', title: '结果导向', description: '不以「项目上线」为终点,以「客户业务是否真正改善」为衡量标准。每一次交付,都追求可量化的价值。' }, + { number: '02', title: '长期主义', description: '不追逐风口,只做真正为客户创造价值的事。您的下一次难题,还愿意第一个想到我们——这才是我们追求的成功。' }, + { number: '03', title: '技术驱动', description: '用扎实的工程能力和行业经验赢得信任。技术不是炫技的工具,而是解决真实问题的手段。' }, + ], + milestones: [ + { year: '2014', title: '团队启航', description: '核心团队始于大型企业数字化转型项目,深耕 ERP、CRM 实施与定制开发。', highlight: '服务首批 20+ 企业客户' }, + { year: '2018', title: '业务扩展', description: '从单一实施服务向全链路咨询转型,建立战略咨询 + 技术落地的双轮驱动模式。', highlight: '客户突破 100 家' }, + { year: '2021', title: '产品化起步', description: '开始自研产品矩阵,从项目制向「产品+服务」双模式升级。', highlight: '3 款自研产品上线' }, + { year: '2023', title: '行业深耕', description: '聚焦制造、零售、医疗三大核心行业,建立行业解决方案库与最佳实践沉淀。', highlight: '客户续约率 95%+' }, + { year: '2026', title: '睿新致远成立', description: '四川睿新致远科技有限公司在成都正式成立,以全新品牌开启下一段征程。', highlight: '全新启航,步履不停' }, + ], + keyMetrics: [ + { value: '500+', label: '服务企业', sub: '六大行业覆盖' }, + { value: '200+', label: '专业顾问', sub: '平均行业经验 8 年' }, + { value: '98%', label: '客户满意度', sub: '97% 续约率' }, + { value: '12年', label: '行业深耕', sub: 'since 2014' }, + ], + certifications: [ + { name: 'ISO 27001', description: '信息安全管理体系认证', icon: 'Shield' }, + { name: 'ISO 9001', description: '质量管理体系认证', icon: 'Award' }, + { name: 'CMMI 3级', description: '软件能力成熟度认证', icon: 'Zap' }, + { name: '高新技术企业', description: '国家高新技术企业认定', icon: 'Trophy' }, + ], + partners: [ + 'SAP', 'Oracle', 'Salesforce', 'Microsoft', + '阿里云', '腾讯云', '华为云', '金蝶', + ], + ctaTitle: '准备好开始了吗?', + ctaDescription: '无论您是想了解合作机会,还是希望深入探讨具体方案,都欢迎随时联系。', + }, + ]); + console.log(' ✅ 关于页面'); + + console.log('📝 导入团队页面...'); + await seedItems('team-page', [ + { + slug: 'default', + title: '团队介绍', + heroSubtitle: 'Our Team', + heroTitle: '既懂技术又懂业务的\n复合型团队', + heroDescription: '我们的成员不只是写代码的工程师——我们是能理解您业务场景、能和您一起想清楚问题的合作伙伴。', + heroCtaText: '与我们交流', + heroCtaLink: '/contact', + heroSecondaryCtaText: '了解公司', + heroSecondaryCtaLink: '/about', + stats: [ + { value: 12, suffix: '+', label: '年行业经验' }, + { value: 10, suffix: '+', label: '核心成员' }, + { value: 5, suffix: '+', label: '行业覆盖' }, + ], + strengths: [ + { icon: 'Shield', number: '01', title: '12 年+ 行业深耕', description: '核心团队长期从事技术咨询、企业数字化等领域,服务覆盖金融、制造、零售、政务、农业等多个行业。' }, + { icon: 'Building2', number: '02', title: '大型 IT 企业背景', description: '开发团队成员来自多个大型传统 IT 企业,具备扎实的工程能力、规范化的交付流程和严格的质量意识。' }, + { icon: 'Users', number: '03', title: '复合型技术团队', description: '既懂技术又懂业务,能够深入理解客户的真实场景和痛点,提供真正可落地的解决方案。' }, + { icon: 'Code', number: '04', title: '全栈技术能力', description: '从前端到后端、从云原生到数据智能、从移动端到物联网——应对各种复杂技术挑战。' }, + { icon: 'Target', number: '05', title: '结果导向交付', description: '不以"项目上线"为终点,以"客户业务是否真正改善"为衡量标准。每一个交付成果都追求可量化的业务价值。' }, + ], + culture: [ + { icon: 'Layers', number: '01', title: '扁平化协作', description: '没有冗长的汇报链条,每个人都有机会直接参与决策。信息透明,沟通高效。' }, + { icon: 'BookOpen', number: '02', title: '持续学习', description: '技术日新月异,我们保持对新技术的好奇。内部分享、技术沙龙、外部培训——学习是日常的一部分。' }, + { icon: 'Sparkles', number: '03', title: '客户成功', description: '我们的成就感来自客户的成功。客户的业务增长,就是我们最好的成绩单。' }, + ], + teamAboutTitle: '关于我们的团队', + teamAboutParagraphs: [ + '我们的核心团队长期从事技术咨询、企业数字化等行业,拥有 12 年以上的深厚积累。', + '开发团队成员来自于多个大型传统 IT 企业,具备扎实的工程能力和规范化的交付经验。', + '我们相信,优秀的技术咨询不仅需要过硬的技术能力,更需要深入理解客户的业务场景和真实需求。每一位成员都是既懂技术又懂业务的复合型人才。', + ], + strengthsSectionTitle: '我们的底气,从何而来', + strengthsSectionDescription: '不是自吹自擂,而是多年实战积累的真实能力', + cultureSectionTitle: '我们怎样工作', + cultureSectionDescription: '好的团队文化,最终体现在为客户创造的价值上', + ctaTitle: '想成为我们的一员?', + ctaDescription: '我们始终在寻找既懂技术又热爱业务的伙伴。如果您认同我们的理念,欢迎聊聊。', + ctaPrimaryText: '联系我们', + ctaPrimaryLink: '/contact', + ctaSecondaryText: '了解公司', + ctaSecondaryLink: '/about', + }, + ]); + console.log(' ✅ 团队页面'); + + console.log('📝 导入联系页面...'); + await seedItems('contact-page', [ + { + slug: 'default', + title: '联系我们', + heroSubtitle: 'Contact Us', + heroTitle: '随时欢迎\n聊一聊', + heroDescription: '工作日 2 小时内快速响应,首次咨询免费。\n没有销售压力,只是坐下来把问题理清楚。', + companyName: '四川睿新致远科技有限公司', + companyEmail: 'contact@novalon.cn', + companyAddress: '中国四川省成都市龙泉驿区幸福路12号', + workHoursWeekday: '周一至周五', + workHoursTime: '9:00 - 18:00', + promises: [ + { text: '工作日 2 小时内快速响应您的咨询' }, + { text: '提供免费的业务咨询和方案评估服务' }, + { text: '根据您的需求量身定制最优解决方案' }, + ], + formTitle: '发送消息', + ctaTitle: '还是想直接聊?', + ctaDescription: '表单填起来太麻烦?直接发邮件到 contact@novalon.cn,我们同样会快速回复。', + }, + ]); + console.log(' ✅ 联系页面'); + + console.log('📝 导入法律页面...'); + await seedItems('legal-page', [ + { + slug: 'default', + title: '法律条款', + pageSubtitle: '隐私政策与服务条款', + lastUpdated: '2026-02-26', + privacyPolicy: { + title: '隐私政策', + subtitle: '我们重视您的隐私,致力于保护您的个人信息安全', + lastUpdated: '2026-02-26', + sections: [ + { title: '引言', content: '四川睿新致远科技有限公司(以下简称"我们"、"公司")深知个人信息对您的重要性,并会尽全力保护您的个人信息安全可靠。我们致力于维持您对我们的信任,恪守以下原则,保护您的个人信息:权责一致原则、目的明确原则、选择同意原则、最少够用原则、确保安全原则、主体参与原则、公开透明原则等。本隐私政策适用于您通过四川睿新致远科技有限公司官方网站、移动应用、产品服务等渠道访问和使用我们的产品和服务时,我们收集和使用您的个人信息的情形。' }, + { title: '一、我们如何收集和使用您的个人信息', content: '我们会遵循正当、合法、必要的原则,仅为实现产品功能,向您提供服务之目的,收集和使用您的个人信息。\n\n1.1 我们收集的个人信息:\n• 账户信息:当您注册账户时,我们可能收集您的姓名、电子邮箱地址、手机号码、公司名称等。\n• 联系信息:当您通过联系表单或客服与我们沟通时,我们可能收集您的姓名、电子邮箱地址、手机号码、留言内容等。\n• 使用信息:我们可能收集您使用我们产品和服务的信息,包括访问时间、浏览记录、操作日志等。\n• 设备信息:我们可能收集您使用的设备信息,包括设备型号、操作系统、浏览器类型等。\n\n1.2 我们如何使用您的个人信息:\n• 提供产品和服务:向您提供我们的产品和服务,处理您的请求和订单。\n• 客户服务:与您联系,提供客户支持,回复您的咨询和反馈。\n• 改进产品和服务:分析使用情况,改进我们的产品和服务质量。\n• 安全保障:检测、预防和处理安全事件,保护您和我们的合法权益。\n• 法律合规:遵守法律法规要求,履行法律义务。' }, + { title: '二、我们如何共享、转让、公开披露您的个人信息', content: '2.1 共享:我们不会向其他任何公司、组织和个人分享您的个人信息,但以下情况除外:在获取明确同意的情况下分享;根据法律法规、法律程序、强制性的行政或司法要求所必须的情况进行提供;在涉及合并、收购或破产清算时,如涉及到个人信息转让,我们会要求新的持有您个人信息的公司、组织继续受本隐私政策的约束。\n\n2.2 转让:我们不会将您的个人信息转让给任何公司、组织和个人,但以下情况除外:在获取明确同意的情况下转让;根据适用的法律法规、法律程序、强制性的行政或司法要求所必须的情况进行提供;在涉及合并、收购或破产清算时。\n\n2.3 公开披露:我们仅会在以下情况下公开披露您的个人信息:获得您的明确同意;基于法律法规或法律程序的要求;在涉及合并、收购或破产清算时;为保护我们或他人的合法权益免受损害。' }, + { title: '三、我们如何保护您的个人信息', content: '我们已使用符合业界标准的安全防护措施保护您提供的个人信息,防止数据遭到未经授权访问、公开披露、使用、修改、损坏或丢失。我们会采取一切合理可行的措施,保护您的个人信息:\n• 使用加密技术确保数据传输和存储安全。\n• 限制访问权限,仅授权人员可访问个人信息。\n• 定期进行安全审计和风险评估。\n• 建立数据备份和恢复机制。\n• 制定应急响应预案,及时处理安全事件。' }, + { title: '四、您的权利', content: '按照中国相关的法律、法规、标准,以及其他国家、地区的通行做法,我们保障您对自己的个人信息行使以下权利:访问您的个人信息;更正您的个人信息;删除您的个人信息;改变您授权同意的范围;注销您的账户;获取您的个人信息副本。如您需要行使上述权利,请通过本隐私政策提供的联系方式与我们联系。' }, + { title: '五、未成年人保护', content: '我们非常重视对未成年人个人信息的保护。如果您是18周岁以下的未成年人,在使用我们的产品和服务前,应事先取得您家长或法定监护人的同意。如您是未成年人的监护人,当您对您所监护的未成年人的个人信息处理存在疑问时,请通过本隐私政策提供的联系方式与我们联系。' }, + { title: '六、隐私政策的更新', content: '我们可能适时更新本隐私政策的条款,该等更新构成本隐私政策的一部分。如该等更新造成您在本隐私政策下权利的实质减少,我们将在更新生效前通过在主页上显著位置提示或向您发送电子邮件或其他方式通知您,在该种情况下,若您继续使用我们的服务,即表示同意受经修订的本隐私政策的约束。' }, + { title: '七、Cookie 和网站分析工具', content: '7.1 Cookie 使用说明:我们使用 Cookie 和类似技术来提供、保护和改进我们的服务。Cookie 是存储在您设备上的小型文本文件,帮助我们识别您的设备、记住您的偏好设置。\n\nCookie类型及用途:\n• 必要Cookie:网站基本功能运行,会话期间,必需\n• 分析Cookie:了解网站使用情况改进服务,14个月,非必需\n• 营销Cookie:个性化广告(当前未使用),非必需\n\n7.2 Google Analytics 使用说明:我们使用 Google Analytics 4(由 Google LLC 提供)分析网站使用情况。收集数据包括:访问的页面和停留时间;设备类型、浏览器类型;地理位置(国家/城市级别,IP地址已匿名化);访问来源。\n已采取的保护措施:IP地址匿名化;数据保留期限设为14个月;禁用广告个性化功能;禁用Google信号;不与Google其他服务共享数据用于广告目的。\n\n7.3 您的选择:您可以在首次访问时选择接受或拒绝分析Cookie;可以点击"Cookie设置"按钮随时更改偏好;可以通过浏览器设置删除或阻止Cookie。\n\n7.4 数据删除请求:如您希望删除我们持有的您的个人数据,请通过 privacy@novalon.cn 联系我们。联系地址:中国四川省成都市龙泉驿区幸福路12号。' }, + { title: '八、如何联系我们', content: '如果您对本隐私政策有任何疑问、意见或建议,或需要行使您的权利,请通过以下方式与我们联系:\n\n公司名称:四川睿新致远科技有限公司\n联系邮箱:contact@novalon.cn\n隐私邮箱:privacy@novalon.cn\n联系地址:中国四川省成都市龙泉驿区幸福路12号' }, + ], + }, + termsOfService: { + title: '服务条款', + subtitle: '请仔细阅读以下服务条款,使用我们的服务即表示您同意这些条款', + lastUpdated: '2026-02-26', + sections: [ + { title: '引言', content: '欢迎使用四川睿新致远科技有限公司(以下简称"我们"、"公司")提供的产品和服务。在使用我们的产品和服务之前,请您仔细阅读并理解本服务条款。如果您不同意本服务条款的任何内容,请停止使用我们的产品和服务。本服务条款是您与四川睿新致远科技有限公司之间就使用我们的产品和服务所订立的协议。我们有权根据需要不时修改本服务条款,修改后的条款一旦公布即代替原条款,恕不另行通知。' }, + { title: '一、服务内容', content: '我们提供的产品和服务包括但不限于:软件开发、云服务、数据分析、信息安全、企业级软件产品(如ERP、CRM、后台管理系统、BI等)等。具体服务内容以我们官方网站或产品文档为准。我们保留随时修改、暂停或终止部分或全部服务的权利,无需事先通知。对于因服务修改、暂停或终止而给您造成的任何损失,我们不承担任何责任,除非法律另有规定。' }, + { title: '二、用户注册与账户', content: '2.1 注册资格:您确认,在您完成注册程序或以其他方式实际使用本服务时,您应当是具备完全民事权利能力和完全民事行为能力的自然人、法人或其他组织。若您不具备前述主体资格,则您及您的监护人应承担因此而导致的一切后果,且我们有权注销或永久冻结您的账户。\n\n2.2 账户安全:您有责任维护您账户的安全性和保密性。您不得向任何第三方泄露您的账户信息,也不得与他人共享您的账户。如果您发现任何未经授权使用您账户的情况,应立即通知我们。您对您账户下发生的所有活动负责。我们对因您未能维护账户安全而造成的任何损失不承担责任。' }, + { title: '三、用户行为规范', content: '在使用我们的产品和服务时,您同意遵守以下行为规范:\n• 遵守所有适用的法律法规和本服务条款。\n• 不得利用我们的产品和服务进行任何违法或不当活动。\n• 不得干扰或破坏我们的产品和服务或与我们的产品和服务相连的服务器和网络。\n• 不得上传、传播或存储任何违法、有害、威胁、诽谤、骚扰、侵权或其他不当内容。\n• 不得侵犯他人的知识产权、隐私权或其他合法权益。\n• 不得进行任何形式的商业欺诈或诈骗活动。\n• 不得恶意收集或获取其他用户的信息。\n如果我们认定您违反了本服务条款或任何适用法律,我们有权在不事先通知的情况下,暂停或终止您的账户,并拒绝您现在或将来使用我们的产品和服务。' }, + { title: '四、知识产权', content: '我们的产品和服务中包含的所有内容,包括但不限于软件、设计、文字、图片、音频、视频、商标、服务标识等,均受著作权法、商标法、专利法或其他适用法律的保护。除非另有明确说明,我们拥有或持有我们产品和服务中所有内容的所有知识产权。您不得以任何形式复制、修改、传播、展示、执行、创作衍生作品、转让、出售或以其他方式使用这些内容,除非获得我们的明确书面许可。您在使用我们的产品和服务时产生的任何内容,您仍保留其知识产权,但您授予我们全球性、非独占性、免版税的许可,以使用、复制、修改、传播、展示和执行这些内容,仅限于提供和改进我们的产品和服务。' }, + { title: '五、服务费用与支付', content: '5.1 费用标准:我们的产品和服务可能需要支付费用。具体费用标准以我们官方网站或产品文档公布的价格为准。我们保留随时调整价格的权利,调整后的价格适用于调整后的新订单或续费。\n\n5.2 支付方式:我们接受多种支付方式,包括但不限于银行转账、支付宝、微信支付等。\n\n5.3 退款政策:除非另有明确说明,我们提供的产品和服务一经售出,不予退款。如因我们的原因导致产品或服务无法正常使用,我们将根据实际情况提供相应的补偿或解决方案。' }, + { title: '六、免责声明', content: '我们的产品和服务按"现状"和"可用"基础提供,不提供任何明示或暗示的保证,包括但不限于对适销性、适用性、非侵权性或准确性、可靠性的保证。\n\n我们不对以下情况承担责任:\n• 因您违反本服务条款或任何适用法律而导致的任何损失或损害。\n• 因不可抗力、网络故障、设备故障等不可控因素导致的服务中断或数据丢失。\n• 因第三方服务或内容导致的任何损失或损害。\n• 因您使用或无法使用我们的产品和服务而导致的任何间接、附带、特殊或后果性损害。' }, + { title: '七、服务终止', content: '您可以随时停止使用我们的产品和服务,并注销您的账户。我们也有权在不事先通知的情况下,因以下原因暂停或终止您的账户:\n• 您违反本服务条款或任何适用法律。\n• 我们出于安全、法律或商业考虑,认为有必要终止您的账户。\n• 您长时间未使用您的账户。\n• 我们停止提供相关的产品和服务。' }, + { title: '八、争议解决', content: '本服务条款的订立、执行、解释及争议解决均适用中华人民共和国法律。如就本服务条款发生任何争议,双方应首先通过友好协商解决;协商不成的,任何一方均可向公司所在地有管辖权的人民法院提起诉讼。' }, + { title: '九、其他条款', content: '• 本服务条款构成您与我们就使用我们的产品和服务所达成的完整协议,取代之前的所有口头或书面协议。\n• 如本服务条款的任何条款被认定为无效或不可执行,该条款应被限制或排除,以使其有效和可执行,其余条款继续有效。\n• 我们未行使或延迟行使本服务条款项下的任何权利或规定,不构成对该权利或规定的放弃。\n• 本服务条款的标题仅为方便而设,不影响本服务条款的解释。' }, + { title: '十、联系我们', content: '如果您对本服务条款有任何疑问、意见或建议,请通过以下方式与我们联系:\n\n公司名称:四川睿新致远科技有限公司\n联系邮箱:contact@novalon.cn\n联系地址:中国四川省成都市龙泉驿区幸福路12号' }, + ], + }, + }, + ]); + console.log(' ✅ 法律页面'); + + // ============ 5. 创建默认内容区域 ============ + console.log('📝 创建默认内容区域...'); + const defaultZones = [ + { + code: 'home-stats', + name: '首页 - 数据指标', + pageCode: 'home', + zoneKey: 'stats', + allowedModels: ['stat-item'], + items: statList.slice(0, 4).map((_: unknown, i: number) => ({ + itemId: `stat-${i}`, + sortOrder: i, + variant: 'default', + })), + }, + { + code: 'home-services', + name: '首页 - 服务展示', + pageCode: 'home', + zoneKey: 'services', + allowedModels: ['service'], + items: SERVICES.slice(0, 6).map((_: unknown, i: number) => { + const s = _ as Record; + return { itemId: String(s.id || `service-${i}`), sortOrder: i, variant: 'default' }; + }), + }, + { + code: 'home-solutions', + name: '首页 - 解决方案', + pageCode: 'home', + zoneKey: 'solutions', + allowedModels: ['solution'], + items: SOLUTIONS.slice(0, 6).map((_: unknown, i: number) => { + const s = _ as Record; + return { itemId: String(s.id || `solution-${i}`), sortOrder: i, variant: 'default' }; + }), + }, + { + code: 'home-cases', + name: '首页 - 精选案例', + pageCode: 'home', + zoneKey: 'cases', + allowedModels: ['case-study'], + items: CASE_STUDIES.filter((cs) => cs.featured).slice(0, 3).map((cs, i: number) => ({ + itemId: cs.id, + sortOrder: i, + variant: 'default', + })), + }, + { + code: 'home-news', + name: '首页 - 新闻动态', + pageCode: 'home', + zoneKey: 'news', + allowedModels: ['news'], + items: NEWS.slice(0, 3).map((_: unknown, i: number) => { + const n = _ as Record; + return { itemId: String(n.id || `news-${i}`), sortOrder: i, variant: 'default' }; + }), + }, + ]; + + for (const zone of defaultZones) { + await prisma.contentZone.upsert({ + where: { code: zone.code }, + update: { + name: zone.name, + pageCode: zone.pageCode, + zoneKey: zone.zoneKey, + allowedModels: JSON.stringify(zone.allowedModels), + items: JSON.stringify(zone.items), + }, + create: { + code: zone.code, + name: zone.name, + pageCode: zone.pageCode, + zoneKey: zone.zoneKey, + allowedModels: JSON.stringify(zone.allowedModels), + items: JSON.stringify(zone.items), + }, + }); + console.log(` ✅ 区域: ${zone.name}`); + } + + // ============ 统计 ============ + const itemCount = await prisma.contentItem.count(); + const modelCount = await prisma.contentModel.count(); + const zoneCount = await prisma.contentZone.count(); + + console.log('\n🎉 数据库初始化完成!'); + console.log(` 📊 内容模型: ${modelCount}`); + console.log(` 📄 内容条目: ${itemCount}`); + console.log(` 🗂️ 内容区域: ${zoneCount}`); + console.log(` 👤 管理员: admin / admin123`); +} + +main() + .catch((e) => { + console.error('❌ 种子数据初始化失败:', e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); \ No newline at end of file diff --git a/src/app/(marketing)/about/about-content-v1.tsx b/src/app/(marketing)/about/about-content-v1.tsx new file mode 100644 index 0000000..dc31683 --- /dev/null +++ b/src/app/(marketing)/about/about-content-v1.tsx @@ -0,0 +1,359 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; +import { Button } from '@/components/ui/button'; +import { ArrowRight, Target, Heart, Lightbulb, CheckCircle2 } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +const EASE_OUT = [0.22, 1, 0.36, 1] as const; + +function GrainOverlay() { + return ( +
+ ); +} + +function SectionLabel({ children, className = '' }: { children: React.ReactNode; className?: string }) { + return ( +
+
+ + {children} + +
+
+ ); +} + +const CORE_VALUES = [ + { + icon: Target, + number: '01', + title: '结果导向', + description: '不以"项目上线"为终点,以"客户业务是否真正改善"为衡量标准。每一次交付,都追求可量化的价值。', + }, + { + icon: Heart, + number: '02', + title: '长期主义', + description: '不追逐风口,只做真正为客户创造价值的事。您的下一次难题,还愿意第一个想到我们——这才是我们追求的成功。', + }, + { + icon: Lightbulb, + number: '03', + title: '技术驱动', + description: '用扎实的工程能力和行业经验赢得信任。技术不是炫技的工具,而是解决真实问题的手段。', + }, +]; + +const MILESTONES = [ + { date: '2026.01', title: '公司成立', description: '四川睿新致远科技有限公司在成都龙泉驿区正式成立' }, + { date: '2026.01', title: '团队组建', description: '核心团队到位,成员来自多个大型 IT 企业,具备扎实的工程能力和规范化交付经验' }, + { date: '2026.02', title: '业务启动', description: '推出企业数字化转型咨询与解决方案服务,开始接触首批客户' }, + { date: '2026.03', title: '产品研发', description: '自主研发 ERP、CRM 等核心产品,逐步构建产品矩阵' }, + { date: '2026.05', title: '研发推进', description: '多款产品进入核心功能开发阶段,同步开展早期用户体验计划' }, +]; + +const PROMISES = [ + '不卖您用不上的技术', + '不说不懂业务的术语', + '不做路过就忘的"一锤子买卖"', +]; + +export default function AboutContentV1() { + return ( +
+ {/* Hero Section */} +
+ + +
+ +
+ + +
+
+ + About Us + +
+
+ + + + +

+ 企业数字化转型的 + 同行者 +

+
+ + +

+ 以智慧连接数字趋势,以伙伴身份陪您成长。我们不把项目完成当作终点——您的业务增长了吗?您的团队能力提升了吗?这才是我们在意的事。 +

+
+ + + + + + +
+
+
12+
+
年行业深耕
+
+
+
6 款
+
自研产品
+
+
+
10+
+
人核心团队
+
+
+
+
+
+ + {/* Brand Story Section */} +
+ + +
+ + +
+
+ + Brand Story + +
+
+ +

+ 智连未来,成长伙伴 +

+ + +
+ + +
01 / 智连未来
+

智连未来

+
+

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

+

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

+

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

+
+
+ + +
02 / 成长伙伴
+

成长伙伴

+
+

我们不把“项目完成”当作终点。

+

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

+

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

+
+
+
+
+ + +
+

我们承诺:

+
+ {PROMISES.map((promise) => ( +
+ + {promise} +
+ ))} +
+

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

+
+
+
+
+ + {/* Core Values Section */} +
+ + +
+ + +
+
+ + Core Values + +
+
+ +

+ 我们坚信的事 +

+

+ 这些不是墙上的标语,而是每一个决策背后的原则 +

+ + +
+ + {CORE_VALUES.map((value) => { + const Icon = value.icon; + return ( + +
+ +
+
+ +
+ {value.number} +
+ +

{value.title}

+

{value.description}

+ + ); + })} + +
+
+
+ + {/* Milestones Section */} +
+ + +
+ + +
+
+ + Milestones + +
+
+ +

+ 一路走来,步步扎实 +

+ + + +
+
+ +
+ {MILESTONES.map((milestone, idx) => { + const isLatest = idx === MILESTONES.length - 1; + return ( + +
+ {milestone.date} +
+ +
+
+ {isLatest && ( +
+ )} +
+ +
+
+

{milestone.title}

+ {isLatest && ( + + 进行中 + + )} +
+

{milestone.description}

+
+ + ); + })} +
+
+ +
+
+ + {/* CTA Section */} +
+ + +
+ +
+ +

+ 与我们同行 +

+

+ 无论您是想了解合作机会,还是希望加入我们的团队,都欢迎随时联系。 +

+ +
+
+
+
+ ); +} diff --git a/src/app/(marketing)/about/about-content-v2.tsx b/src/app/(marketing)/about/about-content-v2.tsx new file mode 100644 index 0000000..371adfc --- /dev/null +++ b/src/app/(marketing)/about/about-content-v2.tsx @@ -0,0 +1,382 @@ +'use client'; + +import { useRef, useState, useEffect } from 'react'; +import { motion, useInView } from 'framer-motion'; +import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; +import { ArrowRight, Target, Heart, Lightbulb, CheckCircle2 } from 'lucide-react'; +import { GrainOverlay, FloatingInkParticles, SectionLabel, EASE_OUT } from '@/components/ui/page-decoration'; +import { useReducedMotion } from '@/hooks/use-reduced-motion'; + +const CORE_VALUES = [ + { + icon: Target, + number: '01', + title: '结果导向', + description: '不以"项目上线"为终点,以"客户业务是否真正改善"为衡量标准。每一次交付,都追求可量化的价值。', + }, + { + icon: Heart, + number: '02', + title: '长期主义', + description: '不追逐风口,只做真正为客户创造价值的事。您的下一次难题,还愿意第一个想到我们——这才是我们追求的成功。', + }, + { + icon: Lightbulb, + number: '03', + title: '技术驱动', + description: '用扎实的工程能力和行业经验赢得信任。技术不是炫技的工具,而是解决真实问题的手段。', + }, +]; + +const MILESTONES = [ + { date: '2026.01', title: '公司成立', description: '四川睿新致远科技有限公司在成都龙泉驿区正式成立' }, + { date: '2026.01', title: '团队组建', description: '核心团队到位,成员来自多个大型 IT 企业,具备扎实的工程能力和规范化交付经验' }, + { date: '2026.02', title: '业务启动', description: '推出企业数字化转型咨询与解决方案服务,开始接触首批客户' }, + { date: '2026.03', title: '产品研发', description: '自主研发 ERP、CRM 等核心产品,逐步构建产品矩阵' }, + { date: '2026.05', title: '研发推进', description: '多款产品进入核心功能开发阶段,同步开展早期用户体验计划' }, +]; + +const PROMISES = [ + '不卖您用不上的技术', + '不说不懂业务的术语', + '不做路过就忘的"一锤子买卖"', +]; + +const STATS = [ + { value: 12, suffix: '+', label: '年行业深耕' }, + { value: 6, suffix: '款', label: '自研产品' }, + { value: 10, suffix: '+', label: '人核心团队' }, +]; + +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 ValueCard({ value, index }: { value: typeof CORE_VALUES[0]; index: number }) { + const Icon = value.icon; + const shouldReduceMotion = useReducedMotion(); + + return ( + +
+ +
+
+
+ +
+ {value.number} +
+ +

{value.title}

+

{value.description}

+
+ +
+ + ); +} + +export default function AboutContentV2() { + const statsRef = useRef(null); + const isInView = useInView(statsRef, { once: true, margin: '-100px' }); + + return ( +
+ {/* Hero Section */} +
+ + + +
+ +
+
+ + About Us + + + +

+ 企业数字化转型的 + 同行者 +

+
+ + +

+ 以智慧连接数字趋势,以伙伴身份陪您成长。我们不把项目完成当作终点——您的业务增长了吗?您的团队能力提升了吗?这才是我们在意的事。 +

+
+ + + + + +
+ {STATS.map((stat, idx) => ( + + ))} +
+
+
+
+ + {/* Brand Story Section */} +
+ + +
+ + Brand Story +

+ 智连未来,成长伙伴 +

+
+ +
+ + +
+
01 / 智连未来
+

智连未来

+
+

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

+

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

+

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

+
+ + + +
+
02 / 成长伙伴
+

成长伙伴

+
+

我们不把「项目完成」当作终点。

+

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

+

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

+
+ + +
+ + +
+

我们承诺:

+
+ {PROMISES.map((promise) => ( +
+ + {promise} +
+ ))} +
+

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

+
+
+
+
+ + {/* Core Values Section */} +
+ + +
+ + Core Values +

+ 我们坚信的事 +

+

+ 这些不是墙上的标语,而是每一个决策背后的原则 +

+
+ +
+ {CORE_VALUES.map((value, idx) => ( + + ))} +
+
+
+ + {/* Milestones Section */} +
+ + +
+ + Milestones +

+ 一路走来,步步扎实 +

+
+ + +
+
+ +
+ {MILESTONES.map((milestone, idx) => { + const isLatest = idx === MILESTONES.length - 1; + return ( + +
+ {milestone.date} +
+ +
+
+ {isLatest && ( +
+ )} +
+ +
+
+

{milestone.title}

+ {isLatest && ( + + 进行中 + + )} +
+

{milestone.description}

+
+ + ); + })} +
+
+ +
+
+ + {/* CTA Section */} +
+ + + +
+ +
+ +

+ 与我们同行 +

+

+ 无论您是想了解合作机会,还是希望加入我们的团队,都欢迎随时联系。 +

+ +
+
+
+
+ ); +} diff --git a/src/app/(marketing)/about/about-content-v3.tsx b/src/app/(marketing)/about/about-content-v3.tsx new file mode 100644 index 0000000..f1d75bc --- /dev/null +++ b/src/app/(marketing)/about/about-content-v3.tsx @@ -0,0 +1,443 @@ +'use client'; + +import { + Trophy, Users, Briefcase, Award, + Target, Heart, Lightbulb, CheckCircle2, + Building2, Shield, Zap, TrendingUp, +} from 'lucide-react'; +import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; +import { MetricCard } from '@/components/ui/metric-card'; +import { MilestoneTimeline } from '@/components/ui/milestone-timeline'; +import { GrainOverlay, FloatingInkParticles, SectionLabel } from '@/components/ui/page-decoration'; + +const CORE_VALUES = [ + { + icon: Target, + number: '01', + title: '结果导向', + description: '不以「项目上线」为终点,以「客户业务是否真正改善」为衡量标准。每一次交付,都追求可量化的价值。', + }, + { + icon: Heart, + number: '02', + title: '长期主义', + description: '不追逐风口,只做真正为客户创造价值的事。您的下一次难题,还愿意第一个想到我们——这才是我们追求的成功。', + }, + { + icon: Lightbulb, + number: '03', + title: '技术驱动', + description: '用扎实的工程能力和行业经验赢得信任。技术不是炫技的工具,而是解决真实问题的手段。', + }, +]; + +const MILESTONES = [ + { + year: '2014', + title: '团队启航', + description: '核心团队始于大型企业数字化转型项目,深耕 ERP、CRM 实施与定制开发,积累了扎实的工程交付能力。', + highlight: '服务首批 20+ 企业客户', + }, + { + year: '2018', + title: '业务扩展', + description: '从单一实施服务向全链路咨询转型,建立战略咨询 + 技术落地的双轮驱动模式。', + highlight: '客户突破 100 家', + }, + { + year: '2021', + title: '产品化起步', + description: '开始自研产品矩阵,从项目制向「产品+服务」双模式升级,覆盖更多成长型企业需求。', + highlight: '3 款自研产品上线', + }, + { + year: '2023', + title: '行业深耕', + description: '聚焦制造、零售、医疗三大核心行业,建立行业解决方案库与最佳实践沉淀。', + highlight: '客户续约率 95%+', + }, + { + year: '2026', + title: '睿新致远成立', + description: '四川睿新致远科技有限公司在成都正式成立,以全新品牌开启下一段征程。', + highlight: '全新启航,步履不停', + }, +]; + +const PROMISES = [ + '不卖您用不上的技术', + '不说不懂业务的术语', + '不做路过就忘的「一锤子买卖」', +]; + +const KEY_METRICS = [ + { + icon: , + label: '服务企业', + value: 500, + suffix: '+', + description: '覆盖制造、零售、医疗、教育等六大行业', + trend: { value: '23% YoY', direction: 'up' as const, label: '增长' }, + accentColor: 'brand' as const, + }, + { + icon: , + label: '专业顾问', + value: 200, + suffix: '+', + description: '10年以上资深顾问占比 40%,平均行业经验 8 年', + trend: { value: '年均 30+', direction: 'up' as const, label: '增长' }, + accentColor: 'blue' as const, + }, + { + icon: , + label: '客户满意度', + value: 98, + suffix: '%', + description: '80% 客户合作超过 3 年,续约率 97%', + trend: { value: '97% 续约率', direction: 'up' as const, label: '' }, + accentColor: 'teal' as const, + }, + { + icon: , + label: '行业经验', + value: 12, + suffix: '年', + description: '跨越四轮技术变革周期,持续进化', + trend: { value: 'since 2014', direction: 'neutral' as const, label: '' }, + accentColor: 'amber' as const, + }, +]; + +const CERTIFICATIONS = [ + { name: 'ISO 27001', desc: '信息安全管理体系认证', icon: Shield }, + { name: 'ISO 9001', desc: '质量管理体系认证', icon: Award }, + { name: 'CMMI 3级', desc: '软件能力成熟度认证', icon: Zap }, + { name: '高新技术企业', desc: '国家高新技术企业认定', icon: Trophy }, +]; + +const PARTNERS = [ + 'SAP', 'Oracle', 'Salesforce', 'Microsoft', + '阿里云', '腾讯云', '华为云', '金蝶', +]; + +function ValueCard({ value, index: _index }: { value: typeof CORE_VALUES[0]; index: number }) { + const Icon = value.icon; + + return ( +
+
+ +
+
+
+ +
+ {value.number} +
+ +

{value.title}

+

{value.description}

+
+ +
+
+ ); +} + +export default function AboutContentV3() { + return ( +
+ {/* Hero Section */} +
+ + + +
+ +
+
+ + About Us + + + +

+ 企业数字化转型的 + 同行者 +

+
+ + +

+ 十二年深耕,五百余家企业的共同选择。我们不把项目完成当作终点——您的业务增长了吗?您的团队能力提升了吗?这才是我们在意的事。 +

+
+ + + + +
+
+
+ + {/* Key Metrics Section */} +
+ + +
+
+ +
+ + By The Numbers +

+ 用数据说话的专业实力 +

+

+ 十二年积累,每一个数字背后都是真实的客户价值与扎实的交付承诺 +

+
+ +
+ {KEY_METRICS.map((metric, i) => ( + +
+ +
+
+ ))} +
+
+
+ + {/* Brand Story Section */} +
+ + +
+
+ + Brand Story +

+ 智连未来,
成长伙伴 +

+

+ 我们坚持对行业趋势的深度研究,不追逐昙花一现的概念。每一次方案,都源于对您业务场景的洞察;每一次连接,都为了让技术真正服务于您的未来。 +

+
+
+ +
+
+
三不原则
+
我们对客户的承诺
+
+
+
+ +
+ +
+ {PROMISES.map((promise, i) => ( +
+
+
+
+ +
+

{promise}

+
+
+ ))} +
+

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

+
+
+ +
+
+
+
+ + {/* Core Values Section */} +
+ + +
+ + Core Values +

+ 我们坚信的事 +

+

+ 这些不是墙上的标语,而是每一个决策背后的原则 +

+
+ +
+ {CORE_VALUES.map((value, idx) => ( + + + + ))} +
+
+
+ + {/* Milestones Section */} +
+ + +
+ + Our Journey +

+ 一路走来,步步扎实 +

+

+ 从 2014 到 2026,十二年只是起点 +

+
+ +
+ ({ + year: m.year, + title: m.title, + description: m.description, + highlight: m.highlight, + }))} + variant="vertical" + theme="dark" + /> +
+
+
+ + {/* Certifications & Partners Section */} +
+ + +
+
+ +
+
+ + Certifications +

+ 资质与认证 +

+
+ {CERTIFICATIONS.map((cert) => { + const Icon = cert.icon; + return ( +
+
+ +
+
{cert.name}
+
{cert.desc}
+
+ ); + })} +
+
+ + + Partners +

+ 生态合作伙伴 +

+
+ {PARTNERS.map((partner) => ( +
+ + {partner} + +
+ ))} +
+

+ 与全球顶尖的软件厂商和云服务提供商建立深度合作,确保为客户提供最优质的技术方案与服务保障。 +

+
+
+
+
+ + {/* CTA Section */} +
+ + + +
+ +
+ +

+ 与我们同行 +

+

+ 无论您是想了解合作机会,还是希望加入我们的团队,都欢迎随时联系。 +

+ +
+
+
+
+ ); +} diff --git a/src/app/(marketing)/about/about-content-v4.tsx b/src/app/(marketing)/about/about-content-v4.tsx new file mode 100644 index 0000000..d0e8583 --- /dev/null +++ b/src/app/(marketing)/about/about-content-v4.tsx @@ -0,0 +1,420 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { + Trophy, + Shield, Award, Zap, + ArrowUpRight, + type LucideIcon, +} from 'lucide-react'; + +const EASE_OUT = [0.22, 1, 0.36, 1] as const; + +/** Icon name-to-component mapping for CMS string-based icons */ +const ICON_MAP: Record = { + Shield, + Award, + Zap, + Trophy, +}; + +const DEFAULT_CERT_ICON_NAMES = ['Shield', 'Award', 'Zap', 'Trophy'] as const; + +interface AboutData { + heroSubtitle?: string; + heroTitle?: string; + heroDescription?: string; + heroPrimaryCtaLabel?: string; + heroPrimaryCtaHref?: string; + heroSecondaryCtaLabel?: string; + heroSecondaryCtaHref?: string; + whoWeAreTitle?: string; + whoWeAreDescription?: string; + promiseText?: string; + coreValues?: { number: string; title: string; description: string }[]; + milestones?: { year: string; title: string; description: string; highlight: string }[]; + keyMetrics?: { value: string; label: string; sub: string }[]; + certifications?: { name: string; description: string }[]; + partners?: string[]; + ctaTitle?: string; + ctaDescription?: string; + ctaPrimaryLabel?: string; + ctaPrimaryHref?: string; + ctaSecondaryLabel?: string; + ctaSecondaryHref?: string; +} + +function HeroSection({ data }: { data: AboutData }) { + const titleLines = (data.heroTitle || '').split('\n'); + + return ( +
+
+
+ + {data.heroSubtitle || ''} + + + + {titleLines.map((line, i) => ( +
0 ? 'mt-4 sm:mt-6' : ''}`}> + {i === 1 ? ( + + + {line} + + + + ) : ( + line + )} +
+ ))} +
+ + + {data.heroDescription || ''} + + + + + {data.heroPrimaryCtaLabel || ''} + + + + {data.heroSecondaryCtaLabel || ''} + + +
+
+
+ ); +} + +function MetricsStrip({ data }: { data: AboutData }) { + const metrics = data.keyMetrics ?? []; + + if (metrics.length === 0) return null; + + return ( +
+
+
+ +
+
+ {metrics.map((metric, i) => ( + +
+ {metric.value} +
+
{metric.label}
+
{metric.sub}
+
+ ))} +
+
+
+ ); +} + +function WhoWeAreSection({ data }: { data: AboutData }) { + const values = data.coreValues ?? []; + + return ( +
+
+
+ +
+ Who We Are +
+

+ {data.whoWeAreTitle || ''} +

+

+ {data.whoWeAreDescription || ''} +

+
+

+ {data.promiseText || ''} +

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

+ {value.title} +

+

{value.description}

+
+
+
+ ))} +
+
+
+
+
+ ); +} + +function MilestonesSection({ data }: { data: AboutData }) { + const milestones = data.milestones ?? []; + + return ( +
+
+ +
+ Our Journey +
+

+ 一路走来,
步步扎实 +

+
+ +
+ {milestones.map((milestone, idx) => ( + +
+
+ +
+
+
+ {milestone.year} +
+
+
+

{milestone.title}

+

{milestone.description}

+
{milestone.highlight}
+
+
+ + ))} +
+
+
+ ); +} + +function CertificationsSection({ data }: { data: AboutData }) { + const certifications = data.certifications ?? []; + const partners = data.partners ?? []; + + if (certifications.length === 0 && partners.length === 0) return null; + + return ( +
+
+
+ {certifications.length > 0 && ( + +
+ Certifications +
+

+ 资质与认证 +

+
+ {certifications.map((cert, i) => { + const iconName = DEFAULT_CERT_ICON_NAMES[i % DEFAULT_CERT_ICON_NAMES.length] ?? 'Shield'; + const Icon = ICON_MAP[iconName] ?? Shield; + return ( +
+
+ +
+
{cert.name}
+
{cert.description}
+
+ ); + })} +
+
+ )} + + {partners.length > 0 && ( + +
+ Partners +
+

+ 生态合作伙伴 +

+
+ {partners.map((partner) => ( +
+ + {partner} + +
+ ))} +
+

+ 与全球顶尖的软件厂商和云服务提供商建立深度合作,确保为客户提供最优质的技术方案与服务保障。 +

+
+ )} +
+
+
+ ); +} + +function CTASection({ data }: { data: AboutData }) { + return ( +
+
+ +
+ +

+ {data.ctaTitle || ''} +

+

+ {data.ctaDescription || ''} +

+ +
+
+
+ ); +} + +export default function AboutContentV4({ data }: { data: Record }) { + const aboutData = data as AboutData; + + // Empty state when no CMS data is available + if (!data || Object.keys(data).length === 0) { + return ( +
+
内容暂未发布
+
+ ); + } + + return ( +
+ + + + + + +
+ ); +} diff --git a/src/app/(marketing)/cases/[slug]/client.tsx b/src/app/(marketing)/cases/[slug]/client.tsx new file mode 100644 index 0000000..36b4158 --- /dev/null +++ b/src/app/(marketing)/cases/[slug]/client.tsx @@ -0,0 +1,52 @@ +'use client'; + +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'; + +interface CaseDetailClientProps { + item: ContentItem; +} + +/** + * 案例详情客户端组件(CMS 化版本) + * 数据源:CMS ContentItem(由 server 端从 mock-data.ts 同步预取) + * 类型系统:使用 CMS 类型 ContentItem,未来切换真实 API 时无需改业务代码 + */ +export function CaseDetailClient({ item }: CaseDetailClientProps) { + if (!item) { + notFound(); + } + + const data = item.data as unknown as CaseStudyData; + + const detailData = { + id: item.id, + client: data.client, + industry: data.industry, + title: item.title, + subtitle: data.subtitle, + challenge: data.challenge, + solution: data.solution, + result: data.result, + metrics: data.metrics, + timeline: data.timeline, + services: data.services, + testimonial: data.testimonial + ? { + quote: data.testimonial.quote, + author: data.testimonial.author, + role: `${data.testimonial.role},${data.testimonial.company}`, + } + : undefined, + }; + + return ( + <> + + + + ); +} diff --git a/src/app/(marketing)/cases/[slug]/page.tsx b/src/app/(marketing)/cases/[slug]/page.tsx new file mode 100644 index 0000000..b2c7808 --- /dev/null +++ b/src/app/(marketing)/cases/[slug]/page.tsx @@ -0,0 +1,40 @@ +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 { Metadata } from 'next'; + +interface CaseDetailPageProps { + params: Promise<{ slug: string }>; +} + +export async function generateStaticParams() { + const slugs = await getAllPublishedSlugs('case-study'); + return slugs.map((s) => ({ slug: s.slug })); +} + +export async function generateMetadata({ params }: CaseDetailPageProps): Promise { + const { slug } = await params; + const item = await getPublishedItemBySlug('case-study', slug); + + if (!item) { + return { title: `案例未找到 - ${COMPANY_INFO.displayName}` }; + } + + const data = item.data as unknown as CaseStudyData; + + return { + title: `${item.title} - ${data.industry}案例 - ${COMPANY_INFO.displayName}`, + description: `${data.client}数字化转型案例:${data.challenge.substring(0, 80)}... ${data.metrics?.map((m: { value: string; label: string }) => `${m.value} ${m.label}`).join(',')}`, + }; +} + +export default async function CaseDetailPage({ params }: CaseDetailPageProps) { + const { slug } = await params; + const item = await getPublishedItemBySlug('case-study', slug); + + if (!item) { notFound(); } + + return ; +} \ No newline at end of file diff --git a/src/app/(marketing)/cases/cases-content-v1.tsx b/src/app/(marketing)/cases/cases-content-v1.tsx new file mode 100644 index 0000000..6878088 --- /dev/null +++ b/src/app/(marketing)/cases/cases-content-v1.tsx @@ -0,0 +1,320 @@ +'use client'; + +import { useState, useMemo } from 'react'; +import { + Building2, ArrowRight, TrendingUp, Target, Lightbulb, + Filter, +} from 'lucide-react'; +import { ScrollReveal } from '@/components/ui/scroll-reveal'; +import { Badge } from '@/components/ui/badge'; +import { MetricCard } from '@/components/ui/metric-card'; +import { + CASE_INDUSTRIES, + getCasesByIndustry, + type CaseStudy, +} from '@/lib/constants/cases'; +import { + GrainOverlay, + FloatingInkParticles, + SectionLabel, +} from '@/components/ui/page-decoration'; +import { cn } from '@/lib/utils'; + +const colorMap: Record = { + brand: 'bg-brand text-brand', + blue: 'bg-accent-blue text-accent-blue', + teal: 'bg-accent-teal text-accent-teal', + amber: 'bg-accent-amber text-accent-amber', + purple: 'bg-accent-purple text-accent-purple', +}; + +function CaseCard({ caseItem }: { caseItem: CaseStudy; index?: number }) { + const colorStyle = colorMap[caseItem.color]; + const [bgClass, textClass] = colorStyle.split(' '); + + return ( + +
+ +
+
+ + {caseItem.industry} + + + {caseItem.companySize} + +
+ +

+ {caseItem.title} +

+ +
+
+
+ + 挑战 +
+

+ {caseItem.challenge} +

+
+
+
+ + 方案 +
+

+ {caseItem.solution} +

+
+
+ +
+ {caseItem.metrics.slice(0, 2).map((m, i) => ( +
+
+ {m.value} +
+
{m.label}
+
+ ))} +
+ +
+
+ + {caseItem.client} +
+
+ 查看详情 + +
+
+
+
+ ); +} + +const PAGE_METRICS = [ + { + icon: , + label: '服务客户', + value: 500, + suffix: '+', + description: '覆盖六大行业,500+企业客户的共同选择', + trend: { value: '23% YoY', direction: 'up' as const, label: '增长' }, + accentColor: 'brand' as const, + }, + { + icon: , + label: '成功率', + value: 98, + suffix: '%', + description: '项目一次性上线成功率,持续稳定交付', + trend: { value: '97%续约率', direction: 'up' as const, label: '' }, + accentColor: 'teal' as const, + }, + { + icon: , + label: '平均效率提升', + value: 35, + suffix: '%', + description: '客户核心业务流程效率平均提升幅度', + trend: { value: '基于 200+ 案例', direction: 'neutral' as const, label: '' }, + accentColor: 'blue' as const, + }, + { + icon: , + label: '行业覆盖', + value: 6, + suffix: '大', + description: '制造、零售、医疗、教育、金融、物流六大行业', + trend: { value: '持续扩展中', direction: 'up' as const, label: '' }, + accentColor: 'amber' as const, + }, +]; + +export default function CasesContentV1() { + const [selectedIndustry, setSelectedIndustry] = useState('all'); + + const filteredCases = useMemo(() => { + return getCasesByIndustry(selectedIndustry); + }, [selectedIndustry]); + + return ( +
+ {/* Hero Section */} +
+ + + +
+ +
+
+ + Case Studies + + + +

+ 真实案例, + 可衡量的成果 +

+
+ + +

+ 每一个案例,都是我们与客户共同创造的价值故事。从挑战到方案,从过程到成果——用数据说话,用结果证明。 +

+
+ + +
+ + 500+ 企业客户 + + + 98% 项目成功率 + + + 6 大行业覆盖 + + + 97% 客户续约率 + +
+
+
+
+
+ + {/* Key Metrics Section */} +
+
+
+ +
+
+ {PAGE_METRICS.map((metric, i) => ( + +
+ +
+
+ ))} +
+
+
+ + {/* Cases List Section */} +
+ + +
+
+ + All Cases +

+ 全部案例 +

+

+ 按行业筛选,找到与您业务最相关的成功故事 +

+
+ + +
+ + {CASE_INDUSTRIES.map((industry) => ( + + ))} +
+
+
+ + {filteredCases.length > 0 ? ( +
+ {filteredCases.map((caseItem, i) => ( + +
+ +
+
+ ))} +
+ ) : ( +
+

暂无该行业的案例

+
+ )} +
+
+ + {/* CTA Section */} +
+ + + +
+ +
+ +

+ 您的成功故事, + 从这里开始 +

+

+ 无论您处于数字化转型的哪个阶段,我们都有相应的经验和能力帮助您。 + 让我们聊聊您的挑战,看看能创造什么样的成果。 +

+ +
+
+
+
+ ); +} diff --git a/src/app/(marketing)/cases/cases-content-v2.tsx b/src/app/(marketing)/cases/cases-content-v2.tsx new file mode 100644 index 0000000..080b557 --- /dev/null +++ b/src/app/(marketing)/cases/cases-content-v2.tsx @@ -0,0 +1,316 @@ +'use client'; + +import { useState, useMemo } from 'react'; +import { motion } from 'framer-motion'; +import { Building2, ArrowUpRight, Target, Lightbulb, Filter } from 'lucide-react'; +import { + CASE_INDUSTRIES, + getCasesByIndustry, + type CaseStudy, +} from '@/lib/constants/cases'; +import { cn } from '@/lib/utils'; + +const EASE_OUT = [0.22, 1, 0.36, 1] as const; + +const PAGE_METRICS = [ + { value: '500+', label: '服务客户', sub: '覆盖六大行业' }, + { value: '98%', label: '项目成功率', sub: '持续稳定交付' }, + { value: '35%', label: '平均效率提升', sub: '核心业务流程' }, + { value: '6大', label: '行业覆盖', sub: '制造零售医疗等' }, +]; + +function CaseCard({ caseItem, index }: { caseItem: CaseStudy; index: number }) { + return ( + +
+ +
+
+ + {caseItem.industry} + + + {caseItem.companySize} + +
+ +

+ {caseItem.title} +

+ +
+
+
+ + 挑战 +
+

+ {caseItem.challenge} +

+
+
+
+ + 方案 +
+

+ {caseItem.solution} +

+
+
+ +
+ {caseItem.metrics.slice(0, 2).map((m, i) => ( +
+
+ {m.value} +
+
{m.label}
+
+ ))} +
+ +
+
+ + {caseItem.client} +
+
+ 查看详情 + +
+
+
+ + ); +} + +export default function CasesContentV2({ + cases: casesProp, + industries: industriesProp, +}: { + cases?: CaseStudy[]; + industries?: { id: string; label: string }[]; +}) { + const [selectedIndustry, setSelectedIndustry] = useState('all'); + + const industries = industriesProp ?? CASE_INDUSTRIES; + + const filteredCases = useMemo(() => { + if (casesProp) return casesProp; + return getCasesByIndustry(selectedIndustry); + }, [selectedIndustry, casesProp]); + + return ( +
+ {/* Hero Section */} +
+
+
+ + Case Studies + + + +
真实案例,
+
+ + + 可衡量的成果 + + + +
+
+ + + 每一个案例,都是我们与客户共同创造的价值故事。 +
+ 从挑战到方案,从过程到成果——用数据说话,用结果证明。 +
+ + + + 500+ 企业客户 + + + 98% 项目成功率 + + + 6 大行业覆盖 + + + 97% 客户续约率 + + +
+
+
+ + {/* Metrics Strip */} +
+
+
+ +
+
+ {PAGE_METRICS.map((metric, i) => ( + +
+ {metric.value} +
+
{metric.label}
+
{metric.sub}
+
+ ))} +
+
+
+ + {/* Cases List Section */} +
+
+
+ +
+ All Cases +
+

+ 全部案例 +

+

+ 按行业筛选,找到与您业务最相关的成功故事 +

+
+ + +
+ + {industries.map((industry) => ( + + ))} +
+
+
+ + {filteredCases.length > 0 ? ( +
+ {filteredCases.map((caseItem, i) => ( + + ))} +
+ ) : ( +
+

暂无该行业的案例

+
+ )} +
+
+ + {/* CTA Section */} +
+
+ +
+ +

+ 您的成功故事,
从这里开始 +

+

+ 无论您处于数字化转型的哪个阶段,我们都有相应的经验和能力帮助您。 +
+ 让我们聊聊您的挑战,看看能创造什么样的成果。 +

+ +
+
+
+
+ ); +} diff --git a/src/app/(marketing)/cases/cases-content-v3.tsx b/src/app/(marketing)/cases/cases-content-v3.tsx new file mode 100644 index 0000000..b01f5ae --- /dev/null +++ b/src/app/(marketing)/cases/cases-content-v3.tsx @@ -0,0 +1,315 @@ +'use client'; + +import { useState, useMemo } from 'react'; +import { motion } from 'framer-motion'; +import { Building2, ArrowUpRight, Target, Lightbulb, Filter } from 'lucide-react'; +import { + type CaseStudy, +} from '@/lib/constants/cases'; +import { cn } from '@/lib/utils'; + +const EASE_OUT = [0.22, 1, 0.36, 1] as const; + +const PAGE_METRICS = [ + { value: '500+', label: '服务客户', sub: '覆盖六大行业' }, + { value: '98%', label: '项目成功率', sub: '持续稳定交付' }, + { value: '35%', label: '平均效率提升', sub: '核心业务流程' }, + { value: '6大', label: '行业覆盖', sub: '制造零售医疗等' }, +]; + +function CaseCard({ caseItem, index }: { caseItem: CaseStudy; index: number }) { + return ( + +
+ +
+
+ + {caseItem.industry} + + + {caseItem.companySize} + +
+ +

+ {caseItem.title} +

+ +
+
+
+ + 挑战 +
+

+ {caseItem.challenge} +

+
+
+
+ + 方案 +
+

+ {caseItem.solution} +

+
+
+ +
+ {caseItem.metrics.slice(0, 2).map((m, i) => ( +
+
+ {m.value} +
+
{m.label}
+
+ ))} +
+ +
+
+ + {caseItem.client} +
+
+ 查看详情 + +
+
+
+ + ); +} + +export default function CasesContentV3({ + cases: casesProp, + industries: industriesProp, +}: { + cases?: CaseStudy[]; + industries?: { id: string; label: string }[]; +}) { + const [selectedIndustry, setSelectedIndustry] = useState('all'); + + const industries = industriesProp ?? []; + + const filteredCases = useMemo(() => { + const allCases = casesProp ?? []; + if (selectedIndustry === 'all') return allCases; + return allCases.filter(c => c.industry === selectedIndustry); + }, [selectedIndustry, casesProp]); + + return ( +
+ {/* Hero Section */} +
+
+
+ + Case Studies + + + +
真实案例,
+
+ + + 可衡量的成果 + + + +
+
+ + + 每一个案例,都是我们与客户共同创造的价值故事。 +
+ 从挑战到方案,从过程到成果——用数据说话,用结果证明。 +
+ + + + 500+ 企业客户 + + + 98% 项目成功率 + + + 6 大行业覆盖 + + + 97% 客户续约率 + + +
+
+
+ + {/* Metrics Strip */} +
+
+
+ +
+
+ {PAGE_METRICS.map((metric, i) => ( + +
+ {metric.value} +
+
{metric.label}
+
{metric.sub}
+
+ ))} +
+
+
+ + {/* Cases List Section */} +
+
+
+ +
+ All Cases +
+

+ 全部案例 +

+

+ 按行业筛选,找到与您业务最相关的成功故事 +

+
+ + +
+ + {industries.map((industry) => ( + + ))} +
+
+
+ + {filteredCases.length > 0 ? ( +
+ {filteredCases.map((caseItem, i) => ( + + ))} +
+ ) : ( +
+

暂无该行业的案例

+
+ )} +
+
+ + {/* CTA Section */} +
+
+ +
+ +

+ 您的成功故事,
从这里开始 +

+

+ 无论您处于数字化转型的哪个阶段,我们都有相应的经验和能力帮助您。 +
+ 让我们聊聊您的挑战,看看能创造什么样的成果。 +

+ +
+
+
+
+ ); +} diff --git a/src/app/(marketing)/cases/client.tsx b/src/app/(marketing)/cases/client.tsx new file mode 100644 index 0000000..63e486c --- /dev/null +++ b/src/app/(marketing)/cases/client.tsx @@ -0,0 +1,8 @@ +'use client'; + +import type { CaseStudy } from '@/lib/constants/cases'; +import CasesContentV3 from './cases-content-v3'; + +export function CasesClient({ cases }: { cases?: CaseStudy[] }) { + return ; +} \ No newline at end of file diff --git a/src/app/(marketing)/cases/page.tsx b/src/app/(marketing)/cases/page.tsx new file mode 100644 index 0000000..6b799e8 --- /dev/null +++ b/src/app/(marketing)/cases/page.tsx @@ -0,0 +1,16 @@ +import { Metadata } from 'next'; +import { COMPANY_INFO } from '@/lib/constants'; +import { getPublishedItems } from '@/lib/cms/data-server'; +import type { CaseStudy } from '@/lib/constants/cases'; +import { CasesClient } from './client'; + +export const metadata: Metadata = { + title: `客户案例 - ${COMPANY_INFO.displayName}`, + description: `500+企业客户的成功案例,覆盖制造、零售、医疗、教育、金融、物流六大行业。真实数据,可衡量的成果——见证数字化转型的价值。`, +}; + +export default async function CasesPage() { + const items = await getPublishedItems('case-study'); + const cases = items.map((item) => ({ ...item.data, title: item.data.title || item.title } as unknown as CaseStudy)); + return ; +} \ No newline at end of file diff --git a/src/app/(marketing)/cms-studio/page.tsx b/src/app/(marketing)/cms-studio/page.tsx new file mode 100644 index 0000000..d8a75fa --- /dev/null +++ b/src/app/(marketing)/cms-studio/page.tsx @@ -0,0 +1,153 @@ +'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)/contact/contact-content-v1.tsx b/src/app/(marketing)/contact/contact-content-v1.tsx new file mode 100644 index 0000000..43db0de --- /dev/null +++ b/src/app/(marketing)/contact/contact-content-v1.tsx @@ -0,0 +1,557 @@ +'use client'; + +import { useState, Suspense } from 'react'; +import { useSearchParams } from 'next/navigation'; +import { z } from 'zod'; +import { ScrollReveal } from '@/components/ui/scroll-reveal'; +import { Button } from '@/components/ui/button'; +import { LabeledInput as Input } from '@/components/ui/labeled-input'; +import { LabeledTextarea as Textarea } from '@/components/ui/labeled-textarea'; +import { Toast } from '@/components/ui/toast'; +import { + Mail, + MapPin, + Send, + Loader2, + Clock, + HeadphonesIcon, + CheckCircle2, + HelpCircle, + MessageSquare, + ArrowRight, +} from 'lucide-react'; +import { COMPANY_INFO } from '@/lib/constants'; +import { trackContactForm, trackConversion } from '@/lib/analytics'; +import { BreadcrumbSchema } from '@/components/seo/structured-data'; +import { LegacyTooltip as Tooltip } from '@/components/ui/tooltip'; +import { cn } from '@/lib/utils'; + +const contactFormSchema = z.object({ + name: z.string().min(2, '姓名至少需要2个字符'), + phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入有效的手机号码'), + email: z.string().email('请输入有效的邮箱地址'), + subject: z.string().min(2, '主题至少需要2个字符'), + message: z.string().min(10, '留言内容至少需要10个字符'), +}); + +type ContactFormData = z.infer; + +interface FormErrors { + name?: string; + phone?: string; + email?: string; + subject?: string; + message?: string; +} + +const FAQ_ITEMS = [ + { + q: '你们的服务流程是怎样的?', + a: '通常分四步:需求沟通 → 方案评估 → 签约启动 → 敏捷交付。首次咨询免费,我们会在 2 个工作日内给出初步方案建议。', + }, + { + q: '收费模式是怎样的?', + a: '根据项目规模和合作模式灵活定价。项目制按里程碑付款,订阅制按月结算,长期陪跑模式可协商年度合作方案。', + }, + { + q: '数据安全如何保障?', + a: '我们严格遵守数据保护规范,签署保密协议,采用加密传输和权限隔离。项目结束后,客户数据按要求彻底清除。', + }, + { + q: '项目周期一般多长?', + a: '小型项目 2-4 周,中型项目 1-3 个月,大型项目按阶段推进。我们会在方案评估阶段给出明确的时间线。', + }, +]; + +function GrainOverlay() { + return ( +
+ ); +} + +function SectionLabel({ children, className = '' }: { children: React.ReactNode; className?: string }) { + return ( +
+
+ + {children} + +
+
+ ); +} + +function ContactFormContent() { + const searchParams = useSearchParams(); + const isSuccessFromRedirect = searchParams.get('success') === 'true'; + const [showToast, setShowToast] = useState(isSuccessFromRedirect); + const [toastMessage, setToastMessage] = useState( + isSuccessFromRedirect ? '表单提交成功!我们会尽快与您联系。' : '' + ); + const [toastType, setToastType] = useState<'success' | 'error'>( + isSuccessFromRedirect ? 'success' : 'success' + ); + const [isSubmitting, setIsSubmitting] = useState(false); + const [isSubmitted, setIsSubmitted] = useState(isSuccessFromRedirect); + const [formData, setFormData] = useState({ + name: '', + phone: '', + email: '', + subject: '', + message: '', + }); + const [errors, setErrors] = useState({}); + + const validateField = (field: keyof ContactFormData, value: string) => { + try { + contactFormSchema.shape[field].parse(value); + setErrors((prev) => ({ ...prev, [field]: undefined })); + } catch (error) { + if (error instanceof z.ZodError) { + const fieldError = error.issues[0]; + if (fieldError) { + setErrors((prev) => ({ ...prev, [field]: fieldError.message })); + } + } + } + }; + + const handleChange = (field: keyof ContactFormData, value: string) => { + setFormData((prev) => ({ ...prev, [field]: value })); + if (errors[field]) { + validateField(field, value); + } + }; + + const handleBlur = (field: keyof ContactFormData, value: string) => { + validateField(field, value); + }; + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + + const result = contactFormSchema.safeParse(formData); + + if (!result.success) { + const fieldErrors: FormErrors = {}; + result.error.issues.forEach((issue) => { + const field = issue.path[0] as keyof ContactFormData; + fieldErrors[field] = issue.message; + }); + setErrors(fieldErrors); + setToastMessage(`请检查表单:${Object.keys(fieldErrors).length} 项内容需要修正`); + setToastType('error'); + setShowToast(true); + const firstErrorField = document.querySelector('[data-testid*="-input"][aria-invalid="true"]') as HTMLElement; + firstErrorField?.scrollIntoView({ behavior: 'smooth', block: 'center' }); + firstErrorField?.focus(); + return; + } + + setIsSubmitting(true); + + try { + const formBody = new URLSearchParams(); + formBody.append('name', formData.name); + formBody.append('phone', formData.phone); + formBody.append('email', formData.email); + formBody.append('subject', formData.subject); + formBody.append('message', formData.message); + + const response = await fetch('/api/contact', { + method: 'POST', + body: formBody, + }); + + const data = await response.json(); + + if (response.ok && (data.success === 'true' || data.success === true)) { + trackContactForm({ + name: formData.name, + email: formData.email, + company: formData.subject, + }); + trackConversion('contact_form_submission'); + setToastMessage('表单提交成功!我们会尽快与您联系。'); + setToastType('success'); + setShowToast(true); + setIsSubmitted(true); + setFormData({ name: '', phone: '', email: '', subject: '', message: '' }); + setErrors({}); + } else { + const errorMsg = data.message || '提交失败,请稍后重试或直接发送邮件联系我们。'; + if (errorMsg.includes('HTML files') || errorMsg.includes('web server')) { + setToastMessage('表单服务需要在生产环境激活。部署后首次提交会发送确认邮件到 ' + COMPANY_INFO.email); + } else { + setToastMessage(errorMsg); + } + setToastType('error'); + setShowToast(true); + } + } catch { + setToastMessage('网络错误,请稍后重试。'); + setToastType('error'); + setShowToast(true); + } finally { + setIsSubmitting(false); + } + } + + return ( +
+ + {showToast && ( + setShowToast(false)} + /> + )} + + {/* Hero Section */} +
+ + +
+ +
+ + Contact Us + + + +

+ 工作日 2 小时内快速响应,首次咨询免费 +

+
+ + +

+ 随时欢迎 + 聊一聊 +

+
+ + +

+ 无论是一个明确的项目需求,还是一个模糊的想法,我们都愿意坐下来和您一起理清楚。没有任何销售压力。 +

+
+
+
+ + {/* Form + Info Section */} +
+ + +
+ +
+
+ {/* Left: Contact Info */} + +
+

联系方式

+
+
+
+ +
+ +
+ +
+
+ +
+
+

地址

+

+ {COMPANY_INFO.address} +

+
+
+
+
+ +
+
+ +

工作时间

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

我们的承诺

+
+
+
+
+

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

+
+
+
+

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

+
+
+
+

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

+
+
+
+ + + {/* Right: Form */} + +
+
+ +

发送消息

+ + {isSubmitted ? ( +
+
+ +
+

消息已发送

+

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

+
+ ) : ( +
+ +
+
+ + 姓名 + + + + + } + id="name" + placeholder="请输入您的姓名" + required + value={formData.name} + onChange={(e) => handleChange('name', e.target.value)} + onBlur={(e) => handleBlur('name', e.target.value)} + error={errors.name} + className="bg-ink-light border-white/10 text-white placeholder:text-white/30 focus:border-brand/50" + /> +
+
+ + 电话 + + + + + } + id="phone" + type="tel" + placeholder="请输入11位手机号" + required + value={formData.phone} + onChange={(e) => handleChange('phone', e.target.value)} + onBlur={(e) => handleBlur('phone', e.target.value)} + error={errors.phone} + className="bg-ink-light border-white/10 text-white placeholder:text-white/30 focus:border-brand/50" + /> +
+
+ handleChange('email', e.target.value)} + onBlur={(e) => handleBlur('email', e.target.value)} + error={errors.email} + className="bg-ink-light border-white/10 text-white placeholder:text-white/30 focus:border-brand/50" + /> + handleChange('subject', e.target.value)} + onBlur={(e) => handleBlur('subject', e.target.value)} + error={errors.subject} + className="bg-ink-light border-white/10 text-white placeholder:text-white/30 focus:border-brand/50" + /> +