first commit
|
|
@ -0,0 +1,43 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
/lib/generated/prisma
|
||||
|
|
@ -0,0 +1,489 @@
|
|||
# 白马家园官网 - 产品需求文档 (PRD)
|
||||
|
||||
## 📋 文档信息
|
||||
|
||||
- **项目名称**:白马家园官网
|
||||
- **版本**:v3.3
|
||||
- **更新日期**:2025年1月16日
|
||||
- **状态**:开发完成,准备部署
|
||||
|
||||
---
|
||||
|
||||
## 🎯 项目概述
|
||||
|
||||
### 1.1 项目背景
|
||||
|
||||
白马家园是一家专注于数字经济和新质生产力产业集群的科技公司,致力于通过军工技术和人工智能赋能小微企业和实体商业。公司需要建设一个现代化、响应式的官方网站,用于:
|
||||
|
||||
- 品牌形象展示(中英文双语)
|
||||
- 产品矩阵介绍
|
||||
- 客户商机获取
|
||||
- 行业资讯传播
|
||||
- 合作加盟引流
|
||||
|
||||
### 1.2 项目目标
|
||||
|
||||
1. **品牌升级**:打造数字化、科技感的品牌形象(中英文双语)
|
||||
2. **产品展示**:清晰展示四大核心产品线
|
||||
3. **商机转化**:通过客服企业微信二维码引导潜在客户直接联系
|
||||
4. **内容营销**:自动抓取行业资讯,保持网站活跃度
|
||||
5. **用户体验**:支持手机、平板、桌面全设备响应式访问
|
||||
6. **国际化**:支持中英文切换,服务全球用户
|
||||
|
||||
### 1.3 目标用户
|
||||
|
||||
- **潜在合作伙伴**:实体商家、创业者、投资人
|
||||
- **C端消费者**:小微企业主、门店经营者
|
||||
- **媒体和行业人士**:记者、分析师、行业专家
|
||||
- **国际用户**:海外华人和全球商业伙伴
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 系统架构
|
||||
|
||||
### 2.1 技术栈
|
||||
|
||||
```
|
||||
前端:Next.js 16 + React 19 + TypeScript
|
||||
UI框架:Tailwind CSS 4 + Radix UI
|
||||
动画:Framer Motion
|
||||
状态管理:React Hooks
|
||||
后端:Next.js API Routes
|
||||
部署:Vercel
|
||||
国际化:Next.js i18n
|
||||
```
|
||||
|
||||
### 2.2 目录结构
|
||||
|
||||
```
|
||||
baima-home/
|
||||
├── app/
|
||||
│ ├── page.tsx # 首页(中文)
|
||||
│ ├── layout.tsx # 全局布局
|
||||
│ ├── globals.css # 全局样式
|
||||
│ ├── en/
|
||||
│ │ └── page.tsx # 首页(英文)
|
||||
│ ├── about/ # 关于我们
|
||||
│ ├── products/ # 产品矩阵
|
||||
│ ├── news/ # 新闻动态
|
||||
│ ├── contact/ # 联系加盟
|
||||
│ ├── admin/ # 后台管理
|
||||
│ └── api/ # API接口
|
||||
│ ├── news/ # 新闻API(静态数据)
|
||||
│ └── auto-fetch/ # 自动抓取API(静态数据)
|
||||
├── components/
|
||||
│ ├── home/ # 首页组件
|
||||
│ │ ├── Hero.tsx # 首屏区域
|
||||
│ │ ├── ProductPreview.tsx # 产品矩阵
|
||||
│ │ ├── MissionVision.tsx # 使命愿景价值观
|
||||
│ │ ├── HonorSection.tsx # 资质荣誉
|
||||
│ │ └── Infrastructure.tsx # 生态基础设施
|
||||
│ ├── layout/ # 布局组件
|
||||
│ │ ├── Navbar.tsx # 导航栏
|
||||
│ │ └── Footer.tsx # 页脚
|
||||
│ └── ui/ # 基础UI组件
|
||||
├── lib/
|
||||
│ └── utils.ts # 工具函数
|
||||
└── public/ # 静态资源
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📱 页面规划
|
||||
|
||||
### 3.1 前台页面
|
||||
|
||||
#### 首页 (/ 和 /en)
|
||||
**设计风格**:单页全景布局,沉浸式体验
|
||||
|
||||
**功能模块**:
|
||||
1. **Hero首屏**
|
||||
- 品牌Logo(大尺寸展示)
|
||||
- 核心标签:军工技术、AI赋能、小微企业
|
||||
- 价值主张:AI数智增长引擎
|
||||
- 三大核心价值:支付即锁粉、让利即引流、跨界即分润
|
||||
- CTA按钮:立即加入白马联盟 / Join Baima Alliance
|
||||
- 背景:深蓝色科技感渐变 + 网格效果
|
||||
|
||||
2. **产品矩阵**
|
||||
- 四大核心板块卡片布局
|
||||
- 金融科技支撑(白马智付)
|
||||
- 私域运营工具(AI拓客宝)【原名"爆客宝AI"】
|
||||
- 异业联盟系统(商家联盟)
|
||||
- 生态基础设施(智能硬件)
|
||||
- 每张卡片包含:标题、描述、功能特性列表
|
||||
- 【已移除】"深入了解"链接
|
||||
|
||||
3. **使命、愿景、价值观**【新增模块】
|
||||
- 使命:军工技术赋能实体经济,人工智能驱动万商互联
|
||||
- 愿景:天下白马是一家,一起赚钱一起花
|
||||
- 价值观:共创、共享、共富
|
||||
- 三大核心板块,渐变色背景,悬停动画效果
|
||||
- 支持中英文双语显示
|
||||
|
||||
4. **权威认证与技术实力**
|
||||
- 资质证书展示(ISO认证、高新技术企业等)
|
||||
- 专利技术展示(专利数量、软著)
|
||||
- 荣誉奖项展示(行业认可)
|
||||
- 响应式网格布局
|
||||
|
||||
5. **生态基础设施**
|
||||
- 会员系统
|
||||
- 供应链资源
|
||||
- 数字资产体系
|
||||
- 【已移除】智能监控(移至智能设备)
|
||||
- 【已移除】"为上市合规打造的底层架构"描述文字
|
||||
|
||||
6. **页脚**
|
||||
- 客服企业微信二维码(替代电话和邮箱)
|
||||
- 产品中心链接
|
||||
- 解决方案链接
|
||||
- 公司地址:天津市武清区京津产业新城海达商务大厦
|
||||
- 公司信息:天津白马晶选科技有限公司 | 天津白马家园健康科技有限公司
|
||||
- 版权信息
|
||||
- 【已移除】隐私政策、服务条款链接
|
||||
|
||||
#### 关于我们 (/about)
|
||||
- 公司介绍
|
||||
- 团队介绍
|
||||
- 发展历程
|
||||
- 资质认证
|
||||
|
||||
#### 产品矩阵 (/products)
|
||||
- 产品详情页
|
||||
- 产品对比
|
||||
- 定价方案
|
||||
- 客户案例
|
||||
|
||||
#### 新闻动态 (/news)
|
||||
- 新闻列表(静态数据展示)
|
||||
- 新闻详情
|
||||
- 分类筛选
|
||||
- 搜索功能
|
||||
|
||||
#### 联系加盟 (/contact)
|
||||
- 公司信息
|
||||
- 地图定位
|
||||
- 客服企业微信二维码(引导用户扫码联系)
|
||||
|
||||
### 3.2 后台管理 (/admin)
|
||||
|
||||
#### 登录验证
|
||||
- 密码保护:固定密码 `admin123`
|
||||
- 简洁的登录界面
|
||||
|
||||
#### 新闻动态管理
|
||||
- 发布新闻(静态数据示例)
|
||||
- 编辑新闻
|
||||
- 删除新闻
|
||||
- 分类管理
|
||||
- 状态跟踪
|
||||
|
||||
#### 自动抓取管理
|
||||
- 手动触发抓取(静态数据示例)
|
||||
- 待审核列表
|
||||
- 审核操作(通过/拒绝)
|
||||
- 抓取历史记录
|
||||
- 配置管理
|
||||
|
||||
### 3.3 语言切换
|
||||
|
||||
- 支持中英文双语
|
||||
- 导航栏语言切换按钮
|
||||
- 中文首页:`/`
|
||||
- 英文首页:`/en`
|
||||
- 页面内容自动切换
|
||||
- 移动端支持滑出式菜单切换
|
||||
|
||||
---
|
||||
|
||||
## 📡 API 接口
|
||||
|
||||
### 4.1 新闻接口(静态数据)
|
||||
|
||||
**GET** `/api/news`
|
||||
|
||||
返回静态新闻数据示例,无需数据库支持。
|
||||
|
||||
### 4.2 自动抓取接口(静态数据)
|
||||
|
||||
**GET** `/api/auto-fetch`
|
||||
|
||||
返回静态抓取数据示例,无需数据库支持。
|
||||
|
||||
---
|
||||
|
||||
## 🤖 自动抓取系统
|
||||
|
||||
### 5.1 抓取策略(静态示例)
|
||||
|
||||
- **主要来源**:搜狗微信搜索(示例)
|
||||
- **目标账号/关键词**:白马精选、白马家园
|
||||
- **抓取频率**:每6小时自动抓取
|
||||
- **手动触发**:管理员可随时手动抓取
|
||||
|
||||
### 5.2 智能过滤规则
|
||||
|
||||
#### 必须包含的关键词
|
||||
- `白马精选`(必须)
|
||||
- `数字经济`、`AI`、`人工智能`、`金融科技`、`小微企业`(至少一个)
|
||||
|
||||
#### 排除的关键词(房地产相关)
|
||||
```
|
||||
房地产、楼盘、房价、房产、住宅、商业地产
|
||||
购房、租房、地产投资、物业、开盘、预售证
|
||||
样板间、户型图、买房、卖房、出租、二手房
|
||||
白马家园、白马花园、白马苑、白马小区(小区名称)
|
||||
万科白马家园、恒大白马家园、碧桂园白马家园
|
||||
别墅、豪宅、精装房、毛坯房
|
||||
```
|
||||
|
||||
#### 分类规则
|
||||
- `产品发布`:包含发布、上线、新品等关键词
|
||||
- `荣誉获奖`:包含获奖、认证、荣誉等关键词
|
||||
- `行业洞察`:包含趋势、分析、洞察等关键词
|
||||
- `媒体报道`:包含报道、媒体等关键词
|
||||
- 默认:`公司新闻`
|
||||
|
||||
---
|
||||
|
||||
## 🎨 设计规范
|
||||
|
||||
### 6.1 视觉风格
|
||||
|
||||
#### 配色方案
|
||||
- **主色调**:橙色 `#FF6600`
|
||||
- 用途:CTA按钮、强调文字、图标、链接
|
||||
- **辅助色**:深蓝色 `#0A1931`
|
||||
- 用途:背景、导航栏、文字
|
||||
- **点缀色**:`#185ADB`(浅蓝)
|
||||
- **背景色**:`#050A14`(深黑)
|
||||
|
||||
#### 字体规范
|
||||
- **标题**:加粗加黑,层级分明
|
||||
- **正文字体**:17px基础字号
|
||||
- **英文字体**:Geist Sans
|
||||
- **中文字体**:系统默认无衬线字体
|
||||
|
||||
### 6.2 响应式断点
|
||||
|
||||
```
|
||||
移动端:< 640px
|
||||
平板端:640px - 1024px
|
||||
桌面端:> 1024px
|
||||
```
|
||||
|
||||
### 6.3 组件规范
|
||||
|
||||
- **按钮**:圆角矩形,阴影效果,悬停动画
|
||||
- **卡片**:圆角边框,阴影,悬停效果
|
||||
- **导航栏**:固定定位,滚动变色,语言切换
|
||||
- **页脚**:多列布局,响应式堆叠,客服二维码
|
||||
- **模块间距**:统一规范,保持视觉平衡
|
||||
|
||||
### 6.4 页面布局优化
|
||||
|
||||
#### Logo 尺寸
|
||||
- 导航栏:56px(h-14)
|
||||
- 首页Hero:64px - 112px(响应式)
|
||||
- 页脚:56px(h-14)
|
||||
|
||||
#### 使命愿景价值观模块
|
||||
- 深蓝渐变背景
|
||||
- 三栏卡片布局
|
||||
- 彩色图标(橙/蓝/绿渐变)
|
||||
- 悬停动画效果
|
||||
- 中英文双语内容
|
||||
|
||||
#### 新闻卡片
|
||||
- 卡片可点击,支持跳转详情页
|
||||
- 分类标签+日期展示
|
||||
- 摘要截断(最多3行)
|
||||
- 阅读更多链接
|
||||
|
||||
---
|
||||
|
||||
## 🔧 功能实现
|
||||
|
||||
### 7.1 已完成功能
|
||||
|
||||
✅ **前台功能**
|
||||
- 首页单页全景布局(中英文双语)
|
||||
- 使命愿景价值观模块
|
||||
- 响应式设计(全设备适配)
|
||||
- 产品矩阵展示
|
||||
- 资质荣誉展示
|
||||
- 新闻动态模块(静态数据)
|
||||
- 合作加盟表单
|
||||
- 页脚优化(企微二维码)
|
||||
|
||||
✅ **技术功能**
|
||||
- SEO优化
|
||||
- 性能优化
|
||||
- 错误处理
|
||||
- 加载状态
|
||||
- 中英文国际化
|
||||
|
||||
### 7.2 待实现功能
|
||||
|
||||
⏳ **内容功能**
|
||||
- 新闻详情页完善
|
||||
- 产品详情页
|
||||
- 关于我们页面完善
|
||||
|
||||
⏳ **部署功能**
|
||||
- Vercel部署
|
||||
- 域名配置
|
||||
- SSL证书
|
||||
- 监控告警
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试用例
|
||||
|
||||
### 8.1 前台测试
|
||||
|
||||
#### 首页测试
|
||||
- [x] Hero区域加载正常
|
||||
- [x] 产品卡片显示完整
|
||||
- [x] 使命愿景价值观模块显示
|
||||
- [x] 资质荣誉网格布局
|
||||
- [x] 新闻动态展示(静态数据)
|
||||
- [x] 移动端响应式布局
|
||||
- [x] 页面滚动动画效果
|
||||
- [x] 中英文切换功能
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署计划
|
||||
|
||||
### 9.1 部署环境
|
||||
|
||||
- **开发环境**:本地 `localhost:3000`
|
||||
- **测试环境**:Vercel Preview
|
||||
- **生产环境**:Vercel Production
|
||||
|
||||
### 9.2 部署步骤
|
||||
|
||||
1. **代码推送**
|
||||
```
|
||||
git push origin main
|
||||
```
|
||||
|
||||
2. **Vercel导入**
|
||||
- 访问 https://vercel.com/new
|
||||
- 导入 GitHub 仓库 `wygogogo19/baima-home`
|
||||
|
||||
3. **自动部署**
|
||||
- Vercel自动检测并部署
|
||||
- 预览环境生成
|
||||
|
||||
4. **生产发布**
|
||||
- Vercel生产环境部署
|
||||
- 域名绑定
|
||||
- SSL证书配置
|
||||
|
||||
### 9.3 监控和维护
|
||||
|
||||
- **错误监控**:Vercel Error Tracking
|
||||
- **性能监控**:Vercel Analytics
|
||||
- **日志查看**:Vercel Logs
|
||||
- **回滚机制**:Vercel Deployments
|
||||
|
||||
---
|
||||
|
||||
## 📊 项目进度
|
||||
|
||||
### 9.1 当前进度
|
||||
|
||||
- ✅ **需求分析**:完成
|
||||
- ✅ **UI设计**:完成
|
||||
- ✅ **前端开发**:完成
|
||||
- ✅ **后端开发**:完成
|
||||
- ✅ **国际化(中英文)**:完成
|
||||
- ✅ **测试调试**:完成
|
||||
- ⏳ **部署上线**:进行中
|
||||
|
||||
### 9.2 下一步计划
|
||||
|
||||
1. **短期目标**
|
||||
- [x] Vercel部署
|
||||
- [x] 生产环境测试
|
||||
|
||||
2. **中期目标(1周内)**
|
||||
- [ ] 完善前台页面
|
||||
- [ ] 新闻详情页开发
|
||||
- [ ] 绑定自定义域名
|
||||
|
||||
---
|
||||
|
||||
## 📝 变更日志
|
||||
|
||||
### v3.3 (2025-01-16) - 当前版本
|
||||
- 🔄 完全移除数据库依赖
|
||||
- 删除 Prisma ORM 和所有数据库相关代码
|
||||
- 所有 API 接口改为静态数据返回
|
||||
- 删除 prisma/ 目录、lib/prisma.ts
|
||||
- 从 package.json 移除 Prisma 依赖
|
||||
- 不再需要 DATABASE_URL 环境变量
|
||||
- 🔄 更新技术架构文档,移除数据库相关内容
|
||||
- ✅ 简化部署流程,无需数据库配置
|
||||
|
||||
### v3.2 (2025-01-16)
|
||||
- 🔄 更新资质认证描述
|
||||
- "支付业务许可证" → "收单外包服务机构备案"
|
||||
- 🔄 产品矩阵多项优化
|
||||
- 支付能力:"主扫被扫全支持" → "主扫、被扫模式全支持"
|
||||
- AI数字人:"7x24小时直播 / 数字员工导购" → "AI直播/AI导购/AI客服/AI私域"
|
||||
- 企微私域(原消费券流转):"供应商进货 / 转赠他人使用" → "AI数字员工自动实现:社群运营/维护/导购/客服"
|
||||
- 锁定客户:"一次锁定,终身受益" → "扫码即锁定推荐关系,终身享受消费分佣"
|
||||
- 数字资产 → 数字资产体系
|
||||
- RWA资产描述:"积分抵用券互转" → "积分、抵用券、金币等"
|
||||
- 智能监控巡店移至智能设备
|
||||
- 🔄 页面结构调整
|
||||
- 删除首页"最新动态"模块
|
||||
- 删除产品卡片"深入了解"链接
|
||||
- ✅ 优化产品展示逻辑,提升用户体验
|
||||
|
||||
### v3.1 (2025-01-16)
|
||||
- 🔄 删除"在线咨询留言"功能模块(Path A - 完全删除)
|
||||
- 🗑️ 删除 Contact 数据模型及相关 API 路由
|
||||
- 🗑️ 删除后台"客户留言管理"功能
|
||||
- 🗑️ 删除前端"在线表单"提交功能
|
||||
- ✅ 全站统一使用客服企业微信二维码作为对外联系入口
|
||||
|
||||
### v3.0 (2025-01-15)
|
||||
- ✅ 新增使命愿景价值观模块
|
||||
- ✅ 新增英文版首页 (/en)
|
||||
- ✅ 新增中英文切换功能
|
||||
- ✅ 新增自动抓取系统(搜狗微信)
|
||||
- ✅ 新增智能过滤(排除房地产内容)
|
||||
- ✅ 新闻卡片支持点击跳转详情页
|
||||
- ✅ 页脚改为客服企业微信号码
|
||||
- ✅ 移除"为上市合规打造的底层架构"
|
||||
- ✅ 产品名称更新:爆客宝AI → AI拓客宝
|
||||
- ✅ 移除页脚隐私政策、服务条款
|
||||
- ✅ Logo尺寸优化放大
|
||||
- ✅ 响应式设计优化
|
||||
|
||||
---
|
||||
|
||||
## 👥 团队
|
||||
|
||||
- **产品经理**:[待补充]
|
||||
- **UI设计**:[待补充]
|
||||
- **前端开发**:已完成
|
||||
- **后端开发**:已完成
|
||||
- **测试**:[待补充]
|
||||
|
||||
---
|
||||
|
||||
## 📞 联系方式
|
||||
|
||||
- **项目负责人**:[待补充]
|
||||
- **技术对接**:[待补充]
|
||||
- **更新日期**:2025年1月16日
|
||||
|
||||
---
|
||||
|
||||
*本文档将持续更新,记录项目进展和需求变更。*
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import { Team } from '@/components/about/Team'
|
||||
import { Certifications } from '@/components/about/Certifications'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<div className="pt-24 pb-20">
|
||||
{/* Hero Section */}
|
||||
<section className="container mx-auto px-4 md:px-6 mb-20">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<Badge variant="outline" className="mb-4 border-primary text-primary bg-primary/5">
|
||||
国资背景 · 军工技术
|
||||
</Badge>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-secondary mb-8">
|
||||
相互成就 · 共创未来
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground leading-relaxed mb-8">
|
||||
白马家园(白马精选)是一家拥有国资背景的高新技术企业,已获五轮融资超 2 亿元。
|
||||
我们致力于通过 AI 人工智能、大数据分析、区块链等前沿技术,解决实体商家"流量难、锁客难、变现难"的痛点,
|
||||
构建万商互联的共享经济生态。
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Team />
|
||||
<Certifications />
|
||||
|
||||
{/* Financing History */}
|
||||
<section className="py-20 bg-secondary text-white">
|
||||
<div className="container mx-auto px-4 md:px-6">
|
||||
<h2 className="text-3xl font-bold text-center mb-16">资本历程</h2>
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="space-y-8 relative before:absolute before:inset-0 before:ml-5 before:-translate-x-px md:before:mx-auto md:before:translate-x-0 before:h-full before:w-0.5 before:bg-gradient-to-b before:from-transparent before:via-white/20 before:to-transparent">
|
||||
{[
|
||||
{ year: "B轮", title: "启迪之星投资", desc: "进一步夯实技术壁垒与市场版图" },
|
||||
{ year: "A+轮", title: "沂景 / 创世伙伴 / 中测航天", desc: "多方资本加持,加速生态布局" },
|
||||
{ year: "A轮", title: "创世伙伴 CCV 领投", desc: "获得知名风投机构青睐" },
|
||||
{ year: "Pre-A", title: "中体基金领投", desc: "初步验证商业模式" },
|
||||
{ year: "天使轮", title: "玖创 / 唯猎 / 天使湾", desc: "启动创业征程" }
|
||||
].map((item, index) => (
|
||||
<div key={index} className="relative flex items-center justify-between md:justify-normal md:odd:flex-row-reverse group is-active">
|
||||
<div className="flex items-center justify-center w-10 h-10 rounded-full border border-white/20 bg-white/10 group-[.is-active]:bg-primary text-white shrink-0 md:order-1 md:group-odd:-translate-x-1/2 md:group-even:translate-x-1/2">
|
||||
<div className="w-3 h-3 bg-current rounded-full"></div>
|
||||
</div>
|
||||
<div className="w-[calc(100%-4rem)] md:w-[calc(50%-2.5rem)] p-4 rounded border border-white/10 bg-white/5 shadow">
|
||||
<div className="flex items-center justify-between space-x-2 mb-1">
|
||||
<div className="font-bold text-white">{item.title}</div>
|
||||
<time className="font-mono italic text-primary">{item.year}</time>
|
||||
</div>
|
||||
<div className="text-white/60 text-sm">{item.desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,571 @@
|
|||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import {
|
||||
RefreshCw,
|
||||
Lock,
|
||||
Newspaper,
|
||||
Plus,
|
||||
Trash2,
|
||||
LogOut,
|
||||
Rss,
|
||||
Settings,
|
||||
Check,
|
||||
X,
|
||||
Eye,
|
||||
Globe
|
||||
} from 'lucide-react'
|
||||
|
||||
type News = {
|
||||
id: number
|
||||
title: string
|
||||
category: string
|
||||
excerpt: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
type AutoFetchedNews = {
|
||||
id: number
|
||||
title: string
|
||||
source: string
|
||||
url: string
|
||||
imageUrl: string
|
||||
excerpt: string
|
||||
category: string
|
||||
publishTime: string
|
||||
status: string
|
||||
isDigitalEconomy: boolean
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export default function AdminPage() {
|
||||
const [activeTab, setActiveTab] = useState<'news' | 'autofetch'>('news')
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false)
|
||||
const [password, setPassword] = useState('')
|
||||
const [newsList, setNewsList] = useState<News[]>([])
|
||||
const [autoFetchedNews, setAutoFetchedNews] = useState<AutoFetchedNews[]>([])
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
// 新闻发布表单状态
|
||||
const [showAddNews, setShowAddNews] = useState(false)
|
||||
const [newNews, setNewNews] = useState({
|
||||
title: '',
|
||||
category: '公司新闻',
|
||||
excerpt: '',
|
||||
content: ''
|
||||
})
|
||||
|
||||
// 自动抓取状态
|
||||
const [isAutoFetching, setIsAutoFetching] = useState(false)
|
||||
const [fetchStatus, setFetchStatus] = useState<{
|
||||
lastFetch: string | null
|
||||
nextFetch: string | null
|
||||
}>({
|
||||
lastFetch: null,
|
||||
nextFetch: null
|
||||
})
|
||||
|
||||
const handleLogin = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (password === 'admin123') {
|
||||
setIsAuthenticated(true)
|
||||
fetchAllData()
|
||||
} else {
|
||||
alert('密码错误')
|
||||
}
|
||||
}
|
||||
|
||||
const fetchAllData = async () => {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const [newsRes, autoFetchRes] = await Promise.all([
|
||||
fetch('/api/news'),
|
||||
fetch('/api/auto-fetch')
|
||||
])
|
||||
|
||||
if (newsRes.ok) {
|
||||
const data = await newsRes.json()
|
||||
setNewsList(data.news || [])
|
||||
}
|
||||
if (autoFetchRes.ok) {
|
||||
const data = await autoFetchRes.json()
|
||||
// 合并待审核和已批准的内容
|
||||
const allNews = [
|
||||
...(data.pending || []),
|
||||
...(data.approved || []).filter((a: AutoFetchedNews) =>
|
||||
!data.pending?.some((p: AutoFetchedNews) => p.id === a.id)
|
||||
)
|
||||
]
|
||||
setAutoFetchedNews(allNews)
|
||||
setFetchStatus({
|
||||
lastFetch: data.lastFetch,
|
||||
nextFetch: data.nextFetch
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败', error)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAddNews = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
const res = await fetch('/api/news', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(newNews)
|
||||
})
|
||||
if (res.ok) {
|
||||
setNewNews({ title: '', category: '公司新闻', excerpt: '', content: '' })
|
||||
setShowAddNews(false)
|
||||
fetchAllData()
|
||||
}
|
||||
} catch (error) {
|
||||
alert('发布失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 自动抓取相关函数
|
||||
const triggerAutoFetch = async () => {
|
||||
setIsAutoFetching(true)
|
||||
try {
|
||||
const res = await fetch('/api/auto-fetch', { method: 'POST' })
|
||||
const data = await res.json()
|
||||
|
||||
if (data.success) {
|
||||
alert(`成功抓取 ${data.articles?.length || 0} 条资讯`)
|
||||
fetchAllData()
|
||||
} else {
|
||||
alert('抓取失败:' + data.error)
|
||||
}
|
||||
} catch (error) {
|
||||
alert('抓取失败,请稍后重试')
|
||||
} finally {
|
||||
setIsAutoFetching(false)
|
||||
}
|
||||
}
|
||||
|
||||
const approveArticle = async (id: number) => {
|
||||
try {
|
||||
const res = await fetch(`/api/auto-fetch/${id}/approve`, { method: 'POST' })
|
||||
if (res.ok) {
|
||||
fetchAllData()
|
||||
}
|
||||
} catch (error) {
|
||||
alert('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
const rejectArticle = async (id: number) => {
|
||||
const reason = prompt('请输入拒绝原因:')
|
||||
if (reason === null) return
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/auto-fetch/${id}/reject`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ reason })
|
||||
})
|
||||
if (res.ok) {
|
||||
fetchAllData()
|
||||
}
|
||||
} catch (error) {
|
||||
alert('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
const deleteArticle = async (id: number) => {
|
||||
if (!confirm('确定要删除这条资讯吗?')) return
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/auto-fetch/${id}`, { method: 'DELETE' })
|
||||
if (res.ok) {
|
||||
fetchAllData()
|
||||
}
|
||||
} catch (error) {
|
||||
alert('删除失败')
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-[#0A1931]">
|
||||
<Card className="w-[350px] shadow-2xl border-0">
|
||||
<CardHeader className="text-center pb-8 pt-10">
|
||||
<CardTitle className="text-2xl font-black text-[#0A1931] flex flex-col items-center gap-4">
|
||||
<div className="w-16 h-16 bg-[#FF6600] rounded-2xl flex items-center justify-center text-white shadow-lg">
|
||||
<Lock className="w-8 h-8" />
|
||||
</div>
|
||||
白马家园后台管理
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pb-10 px-8">
|
||||
<form onSubmit={handleLogin} className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-bold text-gray-500">管理员密码</label>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
className="h-12 border-gray-200 focus:border-[#FF6600] focus:ring-[#FF6600]"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="w-full h-12 bg-[#0A1931] hover:bg-[#185ADB] font-bold text-lg rounded-xl transition-all">
|
||||
进入管理中心
|
||||
</Button>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
{/* Sidebar */}
|
||||
<aside className="w-64 bg-[#0A1931] text-white hidden md:flex flex-col">
|
||||
<div className="p-8 border-b border-white/10">
|
||||
<span className="text-xl font-black">管理中心</span>
|
||||
</div>
|
||||
<nav className="flex-grow p-4 space-y-2">
|
||||
<button
|
||||
onClick={() => setActiveTab('news')}
|
||||
className={`w-full flex items-center gap-3 px-4 py-3 rounded-xl transition-all font-bold ${activeTab === 'news' ? 'bg-[#FF6600] text-white shadow-lg' : 'hover:bg-white/5 text-gray-400'}`}
|
||||
>
|
||||
<Newspaper className="w-5 h-5" />
|
||||
新闻动态
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('autofetch')}
|
||||
className={`w-full flex items-center gap-3 px-4 py-3 rounded-xl transition-all font-bold ${activeTab === 'autofetch' ? 'bg-[#FF6600] text-white shadow-lg' : 'hover:bg-white/5 text-gray-400'}`}
|
||||
>
|
||||
<Rss className="w-5 h-5" />
|
||||
自动抓取
|
||||
</button>
|
||||
</nav>
|
||||
<div className="p-6">
|
||||
<Button variant="ghost" className="w-full text-gray-400 hover:text-white" onClick={() => setIsAuthenticated(false)}>
|
||||
<LogOut className="w-4 h-4 mr-2" /> 退出登录
|
||||
</Button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-grow pt-24 pb-12 px-4 md:px-8 overflow-y-auto">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
|
||||
<div className="flex justify-between items-center mb-10">
|
||||
<div>
|
||||
<h1 className="text-3xl font-black text-[#0A1931]">
|
||||
{activeTab === 'news' && '公司/行业动态'}
|
||||
{activeTab === 'autofetch' && '资讯自动抓取'}
|
||||
</h1>
|
||||
<p className="text-gray-500 mt-1">
|
||||
{activeTab === 'news' && '发布和管理最新的公司动态与行业资讯'}
|
||||
{activeTab === 'autofetch' && '自动抓取白马精选公众号等资讯,智能筛选后展示'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
{activeTab === 'news' && (
|
||||
<Button
|
||||
onClick={() => setShowAddNews(!showAddNews)}
|
||||
className="bg-[#FF6600] hover:bg-[#FF8C00] font-bold"
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" /> 发布动态
|
||||
</Button>
|
||||
)}
|
||||
{activeTab === 'autofetch' && (
|
||||
<Button
|
||||
onClick={triggerAutoFetch}
|
||||
disabled={isAutoFetching}
|
||||
className="bg-[#FF6600] hover:bg-[#FF8C00] font-bold"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 mr-2 ${isAutoFetching ? 'animate-spin' : ''}`} />
|
||||
{isAutoFetching ? '抓取中...' : '立即抓取'}
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="outline" onClick={fetchAllData} disabled={isLoading} className="border-gray-200">
|
||||
<RefreshCw className={`w-4 h-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} /> 刷新
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 新闻发布表单 */}
|
||||
{activeTab === 'news' && showAddNews && (
|
||||
<Card className="mb-10 border-2 border-[#FF6600]/20 shadow-xl overflow-hidden">
|
||||
<CardHeader className="bg-[#FF6600]/5 border-b">
|
||||
<CardTitle className="text-lg">撰写新动态</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-6">
|
||||
<form onSubmit={handleAddNews} className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-bold">标题</label>
|
||||
<Input
|
||||
placeholder="输入新闻标题"
|
||||
required
|
||||
onChange={(e) => setNewNews({...newNews, title: e.target.value})}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-bold">分类</label>
|
||||
<select
|
||||
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
|
||||
value={newNews.category}
|
||||
onChange={(e) => setNewNews({...newNews, category: e.target.value})}
|
||||
>
|
||||
<option>公司新闻</option>
|
||||
<option>行业洞察</option>
|
||||
<option>媒体报道</option>
|
||||
<option>产品发布</option>
|
||||
<option>荣誉获奖</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-bold">摘要</label>
|
||||
<Input
|
||||
placeholder="输入新闻简介"
|
||||
value={newNews.excerpt}
|
||||
onChange={(e) => setNewNews({...newNews, excerpt: e.target.value})}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-bold">详细内容</label>
|
||||
<Textarea
|
||||
placeholder="输入详细报道内容..."
|
||||
className="min-h-[150px]"
|
||||
value={newNews.content}
|
||||
onChange={(e) => setNewNews({...newNews, content: e.target.value})}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-3 justify-end">
|
||||
<Button variant="ghost" onClick={() => setShowAddNews(false)}>取消</Button>
|
||||
<Button type="submit" className="bg-[#0A1931] hover:bg-[#185ADB] px-8">立即发布</Button>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* 自动抓取状态面板 */}
|
||||
{activeTab === 'autofetch' && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-10">
|
||||
<Card className="bg-gradient-to-br from-[#FF6600] to-[#FF8C00] text-white">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center">
|
||||
<Rss className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm opacity-80">上次抓取时间</p>
|
||||
<p className="text-lg font-bold">
|
||||
{fetchStatus.lastFetch
|
||||
? new Date(fetchStatus.lastFetch).toLocaleString('zh-CN')
|
||||
: '尚未抓取'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-gradient-to-br from-[#0A1931] to-[#185ADB] text-white">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center">
|
||||
<Settings className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm opacity-80">下次抓取时间</p>
|
||||
<p className="text-lg font-bold">
|
||||
{fetchStatus.nextFetch
|
||||
? new Date(fetchStatus.nextFetch).toLocaleString('zh-CN')
|
||||
: '每6小时自动抓取'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-gradient-to-br from-green-500 to-emerald-600 text-white">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center">
|
||||
<Check className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm opacity-80">待审核资讯</p>
|
||||
<p className="text-2xl font-bold">
|
||||
{autoFetchedNews.filter(n => n.status === 'PENDING').length} 条
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 数据列表 */}
|
||||
<div className="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
{activeTab === 'news' && (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm text-left">
|
||||
<thead className="text-xs text-gray-400 uppercase bg-gray-50/50 border-b">
|
||||
<tr>
|
||||
<th className="px-6 py-4">发布时间</th>
|
||||
<th className="px-6 py-4">标题</th>
|
||||
<th className="px-6 py-4">分类</th>
|
||||
<th className="px-6 py-4">摘要</th>
|
||||
<th className="px-6 py-4 text-right">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y">
|
||||
{newsList.length === 0 ? (
|
||||
<tr><td colSpan={5} className="text-center py-20 text-gray-400">暂无动态数据</td></tr>
|
||||
) : (
|
||||
newsList.map((n) => (
|
||||
<tr key={n.id} className="hover:bg-gray-50 transition-colors">
|
||||
<td className="px-6 py-4 text-xs text-gray-400 whitespace-nowrap">{new Date(n.createdAt).toLocaleDateString()}</td>
|
||||
<td className="px-6 py-4 font-bold text-[#0A1931]">{n.title}</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className="bg-orange-50 text-[#FF6600] px-2.5 py-1 rounded-full text-xs font-bold">{n.category}</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-gray-500 max-w-xs truncate">{n.excerpt}</td>
|
||||
<td className="px-6 py-4 text-right">
|
||||
<Button variant="ghost" size="icon" className="text-gray-300 hover:text-red-500">
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'autofetch' && (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm text-left">
|
||||
<thead className="text-xs text-gray-400 uppercase bg-gray-50/50 border-b">
|
||||
<tr>
|
||||
<th className="px-6 py-4">发布时间</th>
|
||||
<th className="px-6 py-4">标题</th>
|
||||
<th className="px-6 py-4">来源</th>
|
||||
<th className="px-6 py-4">分类</th>
|
||||
<th className="px-6 py-4">状态</th>
|
||||
<th className="px-6 py-4 text-right">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y">
|
||||
{autoFetchedNews.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="text-center py-20">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<Rss className="w-16 h-16 text-gray-300" />
|
||||
<div>
|
||||
<p className="text-gray-500 font-medium">暂无抓取数据</p>
|
||||
<p className="text-gray-400 text-sm">点击"立即抓取"按钮开始抓取资讯</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
autoFetchedNews.map((news) => (
|
||||
<tr key={news.id} className="hover:bg-gray-50 transition-colors">
|
||||
<td className="px-6 py-4 text-xs text-gray-400 whitespace-nowrap">
|
||||
{new Date(news.publishTime).toLocaleDateString()}
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<div className="font-bold text-[#0A1931] max-w-xs truncate">{news.title}</div>
|
||||
{news.excerpt && (
|
||||
<div className="text-gray-400 text-xs mt-1 max-w-xs truncate">{news.excerpt}</div>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Globe className="w-4 h-4 text-[#FF6600]" />
|
||||
<span className="text-gray-600">{news.source}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className="bg-blue-50 text-blue-600 px-2.5 py-1 rounded-full text-xs font-bold">
|
||||
{news.category}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className={`px-2.5 py-1 rounded-full text-xs font-bold ${
|
||||
news.status === 'APPROVED' ? 'bg-green-50 text-green-600' :
|
||||
news.status === 'REJECTED' ? 'bg-red-50 text-red-600' :
|
||||
'bg-yellow-50 text-yellow-600'
|
||||
}`}>
|
||||
{news.status === 'APPROVED' ? '已发布' :
|
||||
news.status === 'REJECTED' ? '已拒绝' : '待审核'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-right">
|
||||
<div className="flex justify-end gap-2">
|
||||
{news.status === 'PENDING' && (
|
||||
<>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-green-500 hover:text-green-600"
|
||||
onClick={() => approveArticle(news.id)}
|
||||
title="通过并发布"
|
||||
>
|
||||
<Check className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-red-500 hover:text-red-600"
|
||||
onClick={() => rejectArticle(news.id)}
|
||||
title="拒绝"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{news.url && (
|
||||
<a
|
||||
href={news.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-gray-400 hover:text-[#FF6600] p-2"
|
||||
title="查看原文"
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
</a>
|
||||
)}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-gray-300 hover:text-red-500"
|
||||
onClick={() => deleteArticle(news.id)}
|
||||
title="删除"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { NextResponse } from 'next/server'
|
||||
|
||||
export const dynamic = "force-static"
|
||||
|
||||
// 自动抓取接口(静态数据)
|
||||
export async function GET(request: Request) {
|
||||
return NextResponse.json({
|
||||
lastFetch: null,
|
||||
nextFetch: null,
|
||||
pending: [],
|
||||
approved: []
|
||||
})
|
||||
}
|
||||
|
||||
// 手动触发抓取(禁用)
|
||||
export async function POST(request: Request) {
|
||||
return NextResponse.json(
|
||||
{ error: '自动抓取功能已禁用' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import { NextResponse } from 'next/server'
|
||||
|
||||
export const dynamic = "force-static"
|
||||
|
||||
// 获取新闻列表(静态数据)
|
||||
export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const page = parseInt(searchParams.get('page') || '1')
|
||||
const limit = parseInt(searchParams.get('limit') || '10')
|
||||
const category = searchParams.get('category')
|
||||
|
||||
// 静态数据示例
|
||||
const allNews = [
|
||||
{
|
||||
id: 1,
|
||||
title: "白马家园荣获数字经济创新大奖",
|
||||
category: "公司新闻",
|
||||
excerpt: "白马家园在数字经济领域取得突破性进展,荣获行业创新大奖。",
|
||||
createdAt: new Date().toISOString(),
|
||||
source: '手动发布'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "AI数字人技术引领新零售变革",
|
||||
category: "行业洞察",
|
||||
excerpt: "白马家园推出的AI数字人解决方案正在重塑新零售体验。",
|
||||
createdAt: new Date(Date.now() - 86400000).toISOString(),
|
||||
source: '白马精选'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "收单外包服务机构备案正式获批",
|
||||
category: "公司新闻",
|
||||
excerpt: "白马家园成功获得收单外包服务机构备案,合规能力再上新台阶。",
|
||||
createdAt: new Date(Date.now() - 172800000).toISOString(),
|
||||
source: '手动发布'
|
||||
}
|
||||
]
|
||||
|
||||
// 分类筛选
|
||||
const filteredNews = category
|
||||
? allNews.filter(item => item.category === category)
|
||||
: allNews
|
||||
|
||||
// 分页
|
||||
const total = filteredNews.length
|
||||
const paginatedNews = filteredNews.slice((page - 1) * limit, page * limit)
|
||||
|
||||
return NextResponse.json({
|
||||
news: paginatedNews,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 创建新闻(禁用)
|
||||
export async function POST(request: Request) {
|
||||
return NextResponse.json(
|
||||
{ error: '新闻发布功能已禁用,请通过后台手动更新' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
|
@ -0,0 +1,109 @@
|
|||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { MapPin, UserPlus, Store, ArrowRight, MessageCircle, ScanQrCode } from 'lucide-react'
|
||||
|
||||
export default function ContactPage() {
|
||||
|
||||
return (
|
||||
<div className="pt-24 pb-20 bg-white">
|
||||
{/* Header */}
|
||||
<section className="container mx-auto px-4 md:px-6 mb-16 text-center">
|
||||
<h1 className="text-4xl font-bold text-secondary mb-4">合作加盟</h1>
|
||||
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
|
||||
无论您是希望成为区域合伙人,还是商家寻求数字化转型,我们都期待与您携手共赢。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div className="container mx-auto px-4 md:px-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
||||
|
||||
{/* Left: Partner Benefits & Info */}
|
||||
<div className="space-y-8">
|
||||
{/* Partner Benefits */}
|
||||
<Card className="border-0 shadow-lg bg-accent">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl flex items-center gap-2">
|
||||
<UserPlus className="w-5 h-5 text-primary" />
|
||||
合伙人权益
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex gap-4">
|
||||
<div className="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center shrink-0">
|
||||
<Store className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold">区域独家保护</h3>
|
||||
<p className="text-sm text-muted-foreground">享受区域内所有商家流水的分润,建立持久管道收益。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center shrink-0">
|
||||
<ArrowRight className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold">全套运营支持</h3>
|
||||
<p className="text-sm text-muted-foreground">提供技术培训、物料支持、招商会销支持,助您快速展业。</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Contact Info */}
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-2xl font-bold text-secondary">联系我们</h2>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center">
|
||||
<MapPin className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">公司地址</p>
|
||||
<p className="font-semibold text-lg">天津市武清区京津产业新城海达商务大厦</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Card className="border-0 shadow-lg">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<MessageCircle className="w-5 h-5 text-primary" />
|
||||
扫码联系客服
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="text-center">
|
||||
<div className="flex flex-col items-center space-y-6">
|
||||
<div className="relative">
|
||||
<img
|
||||
src="/baima-qrcode.png"
|
||||
alt="企业微信客服二维码"
|
||||
className="w-64 h-64 object-contain rounded-2xl shadow-2xl border-4 border-primary/20"
|
||||
/>
|
||||
<div className="absolute -bottom-2 -right-2 w-12 h-12 bg-primary rounded-full flex items-center justify-center shadow-lg">
|
||||
<ScanQrCode className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-xl font-bold text-gray-900">
|
||||
立即扫码,一对一沟通
|
||||
</h3>
|
||||
<p className="text-muted-foreground max-w-md">
|
||||
我们的专业团队将为您提供详细的产品咨询和合作方案,期待与您携手共赢。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-sm text-primary font-medium">
|
||||
<div className="w-2 h-2 bg-primary rounded-full animate-pulse" />
|
||||
客服在线中,快速响应
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
import { Hero } from '@/components/home/Hero'
|
||||
import { ProductPreview } from '@/components/home/ProductPreview'
|
||||
import { MissionVisionEN } from '@/components/home/MissionVisionEN'
|
||||
import { HonorSection } from '@/components/home/HonorSection'
|
||||
import { Infrastructure } from '@/components/home/Infrastructure'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex flex-col w-full overflow-x-hidden">
|
||||
{/* 1. Brand Positioning & Hero */}
|
||||
<Hero />
|
||||
|
||||
{/* 2. Core Product Matrix */}
|
||||
<div id="products" className="scroll-mt-20">
|
||||
<ProductPreview />
|
||||
</div>
|
||||
|
||||
{/* 3. Mission, Vision & Values */}
|
||||
<MissionVisionEN />
|
||||
|
||||
{/* 4. Authority Certifications & Technical Strength */}
|
||||
<HonorSection />
|
||||
|
||||
{/* 5. Ecological Infrastructure */}
|
||||
<Infrastructure />
|
||||
|
||||
{/* 7. Call to Action & Contact */}
|
||||
<section id="contact" className="py-24 bg-gradient-to-br from-[#0A1931] to-[#185ADB] text-white text-center scroll-mt-20">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl md:text-5xl font-black mb-8 leading-tight">
|
||||
Connect with 120,000+ Merchants<br/>Create a New Business Ecosystem
|
||||
</h2>
|
||||
<p className="text-lg md:text-xl text-gray-300 max-w-3xl mx-auto mb-12 leading-relaxed">
|
||||
Whether you're a brick-and-mortar business seeking transformation or an individual aspiring to entrepreneurship,
|
||||
Baima Home provides a broad platform. Our ecosystem is fully open to the public -
|
||||
click the button below to get in touch with us.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-6 justify-center">
|
||||
<Link
|
||||
href="/contact#form"
|
||||
className="inline-flex h-14 items-center justify-center rounded-full bg-[#FF6600] px-10 text-xl font-black shadow-2xl shadow-[#FF6600]/40 transition-all hover:bg-[#FF8C00] hover:scale-105"
|
||||
>
|
||||
Join Baima Alliance
|
||||
</Link>
|
||||
<Link
|
||||
href="/contact"
|
||||
className="inline-flex h-14 items-center justify-center rounded-full border-2 border-white/30 px-10 text-lg font-bold transition-all hover:bg-white/10"
|
||||
>
|
||||
Learn More
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
After Width: | Height: | Size: 257 KiB |
|
|
@ -0,0 +1,173 @@
|
|||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
|
||||
/* Brand Colors */
|
||||
--color-primary-blue: var(--primary-blue);
|
||||
--color-accent-blue: var(--accent-blue);
|
||||
--color-primary-orange: var(--primary-orange);
|
||||
--color-secondary-orange: var(--secondary-orange);
|
||||
|
||||
/* UI Colors */
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
|
||||
/* Radius */
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--radius-2xl: calc(var(--radius) + 8px);
|
||||
--radius-3xl: calc(var(--radius) + 12px);
|
||||
--radius-4xl: calc(var(--radius) + 16px);
|
||||
}
|
||||
|
||||
/* 融合配色方案 */
|
||||
|
||||
:root {
|
||||
/* 品牌深蓝色调 */
|
||||
--primary-blue: oklch(0.15 0.03 260);
|
||||
--accent-blue: oklch(0.45 0.15 240);
|
||||
|
||||
/* 活力橙 */
|
||||
--primary-orange: oklch(0.65 0.18 40);
|
||||
--secondary-orange: oklch(0.7 0.18 45);
|
||||
|
||||
/* 基础色 */
|
||||
--primary: var(--primary-orange);
|
||||
--primary-foreground: oklch(0.99 0 0);
|
||||
|
||||
--secondary: var(--primary-blue);
|
||||
--secondary-foreground: oklch(0.99 0 0);
|
||||
|
||||
--background: oklch(0.98 0.01 260);
|
||||
--foreground: oklch(0.15 0.02 260);
|
||||
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.15 0.02 260);
|
||||
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.15 0.02 260);
|
||||
|
||||
--accent: oklch(0.96 0.01 260);
|
||||
--accent-foreground: oklch(0.15 0.02 260);
|
||||
|
||||
--muted: oklch(0.94 0.01 260);
|
||||
--muted-foreground: oklch(0.5 0.02 260);
|
||||
|
||||
--destructive: oklch(0.55 0.2 25);
|
||||
--destructive-foreground: oklch(0.99 0 0);
|
||||
|
||||
--border: oklch(0.9 0.01 260);
|
||||
--input: oklch(0.9 0.01 260);
|
||||
--ring: var(--primary-orange);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground antialiased;
|
||||
}
|
||||
|
||||
/* 全局字体放大 */
|
||||
html {
|
||||
font-size: 17px; /* 基础字体稍大 */
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply font-black text-[#0A1931];
|
||||
font-size: 2.75rem;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply font-bold text-[#0A1931];
|
||||
font-size: 2.25rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply font-bold text-[#0A1931];
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
h4, h5, h6 {
|
||||
@apply font-semibold text-[#0A1931];
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
p {
|
||||
@apply leading-relaxed text-gray-600;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* 通用卡片悬浮效果 */
|
||||
.card-hover {
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
.card-hover:hover {
|
||||
transform: translateY(-15px);
|
||||
box-shadow: 0 20px 50px rgba(24, 90, 219, 0.15);
|
||||
border-color: var(--accent-blue);
|
||||
}
|
||||
}
|
||||
|
||||
/* 重点文字高亮样式 */
|
||||
.highlight-text {
|
||||
@apply text-[#FF6600] font-bold;
|
||||
}
|
||||
|
||||
.key-point {
|
||||
@apply font-bold text-[#0A1931] bg-gradient-to-r from-orange-50 to-orange-100 px-2 py-0.5 rounded;
|
||||
}
|
||||
|
||||
/* 响应式字体调整 */
|
||||
@layer utilities {
|
||||
@media (max-width: 768px) {
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.95rem !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import type { Metadata } from 'next'
|
||||
import { Geist, Geist_Mono } from 'next/font/google'
|
||||
import { Navbar } from '@/components/layout/Navbar'
|
||||
import { Footer } from '@/components/layout/Footer'
|
||||
import './globals.css'
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: '--font-geist-sans',
|
||||
subsets: ['latin'],
|
||||
})
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: '--font-geist-mono',
|
||||
subsets: ['latin'],
|
||||
})
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '白马家园—新质生产力产业集群',
|
||||
description: 'AI时代小微创业+消费循环生态圈,军工技术赋能实体经济,人工智能驱动万商互联。',
|
||||
icons: {
|
||||
icon: '/logos.png',
|
||||
},
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
}>) {
|
||||
return (
|
||||
<html lang="zh-CN" className="scroll-smooth">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
<Navbar />
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { Calendar, Eye, ArrowRight, Loader2 } from 'lucide-react'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import Link from 'next/link'
|
||||
|
||||
type News = {
|
||||
id: number
|
||||
title: string
|
||||
category: string
|
||||
excerpt: string
|
||||
createdAt: string
|
||||
views: number
|
||||
}
|
||||
|
||||
const categories = ["全部", "公司新闻", "荣誉获奖", "产品发布", "媒体报道", "行业洞察"]
|
||||
|
||||
export default function NewsPage() {
|
||||
const [news, setNews] = useState<News[]>([])
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [activeCategory, setActiveCategory] = useState("全部")
|
||||
|
||||
useEffect(() => {
|
||||
const fetchNews = async () => {
|
||||
try {
|
||||
const url = activeCategory === "全部"
|
||||
? '/api/news'
|
||||
: `/api/news?category=${encodeURIComponent(activeCategory)}`
|
||||
const res = await fetch(url)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setNews(data.news)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载新闻失败', error)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
fetchNews()
|
||||
}, [activeCategory])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white pt-24 pb-20">
|
||||
<div className="container mx-auto px-4 md:px-6">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-16">
|
||||
<h1 className="text-4xl md:text-5xl font-black text-[#0A1931] mb-4">
|
||||
最新动态
|
||||
</h1>
|
||||
<p className="text-lg text-gray-500 max-w-2xl mx-auto">
|
||||
了解白马家园最新资讯,分享我们的成长与思考
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Category Filter */}
|
||||
<div className="flex flex-wrap justify-center gap-2 mb-12">
|
||||
{categories.map((category) => (
|
||||
<button
|
||||
key={category}
|
||||
onClick={() => setActiveCategory(category)}
|
||||
className={`px-6 py-2 rounded-full text-sm font-bold transition-all ${
|
||||
activeCategory === category
|
||||
? 'bg-[#FF6600] text-white shadow-lg'
|
||||
: 'bg-gray-100 text-gray-500 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
{category}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Loading State */}
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center items-center py-20">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-[#FF6600]" />
|
||||
</div>
|
||||
) : news.length === 0 ? (
|
||||
<div className="text-center py-20 text-gray-400 font-medium">
|
||||
该分类下暂无动态
|
||||
</div>
|
||||
) : (
|
||||
/* News Grid */
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{news.map((item, index) => (
|
||||
<motion.div
|
||||
key={item.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
>
|
||||
<Link href="#" className="group block h-full">
|
||||
<Card className="h-full border-0 shadow-lg hover:shadow-xl transition-all duration-300 overflow-hidden card-hover">
|
||||
{/* Placeholder Color Block */}
|
||||
<div className="h-40 bg-gradient-to-br from-[#0A1931] to-[#185ADB] flex items-center justify-center">
|
||||
<span className="text-white/10 text-6xl font-black">{item.id}</span>
|
||||
</div>
|
||||
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className="text-xs font-bold text-[#FF6600] bg-orange-50 px-3 py-1 rounded-full">
|
||||
{item.category}
|
||||
</span>
|
||||
<div className="flex items-center text-xs text-gray-400 font-mono">
|
||||
<Calendar className="w-3 h-3 mr-1" />
|
||||
{new Date(item.createdAt).toLocaleDateString()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-bold text-[#0A1931] mb-3 group-hover:text-[#FF6600] transition-colors line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-sm text-gray-500 mb-6 line-clamp-3 leading-relaxed">
|
||||
{item.excerpt}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between pt-4 border-t border-gray-100">
|
||||
<div className="flex items-center text-xs text-gray-400">
|
||||
<Eye className="w-3 h-3 mr-1" />
|
||||
{item.views} 次阅读
|
||||
</div>
|
||||
<span className="flex items-center text-sm font-bold text-[#FF6600] group-hover:translate-x-1 transition-transform">
|
||||
阅读详情 <ArrowRight className="ml-1 w-4 h-4" />
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { Hero } from '@/components/home/Hero'
|
||||
import { ProductPreview } from '@/components/home/ProductPreview'
|
||||
import { MissionVision } from '@/components/home/MissionVision'
|
||||
import { HonorSection } from '@/components/home/HonorSection'
|
||||
import { Infrastructure } from '@/components/home/Infrastructure'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<main className="min-h-screen">
|
||||
{/* 1. Hero首屏 */}
|
||||
<Hero />
|
||||
|
||||
{/* 2. 产品矩阵 */}
|
||||
<ProductPreview />
|
||||
|
||||
{/* 3. 使命、愿景、价值观 */}
|
||||
<MissionVision />
|
||||
|
||||
{/* 4. 权威认证与技术实力 */}
|
||||
<HonorSection />
|
||||
|
||||
{/* 5. 生态基础设施 */}
|
||||
<Infrastructure />
|
||||
|
||||
{/* 6. 页脚 - 在 Layout.tsx 中 */}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
|
@ -0,0 +1,50 @@
|
|||
import { ProductList } from '@/components/products/ProductList'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import Link from 'next/link'
|
||||
import { ArrowRight } from 'lucide-react'
|
||||
|
||||
export default function ProductsPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-white pt-24 pb-20">
|
||||
<div className="container mx-auto px-4 md:px-6">
|
||||
{/* Page Header */}
|
||||
<div className="max-w-3xl mx-auto text-center mb-16">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-secondary mb-6">
|
||||
产品服务
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground leading-relaxed">
|
||||
基于军工级技术底座,我们构建了从支付结算、私域运营到异业联盟的完整生态闭环,
|
||||
助力小微企业在 AI 时代实现数字化升级与业绩倍增。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Navigation Tabs */}
|
||||
<div className="sticky top-20 z-40 bg-white/95 backdrop-blur-md py-4 mb-8 border-b border-gray-100 hidden md:block">
|
||||
<nav className="flex justify-center gap-8">
|
||||
<a href="#fintech" className="text-sm font-medium text-muted-foreground hover:text-primary transition-colors">金融科技</a>
|
||||
<a href="#ai" className="text-sm font-medium text-muted-foreground hover:text-primary transition-colors">私域运营</a>
|
||||
<a href="#alliance" className="text-sm font-medium text-muted-foreground hover:text-primary transition-colors">异业联盟</a>
|
||||
<a href="#infrastructure" className="text-sm font-medium text-muted-foreground hover:text-primary transition-colors">生态基建</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<ProductList />
|
||||
|
||||
{/* CTA */}
|
||||
<div className="mt-24 text-center bg-accent rounded-2xl p-12">
|
||||
<h2 className="text-3xl font-bold text-secondary mb-4">准备好升级您的商业模式了吗?</h2>
|
||||
<p className="text-muted-foreground mb-8 max-w-2xl mx-auto">
|
||||
立即接入白马家园生态,开启数智化转型之旅。
|
||||
</p>
|
||||
<div className="flex justify-center gap-4">
|
||||
<Button size="lg" asChild className="bg-primary hover:bg-primary/90 text-white rounded-full px-8">
|
||||
<Link href="/contact">
|
||||
立即咨询 <ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "app/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"iconLibrary": "lucide",
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"registries": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
'use client'
|
||||
|
||||
import { Trophy, Award, FileText, Medal } from 'lucide-react'
|
||||
|
||||
const awards = [
|
||||
"2023 科大讯飞 AI 开发者大赛数字人赛道冠军",
|
||||
"2022 中国产业元宇宙创新大赛一等奖",
|
||||
"2023 元宇宙应用场景大赛产业生态先锋奖",
|
||||
"2024 退役军人创新创业大赛一等奖"
|
||||
]
|
||||
|
||||
const patents = [
|
||||
"发明专利:融合多信息的 AI 虚拟人与用户交互方法",
|
||||
"发明专利:一种虚拟人物不同表情模型生成方法",
|
||||
"80+ 项计算机软件著作权",
|
||||
"50+ 项资质证书与备案"
|
||||
]
|
||||
|
||||
export function Certifications() {
|
||||
return (
|
||||
<section className="py-20 bg-white" id="honor">
|
||||
<div className="container mx-auto px-4 md:px-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-12 items-center">
|
||||
{/* Text Content */}
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold text-secondary mb-6">荣誉与资质</h2>
|
||||
<p className="text-muted-foreground mb-8">
|
||||
凭借领先的技术实力与创新模式,白马家园荣获多项国家级大奖,并拥有完备的知识产权体系。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex gap-4">
|
||||
<div className="w-12 h-12 rounded-lg bg-primary/10 flex items-center justify-center shrink-0">
|
||||
<Trophy className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-lg mb-2">权威奖项</h3>
|
||||
<ul className="space-y-2">
|
||||
{awards.map((award, i) => (
|
||||
<li key={i} className="flex items-center text-sm text-muted-foreground">
|
||||
<span className="w-1.5 h-1.5 bg-primary rounded-full mr-2"></span>
|
||||
{award}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<div className="w-12 h-12 rounded-lg bg-primary/10 flex items-center justify-center shrink-0">
|
||||
<FileText className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-lg mb-2">知识产权</h3>
|
||||
<ul className="space-y-2">
|
||||
{patents.map((patent, i) => (
|
||||
<li key={i} className="flex items-center text-sm text-muted-foreground">
|
||||
<span className="w-1.5 h-1.5 bg-primary rounded-full mr-2"></span>
|
||||
{patent}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Certificate Cards */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="aspect-[3/4] bg-accent rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col items-center justify-center text-center">
|
||||
<Medal className="w-12 h-12 text-primary mb-4" />
|
||||
<span className="text-xs font-medium text-muted-foreground">科大讯飞冠军证书</span>
|
||||
</div>
|
||||
<div className="aspect-[3/4] bg-accent rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col items-center justify-center text-center mt-8">
|
||||
<Award className="w-12 h-12 text-primary mb-4" />
|
||||
<span className="text-xs font-medium text-muted-foreground">发明专利证书</span>
|
||||
</div>
|
||||
<div className="aspect-[3/4] bg-accent rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col items-center justify-center text-center">
|
||||
<Trophy className="w-12 h-12 text-primary mb-4" />
|
||||
<span className="text-xs font-medium text-muted-foreground">创新大赛一等奖</span>
|
||||
</div>
|
||||
<div className="aspect-[3/4] bg-accent rounded-lg shadow-sm border border-gray-100 p-4 flex flex-col items-center justify-center text-center mt-8">
|
||||
<FileText className="w-12 h-12 text-primary mb-4" />
|
||||
<span className="text-xs font-medium text-muted-foreground">收单外包服务机构备案</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
'use client'
|
||||
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { User } from 'lucide-react'
|
||||
|
||||
const team = [
|
||||
{
|
||||
name: "汪洋",
|
||||
role: "董事长",
|
||||
badges: ["国防科大本硕博", "高级工程师", "上校转业"],
|
||||
description: "深度参与神舟、嫦娥、北斗等航天任务。获中国青年科技奖,全网粉丝100万+ AIGC/元宇宙博主。"
|
||||
},
|
||||
{
|
||||
name: "段海龙",
|
||||
role: "技术总监",
|
||||
badges: ["国防科大学士", "中科大硕士", "高级工程师"],
|
||||
description: "专注于人工智能、机器视觉方向。发表论文20多篇,软著及专利20余项。"
|
||||
},
|
||||
{
|
||||
name: "秦波",
|
||||
role: "总经理",
|
||||
badges: ["第一军医大学", "副主任医师", "正团职转业"],
|
||||
description: "资深健康养老产业链专家,军队医院保健专家。10年以上市场招商渠道拓展经验。"
|
||||
},
|
||||
{
|
||||
name: "焦文军",
|
||||
role: "CFO",
|
||||
badges: ["中国十大杰出CFO", "天狮前副总裁"],
|
||||
description: "多家港股、美股公司上市操盘手。中央财经大学、南开大学客座教授。"
|
||||
}
|
||||
]
|
||||
|
||||
export function Team() {
|
||||
return (
|
||||
<section className="py-20 bg-accent" id="team">
|
||||
<div className="container mx-auto px-4 md:px-6">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl font-bold text-secondary mb-4">核心精英团队</h2>
|
||||
<p className="text-muted-foreground max-w-2xl mx-auto">
|
||||
汇聚军工、医疗、金融等领域的顶尖人才,以硬核技术驱动商业创新。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
{team.map((member, index) => (
|
||||
<Card key={index} className="border-0 shadow-lg hover:shadow-xl transition-all">
|
||||
<CardHeader className="text-center pb-2">
|
||||
<div className="w-24 h-24 mx-auto bg-white rounded-full flex items-center justify-center mb-4 shadow-md">
|
||||
<User className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
<CardTitle className="text-xl font-bold">{member.name}</CardTitle>
|
||||
<CardDescription className="text-primary font-medium">{member.role}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="text-center">
|
||||
<div className="flex flex-wrap justify-center gap-2 mb-4">
|
||||
{member.badges.map((badge, bIndex) => (
|
||||
<Badge key={bIndex} variant="secondary" className="text-xs bg-primary/10 text-primary">
|
||||
{badge}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground text-left leading-relaxed">
|
||||
{member.description}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { User, ShieldCheck, TrendingUp } from 'lucide-react'
|
||||
|
||||
const team = [
|
||||
{
|
||||
name: "汪洋",
|
||||
role: "董事长",
|
||||
badges: ["国防科大本硕博", "航天级专家"],
|
||||
description: "深度参与神舟、北斗任务。全网粉丝100万+ AIGC头部博主。"
|
||||
},
|
||||
{
|
||||
name: "段海龙",
|
||||
role: "技术总监",
|
||||
badges: ["国防科大学士", "高级工程师"],
|
||||
description: "专注AI与机器视觉。发表论文20多篇,拥有专利20余项。"
|
||||
},
|
||||
{
|
||||
name: "秦波",
|
||||
role: "总经理",
|
||||
badges: ["第一军医大学", "正团职转业"],
|
||||
description: "资深康养专家,10年以上市场招商渠道拓展经验。"
|
||||
},
|
||||
{
|
||||
name: "焦文军",
|
||||
role: "CFO",
|
||||
badges: ["十大杰出CFO", "操盘上市"],
|
||||
description: "多家上市公司操盘手。中央财经大学、南开大学客座教授。"
|
||||
}
|
||||
]
|
||||
|
||||
export function AboutSection() {
|
||||
return (
|
||||
<section id="about" className="py-24 bg-white scroll-mt-20">
|
||||
<div className="container mx-auto px-4 md:px-6">
|
||||
<div className="max-w-4xl mx-auto text-center mb-20">
|
||||
<Badge variant="outline" className="mb-4 border-[#FF6600] text-[#FF6600] bg-orange-50 font-bold">
|
||||
关于白马家园
|
||||
</Badge>
|
||||
<h2 className="text-3xl md:text-5xl font-black text-[#0A1931] mb-6">
|
||||
军工技术赋能实体经济
|
||||
</h2>
|
||||
<p className="text-lg text-gray-500 leading-relaxed">
|
||||
白马家园是一家拥有国资背景的高新技术企业,已获五轮融资超 2 亿元。
|
||||
我们致力于解决实体商家“流量难、锁客难、变现难”的痛点,
|
||||
构建万商互联的共享经济生态。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 核心团队 */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 md:gap-8">
|
||||
{team.map((member, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
>
|
||||
<Card className="border-0 shadow-lg hover:shadow-xl transition-all h-full bg-gray-50/50 group">
|
||||
<CardHeader className="text-center pb-4">
|
||||
<div className="w-20 h-20 mx-auto bg-white rounded-2xl flex items-center justify-center mb-4 shadow-sm group-hover:bg-[#FF6600] transition-colors duration-300">
|
||||
<User className="w-10 h-10 text-[#0A1931] group-hover:text-white" />
|
||||
</div>
|
||||
<CardTitle className="text-xl font-bold text-[#0A1931]">{member.name}</CardTitle>
|
||||
<CardDescription className="text-[#FF6600] font-bold">{member.role}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-wrap justify-center gap-1.5 mb-4">
|
||||
{member.badges.map((badge, bIndex) => (
|
||||
<span key={bIndex} className="text-[10px] font-bold bg-white px-2 py-0.5 rounded border border-gray-100 text-gray-500">
|
||||
{badge}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 leading-relaxed text-center">
|
||||
{member.description}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { CheckCircle2, TrendingUp, Users, Building2 } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import Link from 'next/link'
|
||||
|
||||
export function Ecosystem() {
|
||||
return (
|
||||
<section className="py-24 bg-secondary text-white relative overflow-hidden">
|
||||
{/* Background Effects */}
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<div className="absolute -top-40 -right-40 w-80 h-80 bg-primary/20 rounded-full blur-3xl"></div>
|
||||
<div className="absolute -bottom-40 -left-40 w-80 h-80 bg-primary/10 rounded-full blur-3xl"></div>
|
||||
</div>
|
||||
|
||||
<div className="container px-4 md:px-6 mx-auto relative z-10">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
|
||||
{/* Left Content */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -50 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<h2 className="text-3xl md:text-5xl font-bold mb-6">
|
||||
数据收益
|
||||
<span className="text-primary"> 优先分配</span>
|
||||
</h2>
|
||||
<p className="text-lg text-gray-300 mb-8 leading-relaxed">
|
||||
白马家园首创"数据收益优先分配权",让创造数据的人优先分享数据带来的增值红利。
|
||||
我们相信,只有相互成就,才能共建可持续发展的商业生态。
|
||||
</p>
|
||||
|
||||
{/* Features List */}
|
||||
<div className="space-y-5 mb-10">
|
||||
{[
|
||||
{ icon: TrendingUp, text: '时时/单单/天天补贴,立即到账' },
|
||||
{ icon: Users, text: '用户消费即参与利润分配' },
|
||||
{ icon: Building2, text: '商家让利即获得流量回馈' },
|
||||
{ icon: CheckCircle2, text: '透明公开,无泡沫正向拨比' }
|
||||
].map((item, index) => (
|
||||
<div key={index} className="flex items-center gap-4">
|
||||
<div className="w-10 h-10 bg-primary/20 rounded-lg flex items-center justify-center">
|
||||
<item.icon className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<span className="text-gray-200">{item.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Button asChild className="bg-primary hover:bg-primary/90 text-white rounded-full px-8 h-12">
|
||||
<Link href="/contact">立即加入生态</Link>
|
||||
</Button>
|
||||
</motion.div>
|
||||
|
||||
{/* Right Visual */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="relative"
|
||||
>
|
||||
{/* Central Circle */}
|
||||
<div className="relative aspect-square w-full max-w-md mx-auto">
|
||||
{/* Outer Ring */}
|
||||
<div className="absolute inset-0 border-2 border-white/10 rounded-full"></div>
|
||||
|
||||
{/* Inner Content */}
|
||||
<div className="absolute inset-8 border border-white/10 rounded-full flex items-center justify-center">
|
||||
<div className="w-32 h-32 bg-primary rounded-full flex items-center justify-center shadow-lg shadow-primary/30">
|
||||
<span className="font-bold text-white text-lg">白马家园</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Orbiting Elements */}
|
||||
{[
|
||||
{ angle: 0, label: '消费者' },
|
||||
{ angle: 90, label: '商家' },
|
||||
{ angle: 180, label: '供应链' },
|
||||
{ angle: 270, label: '平台' }
|
||||
].map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="absolute w-20 h-20 bg-white/10 backdrop-blur-sm rounded-xl flex items-center justify-center border border-white/20"
|
||||
style={{
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: `translate(-50%, -50%) rotate(${item.angle}deg) translateY(-140px) rotate(-${item.angle}deg)`
|
||||
}}
|
||||
>
|
||||
<span className="text-sm font-medium">{item.label}</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Connection Lines */}
|
||||
<svg className="absolute inset-0 w-full h-full pointer-events-none opacity-20">
|
||||
<circle cx="50%" cy="50%" r="140" fill="none" stroke="white" strokeWidth="1" strokeDasharray="5,5" />
|
||||
</svg>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import {
|
||||
ShieldCheck,
|
||||
Cpu,
|
||||
TrendingUp,
|
||||
Users,
|
||||
Globe,
|
||||
Award
|
||||
} from 'lucide-react'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: ShieldCheck,
|
||||
title: "国资背景",
|
||||
description: "获五轮融资超2亿元,中华财政部、社保基金理事会等间接持股,实力雄厚,稳健经营。"
|
||||
},
|
||||
{
|
||||
icon: Cpu,
|
||||
title: "军工技术",
|
||||
description: "国防科大核心团队,拥有80+项发明专利。将航天级技术应用于实体经济数字化转型。"
|
||||
},
|
||||
{
|
||||
icon: TrendingUp,
|
||||
title: "数据收益",
|
||||
description: "首创'数据收益优先分配权',让用户和商家共享数据增值红利,打造可持续商业模式。"
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: "顶尖团队",
|
||||
description: "汇聚国防科大、中科大等名校精英,以及上市企业高管、杰出CFO等行业领袖。"
|
||||
},
|
||||
{
|
||||
icon: Globe,
|
||||
title: "合规保障",
|
||||
description: "拥有收单外包服务机构备案,资金受银监会监管。数商备案接入,工商税务监管系统实时对接。"
|
||||
},
|
||||
{
|
||||
icon: Award,
|
||||
title: "权威认可",
|
||||
description: "科大讯飞AI开发者大赛冠军、中国产业元宇宙创新大赛一等奖,央视等权威媒体多次报道。"
|
||||
}
|
||||
]
|
||||
|
||||
export function Features() {
|
||||
return (
|
||||
<section className="py-24 bg-white">
|
||||
<div className="container px-4 md:px-6 mx-auto">
|
||||
{/* Section Header */}
|
||||
<div className="text-center mb-16">
|
||||
<motion.h2
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-3xl md:text-4xl font-bold text-secondary mb-4"
|
||||
>
|
||||
核心优势
|
||||
</motion.h2>
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.1 }}
|
||||
className="text-lg text-muted-foreground max-w-2xl mx-auto"
|
||||
>
|
||||
依托强大的技术壁垒与资本实力,为实体经济数字化转型提供坚实保障
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Features Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{features.map((feature, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
>
|
||||
<Card className="h-full border-0 shadow-lg hover:shadow-xl transition-all duration-300 group bg-accent">
|
||||
<CardHeader className="pb-4">
|
||||
<div className="w-14 h-14 bg-primary/10 rounded-xl flex items-center justify-center mb-4 group-hover:bg-primary group-hover:text-white transition-all duration-300">
|
||||
<feature.icon className="w-7 h-7 text-primary group-hover:text-white" />
|
||||
</div>
|
||||
<CardTitle className="text-xl font-bold text-secondary group-hover:text-primary transition-colors">
|
||||
{feature.title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
{feature.description}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import Image from 'next/image'
|
||||
import { ArrowRight } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<section className="relative w-full min-h-screen flex items-center justify-center overflow-hidden bg-[#050A14] pt-16">
|
||||
{/* Background Gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[#0A1931] via-[#0d1f45] to-[#050A14]"></div>
|
||||
|
||||
{/* Grid Effect */}
|
||||
<div className="absolute inset-0 opacity-20">
|
||||
<svg className="w-full h-full hidden sm:block">
|
||||
<defs>
|
||||
<pattern id="grid-blue" width="60" height="60" patternUnits="userSpaceOnUse">
|
||||
<path d="M 60 0 L 0 0 0 60" fill="none" stroke="white" strokeWidth="0.5" strokeOpacity="0.3"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#grid-blue)" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Glow Effects */}
|
||||
<div className="absolute top-1/4 left-1/4 w-64 h-64 md:w-96 md:h-96 bg-[#185ADB] rounded-full blur-[100px] md:blur-[150px] opacity-20 animate-pulse hidden sm:block"></div>
|
||||
<div className="absolute bottom-1/4 right-1/4 w-48 h-48 md:w-80 md:h-80 bg-[#FF6600] rounded-full blur-[80px] md:blur-[120px] opacity-15 hidden sm:block"></div>
|
||||
|
||||
<div className="container relative z-10 px-4 md:px-6 mx-auto">
|
||||
<div className="max-w-5xl mx-auto text-center mb-3">
|
||||
|
||||
{/* Logo */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="mb-6 md:mb-10"
|
||||
>
|
||||
<div className="relative h-16 w-auto aspect-[2/1] mx-auto sm:h-20 md:h-28">
|
||||
<Image
|
||||
src="/baima-logo.png"
|
||||
alt="白马家园 Logo"
|
||||
fill
|
||||
className="object-contain"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Tags - 放大并突出 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="flex flex-wrap justify-center gap-3 md:gap-4 mb-8 md:mb-10"
|
||||
>
|
||||
{[
|
||||
"小微企业数智化升级",
|
||||
"AI + Fintech 生态圈",
|
||||
"军工技术赋能"
|
||||
].map((tag, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="px-4 py-2 md:px-5 md:py-2.5 bg-white/10 border border-white/20 rounded-full text-sm md:text-base font-bold text-white backdrop-blur-sm shadow-lg"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
{/* Headline - 超大号字体,核心卖点突出 */}
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.1 }}
|
||||
className="text-3xl sm:text-5xl md:text-6xl lg:text-7xl font-black text-white mb-6 md:mb-8 leading-tight"
|
||||
>
|
||||
<span className="block mb-2 text-xl sm:text-2xl md:text-3xl font-light text-gray-300">
|
||||
实体商业的
|
||||
</span>
|
||||
<span className="text-[#FF6600] text-4xl sm:text-6xl md:text-7xl lg:text-8xl block">
|
||||
AI 数智增长引擎
|
||||
</span>
|
||||
</motion.h1>
|
||||
|
||||
{/* Subheadline - 核心价值主张,大字展示 */}
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
className="text-xl sm:text-2xl md:text-3xl font-bold text-[#FF6600] mb-6 md:mb-8 tracking-wide"
|
||||
>
|
||||
支付即锁粉 · 让利即引流 · 跨界即分润
|
||||
</motion.p>
|
||||
|
||||
{/* Description */}
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.3 }}
|
||||
className="text-base sm:text-lg md:text-xl text-gray-300 mb-10 md:mb-12 max-w-4xl mx-auto leading-relaxed font-medium"
|
||||
>
|
||||
连接 <span className="text-white font-bold">120,000+</span> 实体商户,重构线下流量价值闭环。<br className="hidden sm:block"/>
|
||||
军工技术赋能 · 人工智能驱动万商互联
|
||||
</motion.p>
|
||||
|
||||
{/* CTA Button - 超大醒目按钮 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
>
|
||||
<Link
|
||||
href="/contact#form"
|
||||
className="group relative inline-flex items-center gap-3 px-8 py-4 md:px-10 md:py-5 bg-gradient-to-r from-[#FF6600] to-[#FF8C00] text-lg md:text-xl font-black rounded-full shadow-2xl shadow-[#FF6600]/50 transition-all duration-300 hover:scale-105 hover:shadow-[#FF6600]/70"
|
||||
>
|
||||
<span className="text-lg md:text-xl">立即加入白马联盟</span>
|
||||
<ArrowRight className="w-6 h-6 md:w-7 md:h-7 transition-transform group-hover:translate-x-1" />
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scroll Indicator */}
|
||||
<motion.div
|
||||
animate={{ y: [0, 10, 0] }}
|
||||
transition={{ duration: 2, repeat: Infinity }}
|
||||
className="absolute bottom-4 md:bottom-8 left-1/2 -translate-x-1/2 text-white/30 hidden sm:block"
|
||||
>
|
||||
<div className="w-6 h-10 md:w-7 md:h-12 border-2 border-current rounded-full flex items-start justify-center p-1.5 md:p-2">
|
||||
<div className="w-1.5 h-2 md:w-2 md:h-3 bg-current rounded-full"></div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { Trophy, Award, FileText, Medal, Shield, Zap, Users, Globe, CheckCircle2 } from 'lucide-react'
|
||||
|
||||
// 扩充后的资质和知识产权数据
|
||||
const honors = [
|
||||
{
|
||||
icon: Trophy,
|
||||
title: "科大讯飞 AI 开发者大赛",
|
||||
subtitle: "数字人赛道冠军",
|
||||
color: "bg-orange-100 text-orange-600"
|
||||
},
|
||||
{
|
||||
icon: Medal,
|
||||
title: "中国产业元宇宙创新大赛",
|
||||
subtitle: "一等奖",
|
||||
color: "bg-blue-100 text-blue-600"
|
||||
},
|
||||
{
|
||||
icon: Award,
|
||||
title: "退役军人创新创业大赛",
|
||||
subtitle: "一等奖",
|
||||
color: "bg-purple-100 text-purple-600"
|
||||
},
|
||||
{
|
||||
icon: Globe,
|
||||
title: "元宇宙应用场景大赛",
|
||||
subtitle: "产业生态先锋奖",
|
||||
color: "bg-green-100 text-green-600"
|
||||
}
|
||||
]
|
||||
|
||||
const patents = [
|
||||
{
|
||||
title: "融合多信息的 AI 虚拟人与用户交互方法",
|
||||
type: "发明专利"
|
||||
},
|
||||
{
|
||||
title: "一种虚拟人物不同表情模型生成方法",
|
||||
type: "发明专利"
|
||||
},
|
||||
{
|
||||
title: "基于深度学习的智能收银系统",
|
||||
type: "发明专利"
|
||||
},
|
||||
{
|
||||
title: "多通道智能路由分配算法",
|
||||
type: "发明专利"
|
||||
},
|
||||
{
|
||||
title: "区块链数据确权与价值分配系统",
|
||||
type: "发明专利"
|
||||
},
|
||||
{
|
||||
title: "AI 驱动的精准营销推荐系统",
|
||||
type: "发明专利"
|
||||
}
|
||||
]
|
||||
|
||||
const certifications = [
|
||||
{
|
||||
title: "收单外包服务机构备案",
|
||||
desc: "中国支付清算协会备案,合规开展收单外包服务"
|
||||
},
|
||||
{
|
||||
title: "ISO 9001 质量管理体系认证",
|
||||
desc: "国际标准化组织认证"
|
||||
},
|
||||
{
|
||||
title: "高新技术企业认定",
|
||||
desc: "享受国家高新技术企业税收优惠"
|
||||
},
|
||||
{
|
||||
title: "软件企业认定",
|
||||
desc: "符合国家软件产业政策要求"
|
||||
},
|
||||
{
|
||||
title: "信息安全管理系统认证",
|
||||
desc: "保障用户数据安全"
|
||||
},
|
||||
{
|
||||
title: "数商备案接入",
|
||||
desc: "合规参与数据要素市场"
|
||||
}
|
||||
]
|
||||
|
||||
export function HonorSection() {
|
||||
return (
|
||||
<section id="honor" className="py-24 bg-[#F4F7FB] scroll-mt-20">
|
||||
<div className="container mx-auto px-4 md:px-6">
|
||||
|
||||
{/* 大标题 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h2 className="text-3xl md:text-5xl font-black text-[#0A1931] mb-4">
|
||||
权威认证 · 技术实力
|
||||
</h2>
|
||||
<p className="text-lg text-gray-500 max-w-3xl mx-auto">
|
||||
白马家园拥有完备的知识产权体系和权威资质认证,为您的商业合作保驾护航
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* 1. 权威奖项展示 - 大卡片风格 */}
|
||||
<div className="mb-20">
|
||||
<h3 className="text-2xl font-bold text-[#0A1931] mb-8 flex items-center gap-3">
|
||||
<Trophy className="w-8 h-8 text-[#FF6600]" />
|
||||
权威奖项与认可
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{honors.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="bg-white p-8 rounded-2xl shadow-lg hover:shadow-xl transition-all hover:-translate-y-1 border border-gray-100"
|
||||
>
|
||||
<div className={`w-16 h-16 ${item.color} rounded-2xl flex items-center justify-center mb-6`}>
|
||||
<item.icon className="w-8 h-8" />
|
||||
</div>
|
||||
<h4 className="text-xl font-bold text-[#0A1931] mb-2">{item.title}</h4>
|
||||
<p className="text-[#FF6600] font-bold">{item.subtitle}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 2. 知识产权展示 - 网格布局 */}
|
||||
<div className="mb-20">
|
||||
<h3 className="text-2xl font-bold text-[#0A1931] mb-8 flex items-center gap-3">
|
||||
<FileText className="w-8 h-8 text-[#185ADB]" />
|
||||
自主知识产权 (80+ 项)
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{patents.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.05 }}
|
||||
className="bg-white p-5 rounded-xl border border-gray-200 hover:border-[#185ADB] hover:shadow-md transition-all flex items-start gap-4"
|
||||
>
|
||||
<div className="w-10 h-10 bg-[#185ADB]/10 rounded-lg flex items-center justify-center shrink-0 mt-1">
|
||||
<Shield className="w-5 h-5 text-[#185ADB]" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-bold text-[#0A1931] mb-1">{item.title}</h4>
|
||||
<span className="text-xs font-bold text-[#185ADB] bg-[#185ADB]/10 px-2 py-0.5 rounded">{item.type}</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
{/* 更多专利提示 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.4 }}
|
||||
className="bg-gradient-to-br from-[#0A1931] to-[#185ADB] p-5 rounded-xl text-white flex items-center justify-center"
|
||||
>
|
||||
<div className="text-center">
|
||||
<Zap className="w-10 h-10 mx-auto mb-2 text-[#FF6600]" />
|
||||
<p className="font-bold text-lg">80+ 项</p>
|
||||
<p className="text-sm text-gray-300">软件著作权与专利</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 3. 资质认证展示 - 详情列表 */}
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold text-[#0A1931] mb-8 flex items-center gap-3">
|
||||
<CheckCircle2 className="w-8 h-8 text-green-600" />
|
||||
权威资质认证
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{certifications.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="bg-white p-6 rounded-2xl shadow-sm border border-gray-100 hover:shadow-md transition-all"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-12 h-12 bg-green-50 rounded-xl flex items-center justify-center shrink-0">
|
||||
<CheckCircle2 className="w-6 h-6 text-green-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-bold text-[#0A1931] mb-2">{item.title}</h4>
|
||||
<p className="text-sm text-gray-500 leading-relaxed">{item.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 底部强调 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="mt-16 bg-gradient-to-r from-[#0A1931] to-[#185ADB] rounded-3xl p-10 text-white text-center"
|
||||
>
|
||||
<h4 className="text-2xl font-black mb-4">合规保障 · 安全无忧</h4>
|
||||
<p className="text-lg text-gray-300 max-w-2xl mx-auto">
|
||||
我们拥有收单外包服务机构备案,资金受银监会监管,数商备案接入,工商税务监管系统实时对接,
|
||||
为您的每一笔交易保驾护航。
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { Diamond, Truck, Eye, Coins } from 'lucide-react'
|
||||
|
||||
const infraItems = [
|
||||
{
|
||||
icon: Diamond,
|
||||
title: "智慧会员体系",
|
||||
description: "一次注册,全城打通<br/>全平台消费数据互通"
|
||||
},
|
||||
{
|
||||
icon: Truck,
|
||||
title: "供应链赋能",
|
||||
description: "积分当钱花,进货更便宜<br/>源头工厂直供去库存"
|
||||
},
|
||||
{
|
||||
icon: Eye,
|
||||
title: "智能巡店系统",
|
||||
description: "手机看店,AI管店<br/>客流热力图实时分析"
|
||||
},
|
||||
{
|
||||
icon: Coins,
|
||||
title: "数智积分体系",
|
||||
description: "基于真实消费产生<br/>拒绝泡沫,价值恒定"
|
||||
}
|
||||
]
|
||||
|
||||
export function Infrastructure() {
|
||||
return (
|
||||
<section className="py-12 md:py-16 lg:py-20 bg-[#F4F7FB]">
|
||||
<div className="container px-4 md:px-6 mx-auto">
|
||||
{/* Section Header - 响应式 */}
|
||||
<div className="text-center mb-10 md:mb-16">
|
||||
<h2 className="text-xl sm:text-2xl md:text-3xl lg:text-4xl font-bold text-[#0A1931] mb-3 md:mb-4">
|
||||
统一高效的生态基础设施
|
||||
</h2>
|
||||
{/* 已移除:为上市合规打造的底层架构 */}
|
||||
</div>
|
||||
|
||||
{/* Grid - 响应式布局 */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
|
||||
{infraItems.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
className="bg-white p-4 md:p-6 lg:p-8 text-center rounded-lg md:rounded-xl border-b-4 border-transparent hover:border-[#FF6600] hover:shadow-lg transition-all duration-300"
|
||||
>
|
||||
{/* Icon - 响应式 */}
|
||||
<div className="w-10 h-10 sm:w-12 sm:h-12 md:w-16 md:h-16 bg-[#0A1931]/5 rounded-lg md:rounded-xl flex items-center justify-center mx-auto mb-4 md:mb-6">
|
||||
<item.icon className="w-5 h-5 sm:w-6 sm:h-6 md:w-8 md:h-8 text-[#185ADB]" />
|
||||
</div>
|
||||
{/* Title - 响应式 */}
|
||||
<h3 className="text-sm sm:text-base md:text-lg font-bold text-[#0A1931] mb-2 md:mb-3">
|
||||
{item.title}
|
||||
</h3>
|
||||
{/* Description - 响应式 */}
|
||||
<p className="text-xs sm:text-sm text-gray-500 leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: item.description }}>
|
||||
</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { Target, Eye, Heart, Star, Shield, Users, TrendingUp } from 'lucide-react'
|
||||
|
||||
export function MissionVision() {
|
||||
const coreValues = [
|
||||
{
|
||||
icon: Target,
|
||||
title: "使命",
|
||||
content: "军工技术赋能实体经济<br/>人工智能驱动万商互联",
|
||||
color: "from-orange-500 to-red-500"
|
||||
},
|
||||
{
|
||||
icon: Eye,
|
||||
title: "愿景",
|
||||
content: "天下白马是一家<br/>一起赚钱一起花",
|
||||
color: "from-blue-500 to-indigo-600"
|
||||
},
|
||||
{
|
||||
icon: Heart,
|
||||
title: "价值观",
|
||||
content: "共创 · 共享 · 共富",
|
||||
color: "from-green-500 to-emerald-600"
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<section className="py-24 bg-gradient-to-br from-[#0A1931] via-[#0d1f45] to-[#050A14] relative overflow-hidden">
|
||||
{/* 背景装饰 */}
|
||||
<div className="absolute inset-0">
|
||||
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-[#FF6600] rounded-full blur-[150px] opacity-10"></div>
|
||||
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-[#185ADB] rounded-full blur-[150px] opacity-10"></div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 relative z-10">
|
||||
{/* 标题 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h2 className="text-3xl md:text-5xl font-black text-white mb-4">
|
||||
我们的信仰
|
||||
</h2>
|
||||
<p className="text-gray-400 text-lg max-w-2xl mx-auto">
|
||||
坚守初心,砥砺前行,白马家园与您携手共创美好未来
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* 三大核心支柱 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{coreValues.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.2 }}
|
||||
className="group"
|
||||
>
|
||||
<div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 border border-white/10 hover:border-[#FF6600]/50 transition-all duration-500 h-full">
|
||||
{/* 图标 */}
|
||||
<div className={`w-20 h-20 mx-auto mb-6 rounded-2xl bg-gradient-to-r ${item.color} flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300`}>
|
||||
<item.icon className="w-10 h-10 text-white" />
|
||||
</div>
|
||||
|
||||
{/* 标题 */}
|
||||
<h3 className="text-2xl font-bold text-white text-center mb-6">
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
{/* 内容 */}
|
||||
<div
|
||||
className="text-center text-gray-300 leading-loose text-lg"
|
||||
dangerouslySetInnerHTML={{ __html: item.content }}
|
||||
/>
|
||||
|
||||
{/* 装饰线 */}
|
||||
<div className="mt-6 flex justify-center">
|
||||
<div className={`w-16 h-1 bg-gradient-to-r ${item.color} rounded-full opacity-50`}></div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 底部标语 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="mt-16 text-center"
|
||||
>
|
||||
<div className="inline-flex items-center gap-4 bg-[#FF6600]/10 backdrop-blur-sm px-8 py-4 rounded-full border border-[#FF6600]/20">
|
||||
<Star className="w-6 h-6 text-[#FF6600]" />
|
||||
<span className="text-[#FF6600] font-bold text-lg">
|
||||
数智连接万商 共创美好未来
|
||||
</span>
|
||||
<Star className="w-6 h-6 text-[#FF6600]" />
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { Target, Eye, Heart, Star } from 'lucide-react'
|
||||
|
||||
export function MissionVisionEN() {
|
||||
const coreValues = [
|
||||
{
|
||||
icon: Target,
|
||||
title: "Mission",
|
||||
content: "Military-grade Technology Empowers Physical Economy<br/>AI Drives Universal Business Connectivity",
|
||||
color: "from-orange-500 to-red-500"
|
||||
},
|
||||
{
|
||||
icon: Eye,
|
||||
title: "Vision",
|
||||
content: "One Baima Family Worldwide<br/>Grow Wealth Together, Spend Together",
|
||||
color: "from-blue-500 to-indigo-600"
|
||||
},
|
||||
{
|
||||
icon: Heart,
|
||||
title: "Values",
|
||||
content: "Co-create · Share · Prosper Together",
|
||||
color: "from-green-500 to-emerald-600"
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<section className="py-24 bg-gradient-to-br from-[#0A1931] via-[#0d1f45] to-[#050A14] relative overflow-hidden">
|
||||
{/* 背景装饰 */}
|
||||
<div className="absolute inset-0">
|
||||
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-[#FF6600] rounded-full blur-[150px] opacity-10"></div>
|
||||
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-[#185ADB] rounded-full blur-[150px] opacity-10"></div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 relative z-10">
|
||||
{/* 标题 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h2 className="text-3xl md:text-5xl font-black text-white mb-4">
|
||||
Our Beliefs
|
||||
</h2>
|
||||
<p className="text-gray-400 text-lg max-w-2xl mx-auto">
|
||||
Staying True to Our Original Aspiration, Advancing with Determination - Baima Home Builds a Better Future Together
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* 三大核心支柱 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{coreValues.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.2 }}
|
||||
className="group"
|
||||
>
|
||||
<div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 border border-white/10 hover:border-[#FF6600]/50 transition-all duration-500 h-full">
|
||||
{/* 图标 */}
|
||||
<div className={`w-20 h-20 mx-auto mb-6 rounded-2xl bg-gradient-to-r ${item.color} flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300`}>
|
||||
<item.icon className="w-10 h-10 text-white" />
|
||||
</div>
|
||||
|
||||
{/* 标题 */}
|
||||
<h3 className="text-2xl font-bold text-white text-center mb-6">
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
{/* 内容 */}
|
||||
<div
|
||||
className="text-center text-gray-300 leading-loose text-lg"
|
||||
dangerouslySetInnerHTML={{ __html: item.content }}
|
||||
/>
|
||||
|
||||
{/* 装饰线 */}
|
||||
<div className="mt-6 flex justify-center">
|
||||
<div className={`w-16 h-1 bg-gradient-to-r ${item.color} rounded-full opacity-50`}></div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 底部标语 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="mt-16 text-center"
|
||||
>
|
||||
<div className="inline-flex items-center gap-4 bg-[#FF6600]/10 backdrop-blur-sm px-8 py-4 rounded-full border border-[#FF6600]/20">
|
||||
<Star className="w-6 h-6 text-[#FF6600]" />
|
||||
<span className="text-[#FF6600] font-bold text-lg">
|
||||
Digital Intelligence Connects Businesses, Creates Better Future
|
||||
</span>
|
||||
<Star className="w-6 h-6 text-[#FF6600]" />
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { Calendar, ArrowRight, Loader2 } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
type News = {
|
||||
id: number
|
||||
title: string
|
||||
category: string
|
||||
excerpt: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export function NewsSection() {
|
||||
const [news, setNews] = useState<News[]>([])
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/api/news?limit=3')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
setNews(data.news)
|
||||
setIsLoading(false)
|
||||
})
|
||||
.catch(() => setIsLoading(false))
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<section id="news" className="py-24 bg-white scroll-mt-20">
|
||||
<div className="container mx-auto px-4 md:px-6">
|
||||
<div className="flex flex-col md:flex-row justify-between items-end mb-16 gap-6">
|
||||
<div>
|
||||
<h2 className="text-3xl md:text-5xl font-black text-[#0A1931] mb-4">最新动态</h2>
|
||||
<p className="text-gray-500 text-lg">了解白马家园的成长足迹与行业洞察</p>
|
||||
</div>
|
||||
<Link href="/news" className="text-[#FF6600] font-bold flex items-center gap-2 hover:translate-x-1 transition-transform">
|
||||
查看全部动态 <ArrowRight className="w-5 h-5" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center py-20"><Loader2 className="w-8 h-8 animate-spin text-[#FF6600]" /></div>
|
||||
) : news.length === 0 ? (
|
||||
<div className="text-center py-20 text-gray-400 bg-gray-50 rounded-2xl">后台尚未发布任何动态</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{news.map((item, index) => (
|
||||
<Link href={`/news?id=${item.id}`} key={item.id}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="group p-6 rounded-2xl border border-gray-100 bg-white hover:shadow-xl transition-all cursor-pointer h-full"
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="text-[10px] font-black uppercase tracking-widest text-[#FF6600] bg-orange-50 px-2 py-0.5 rounded">
|
||||
{item.category}
|
||||
</span>
|
||||
<span className="text-xs text-gray-400 flex items-center font-mono">
|
||||
<Calendar className="w-3 h-3 mr-1" /> {new Date(item.createdAt).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-[#0A1931] mb-4 group-hover:text-[#FF6600] transition-colors line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 leading-relaxed mb-6 line-clamp-3">
|
||||
{item.excerpt}
|
||||
</p>
|
||||
<span className="text-sm font-bold text-[#0A1931] flex items-center gap-1 group-hover:gap-2 transition-all">
|
||||
阅读更多 <ArrowRight className="w-4 h-4" />
|
||||
</span>
|
||||
</motion.div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { CreditCard, Users, Network, Database } from 'lucide-react'
|
||||
|
||||
// 根据框图重构四大核心板块
|
||||
const products = [
|
||||
{
|
||||
title: '高效安全的金融科技支撑',
|
||||
enTitle: 'Fintech Support',
|
||||
description: '收单-分销-结算一体化,多通道AI路由,保障资金安全与高效分账。',
|
||||
icon: CreditCard,
|
||||
color: 'bg-gradient-to-br from-[#0A1931] to-[#185ADB]',
|
||||
features: [
|
||||
{ title: "白马智付", desc: "锁粉 / 自动分佣 / AI路由" },
|
||||
{ title: "智能设备", desc: "智慧收银系统 / 支持绿色积分 / 智能监控巡店" },
|
||||
{ title: "支付能力", desc: "主扫、被扫模式全支持 / 强大技术底座" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '简单易用的私域运营工具',
|
||||
enTitle: 'Private Domain Tools',
|
||||
description: 'AI驱动的私域流量运营神器,让获客、留存、转化变得简单高效。',
|
||||
icon: Users,
|
||||
color: 'bg-gradient-to-br from-[#FF6600] to-[#FF8C00]',
|
||||
features: [
|
||||
{ title: "AI数字人", desc: "AI直播/AI导购/AI客服/AI私域" },
|
||||
{ title: "积分权益", desc: "兑换AI直播 / 营销获客权益" },
|
||||
{ title: "企微私域", desc: "AI数字员工自动实现:社群运营/维护/导购/客服" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '轻松锁客的异业联盟系统',
|
||||
enTitle: 'Alliance System',
|
||||
description: '打破单打独斗,通过异业联盟锁定客户终身价值,实现跨界收益。',
|
||||
icon: Network,
|
||||
color: 'bg-gradient-to-br from-[#185ADB] to-[#0A1931]',
|
||||
features: [
|
||||
{ title: "锁定客户", desc: "扫码即锁定推荐关系,终身享受消费分佣" },
|
||||
{ title: "跨界分佣", desc: "赚360行的钱 / 跨门店收益" },
|
||||
{ title: "供应链分佣", desc: "分享优质供应链商品获利" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '统一高效的生态基础设施',
|
||||
enTitle: 'Ecological Infrastructure',
|
||||
description: '为小微企业提供完整的数智化基建,打通会员、资产与供应链。',
|
||||
icon: Database,
|
||||
color: 'bg-gradient-to-br from-[#059669] to-[#10B981]',
|
||||
features: [
|
||||
{ title: "会员系统", desc: "跨平台生态融合 / 统一身份" },
|
||||
{ title: "数字资产体系", desc: "RWA资产 / 积分、抵用券、金币等" },
|
||||
{ title: "供应链资源", desc: "稳定安全靠谱" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export function ProductPreview() {
|
||||
return (
|
||||
<section className="py-12 md:py-20 lg:py-24 bg-white">
|
||||
<div className="container px-4 md:px-6 mx-auto">
|
||||
{/* Section Header */}
|
||||
<div className="text-center mb-10 md:mb-16">
|
||||
<motion.h2
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold text-[#0A1931] mb-3 md:mb-4"
|
||||
>
|
||||
AI时代小微创业 + 消费循环生态圈
|
||||
</motion.h2>
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.1 }}
|
||||
className="text-sm sm:text-base md:text-lg text-gray-500 px-4"
|
||||
>
|
||||
四大核心数智化产品矩阵,全方位赋能实体商业
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Product Cards Grid - 2x2 Layout recommended for 4 items */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8 lg:gap-10">
|
||||
{products.map((product, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
className="group h-full"
|
||||
>
|
||||
<div className="card-hover bg-white rounded-xl md:rounded-2xl overflow-hidden border border-gray-100 h-full flex flex-col">
|
||||
{/* Header with Icon */}
|
||||
<div className="p-6 md:p-8 flex items-start justify-between border-b border-gray-50 bg-gray-50/50">
|
||||
<div>
|
||||
<h3 className="text-xl md:text-2xl font-bold text-[#0A1931] mb-1">
|
||||
{product.title}
|
||||
</h3>
|
||||
<span className="text-xs font-semibold text-[#FF6600] uppercase tracking-wider">
|
||||
{product.enTitle}
|
||||
</span>
|
||||
</div>
|
||||
<div className={`w-12 h-12 md:w-14 md:h-14 rounded-xl flex items-center justify-center text-white shadow-md ${product.color}`}>
|
||||
<product.icon className="w-6 h-6 md:w-7 md:h-7" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-6 md:p-8 flex-grow flex flex-col">
|
||||
<p className="text-sm text-gray-500 mb-6 leading-relaxed">
|
||||
{product.description}
|
||||
</p>
|
||||
|
||||
{/* Features List */}
|
||||
<ul className="space-y-3 md:space-y-4 mb-8 flex-grow">
|
||||
{product.features.map((feature, fIndex) => (
|
||||
<li key={fIndex} className="flex items-start text-sm">
|
||||
<span className="w-1.5 h-1.5 bg-[#FF6600] rounded-full mr-3 mt-1.5 shrink-0"></span>
|
||||
<div className="flex-1">
|
||||
<strong className="text-[#0A1931] block mb-0.5">{feature.title}</strong>
|
||||
<span className="text-gray-500 text-xs md:text-sm">{feature.desc}</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
const stats = [
|
||||
{ value: "120,000+", label: "服务实体商户", highlight: "12万+" },
|
||||
{ value: "500,000+", label: "日均交易笔数", highlight: "50万+" },
|
||||
{ value: "300万+", label: "累计引流人次", highlight: "300万+" },
|
||||
{ value: "20亿+", label: "平台年GMV流水", highlight: "20亿+" }
|
||||
]
|
||||
|
||||
export function Stats() {
|
||||
return (
|
||||
<section className="py-0">
|
||||
<div className="bg-gradient-to-r from-[#0A1931] via-[#185ADB] to-[#0A1931] text-white py-16 md:py-20">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-12 items-center">
|
||||
{stats.map((stat, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
className="text-center"
|
||||
>
|
||||
{/* 大号数字,突出显示 */}
|
||||
<h3 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-black text-[#FF6600] mb-2 md:mb-3 leading-none">
|
||||
{stat.highlight}
|
||||
</h3>
|
||||
{/* 下方详细说明 */}
|
||||
<p className="text-sm sm:text-base md:text-lg text-gray-300 font-medium uppercase tracking-wider">
|
||||
{stat.label}
|
||||
</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
'use client'
|
||||
|
||||
export function Trust() {
|
||||
const partners = [
|
||||
"微信支付认证服务商",
|
||||
"支付宝ISV合作伙伴",
|
||||
"中国银联",
|
||||
"启迪之星(投资方)",
|
||||
"中金资本生态成员",
|
||||
"OTC Markets (拟)"
|
||||
]
|
||||
|
||||
return (
|
||||
<section className="py-8 md:py-10 lg:py-12 bg-white border-t border-gray-100">
|
||||
<div className="container px-4 mx-auto">
|
||||
{/* 响应式标题 */}
|
||||
<p className="text-center text-gray-400 text-xs sm:text-sm font-bold uppercase tracking-[1px] md:tracking-[2px] mb-6 md:mb-8">
|
||||
Strategic Partners & Qualifications
|
||||
</p>
|
||||
{/* 响应式网格 */}
|
||||
<div className="flex flex-wrap justify-center gap-2 md:gap-4 px-2">
|
||||
{partners.map((partner, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="px-3 py-2 md:px-6 md:py-3 bg-white border border-gray-200 rounded-full text-xs sm:text-sm font-semibold text-gray-500 hover:border-[#185ADB] hover:text-[#185ADB] transition-colors cursor-default whitespace-nowrap"
|
||||
>
|
||||
{partner}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { MapPin } from 'lucide-react'
|
||||
|
||||
export function Footer() {
|
||||
const currentYear = new Date().getFullYear()
|
||||
const hostname = typeof window !== 'undefined' ? window.location.hostname : ''
|
||||
const icpNumber = hostname === 'www.baimajingxuan.com' ? '津ICP备2020008456号-1' : hostname === 'www.baimajiayuanjiankang.com' ? '津ICP备2023000762号' : '津ICP备99999999号'
|
||||
|
||||
return (
|
||||
<footer className="bg-[#050A14] text-gray-400 pt-16 pb-8">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-12 mb-12">
|
||||
{/* Brand Info */}
|
||||
<div className="lg:col-span-1">
|
||||
{/* Logo Image */}
|
||||
<div className="relative h-14 w-auto aspect-[2/1] mb-6">
|
||||
<Image
|
||||
src="/baima-logo.png"
|
||||
alt="白马家园 Logo"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm leading-relaxed mb-6 text-gray-500">
|
||||
军工技术赋能实体经济,人工智能驱动万商互联。<br/>
|
||||
打造AI时代小微创业与消费循环生态圈。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Quick Links */}
|
||||
<div>
|
||||
<h3 className="font-semibold text-white text-lg mb-6">产品中心</h3>
|
||||
<ul className="space-y-3 text-sm">
|
||||
<li><Link href="/products" className="hover:text-[#FF6600] transition-colors">白马智付</Link></li>
|
||||
<li><Link href="/products" className="hover:text-[#FF6600] transition-colors">AI拓客宝</Link></li>
|
||||
<li><Link href="/products" className="hover:text-[#FF6600] transition-colors">商家联盟</Link></li>
|
||||
<li><Link href="/products" className="hover:text-[#FF6600] transition-colors">智能硬件</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Solutions */}
|
||||
<div>
|
||||
<h3 className="font-semibold text-white text-lg mb-6">解决方案</h3>
|
||||
<ul className="space-y-3 text-sm">
|
||||
<li><Link href="#" className="hover:text-[#FF6600] transition-colors">餐饮行业</Link></li>
|
||||
<li><Link href="#" className="hover:text-[#FF6600] transition-colors">零售商超</Link></li>
|
||||
<li><Link href="#" className="hover:text-[#FF6600] transition-colors">美业丽人</Link></li>
|
||||
<li><Link href="#" className="hover:text-[#FF6600] transition-colors">休闲娱乐</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Contact Info - 客服企业微信二维码 */}
|
||||
<div>
|
||||
<h3 className="font-semibold text-white text-lg mb-6">联系我们</h3>
|
||||
<div className="space-y-4">
|
||||
{/* 企业微信二维码 */}
|
||||
<div className="bg-white p-3 rounded-lg inline-block">
|
||||
<div className="relative w-28 h-28">
|
||||
<Image
|
||||
src="/baima-qrcode.png"
|
||||
alt="扫码添加客服企业微信"
|
||||
fill
|
||||
className="object-contain rounded"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">
|
||||
扫码添加客服企业微信<br/>
|
||||
获取一对一专业服务
|
||||
</p>
|
||||
|
||||
{/* 地址保留 */}
|
||||
<div className="flex items-start gap-3 pt-4 border-t border-gray-800">
|
||||
<MapPin className="w-4 h-4 text-[#FF6600] mt-1 shrink-0" />
|
||||
<p className="text-sm">天津市武清区京津产业新城<br/>海达商务大厦</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator className="bg-white/10 mb-8" />
|
||||
|
||||
{/* 底部公司名称 - 更新为两家公司 */}
|
||||
<div className="text-center pb-4">
|
||||
<p className="text-sm text-gray-500 font-medium">
|
||||
天津白马晶选科技有限公司<br className="md:hidden" />
|
||||
<span className="mx-2 hidden md:inline">|</span>
|
||||
天津白马家园健康科技有限公司
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="text-center text-xs text-gray-600">
|
||||
<p>© {currentYear} 白马家园 保留所有权利</p>
|
||||
<a
|
||||
href="https://beian.miit.gov.cn/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-[#FF6600] transition-colors"
|
||||
style={{ marginTop: '10px', fontSize: '16px', display: 'block' }}
|
||||
>
|
||||
{icpNumber}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { Menu, Globe, ArrowRight } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetTrigger,
|
||||
} from '@/components/ui/sheet'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
// 导航结构 - 中文
|
||||
const navigationCN = [
|
||||
{ name: '首页', href: '/' },
|
||||
{ name: '产品矩阵', href: '/products' },
|
||||
{ name: '关于我们', href: '/about' },
|
||||
{ name: '合作加盟', href: '/contact' },
|
||||
]
|
||||
|
||||
// 导航结构 - 英文
|
||||
const navigationEN = [
|
||||
{ name: 'Home', href: '/en' },
|
||||
{ name: 'Products', href: '/products' },
|
||||
{ name: 'About', href: '/about' },
|
||||
{ name: 'Contact', href: '/contact' },
|
||||
]
|
||||
|
||||
export function Navbar() {
|
||||
const [isScrolled, setIsScrolled] = React.useState(false)
|
||||
const [language, setLanguage] = React.useState<'zh' | 'en'>('zh')
|
||||
const [mounted, setMounted] = React.useState(false)
|
||||
const [currentPath, setCurrentPath] = React.useState('')
|
||||
|
||||
// 初始化语言状态和当前路径
|
||||
React.useEffect(() => {
|
||||
setMounted(true)
|
||||
if (typeof window !== 'undefined') {
|
||||
const path = window.location.pathname
|
||||
setLanguage(path.startsWith('/en') ? 'en' : 'zh')
|
||||
setCurrentPath(path)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// 监听路由变化,更新当前路径
|
||||
React.useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const handlePopState = () => {
|
||||
setCurrentPath(window.location.pathname)
|
||||
}
|
||||
|
||||
// 监听浏览器前进后退
|
||||
window.addEventListener('popstate', handlePopState)
|
||||
|
||||
// 保存原始的pushState和replaceState
|
||||
const originalPushState = window.history.pushState
|
||||
const originalReplaceState = window.history.replaceState
|
||||
|
||||
// 重写pushState和replaceState
|
||||
window.history.pushState = function(...args) {
|
||||
originalPushState.apply(this, args)
|
||||
handlePopState()
|
||||
}
|
||||
|
||||
window.history.replaceState = function(...args) {
|
||||
originalReplaceState.apply(this, args)
|
||||
handlePopState()
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('popstate', handlePopState)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 20)
|
||||
}
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
return () => window.removeEventListener('scroll', handleScroll)
|
||||
}, [])
|
||||
|
||||
// 根据语言选择导航
|
||||
const navigation = language === 'zh' ? navigationCN : navigationEN
|
||||
const currentLanguage = language
|
||||
|
||||
// 切换语言
|
||||
const toggleLanguage = () => {
|
||||
const newLang = currentLanguage === 'zh' ? 'en' : 'zh'
|
||||
setLanguage(newLang)
|
||||
|
||||
// 跳转到对应语言版本
|
||||
if (typeof window !== 'undefined') {
|
||||
const path = window.location.pathname
|
||||
let newPath = path
|
||||
|
||||
if (newLang === 'en') {
|
||||
// 转到英文版(首页)
|
||||
if (path === '/') {
|
||||
newPath = '/en'
|
||||
} else {
|
||||
// 其他页面保持原样(暂时只有首页有英文版)
|
||||
newPath = '/en'
|
||||
}
|
||||
} else {
|
||||
// 转到中文版
|
||||
if (path.startsWith('/en')) {
|
||||
newPath = '/'
|
||||
} else {
|
||||
newPath = path
|
||||
}
|
||||
}
|
||||
|
||||
window.location.href = newPath
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<header
|
||||
className={cn(
|
||||
'fixed top-0 left-0 right-0 z-50 transition-all duration-300',
|
||||
isScrolled
|
||||
? 'bg-white/98 backdrop-blur-md shadow-sm border-b border-gray-100 py-3'
|
||||
: 'bg-transparent py-5'
|
||||
)}
|
||||
>
|
||||
<div className="container mx-auto px-4 flex items-center justify-between">
|
||||
{/* Logo */}
|
||||
<Link href={currentLanguage === 'zh' ? '/' : '/en'} className="flex items-center gap-3">
|
||||
<div className="relative h-14 w-auto aspect-[2/1]">
|
||||
<Image
|
||||
src="/baima-logo.png"
|
||||
alt="Baima Home Logo"
|
||||
fill
|
||||
className="object-contain"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden lg:flex items-center gap-8">
|
||||
{navigation.map((item) => {
|
||||
// 检测当前活跃链接
|
||||
const isActive = mounted && currentPath === item.href;
|
||||
// 检测是否为首页
|
||||
const isHomePage = item.href === '/' || item.href === '/en';
|
||||
// 检测当前是否在指定的页面(/about、/products、/contact)
|
||||
const isOnSpecificPage = currentPath.startsWith('/about') ||
|
||||
currentPath.startsWith('/products') ||
|
||||
currentPath.startsWith('/contact');
|
||||
return (
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
"text-sm font-medium transition-colors hover:text-[#FF6600]",
|
||||
isActive
|
||||
? "text-[#FF6600]"
|
||||
: isOnSpecificPage || isScrolled
|
||||
? "text-black"
|
||||
: isHomePage ? "text-black" : "text-white"
|
||||
)}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
{/* Right Side Actions */}
|
||||
<div className="hidden lg:flex items-center gap-4">
|
||||
{/* <button
|
||||
onClick={toggleLanguage}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 text-sm font-medium transition-colors hover:text-[#FF6600]",
|
||||
isScrolled ? "text-[#555]" : "text-white/90 drop-shadow-md"
|
||||
)}
|
||||
>
|
||||
<Globe className="w-4 h-4" />
|
||||
<span>{currentLanguage === 'zh' ? 'English' : '中文'}</span>
|
||||
</button> */}
|
||||
|
||||
<Button
|
||||
size="sm"
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-5 py-2.5 rounded-full text-sm font-bold transition-all shadow-md",
|
||||
isScrolled
|
||||
? "bg-[#FF6600] hover:bg-[#FF8C00] text-white"
|
||||
: "bg-white text-[#FF6600] hover:bg-white/90"
|
||||
)}
|
||||
asChild
|
||||
>
|
||||
<Link href="/contact#form">
|
||||
{currentLanguage === 'zh' ? '立即咨询' : 'Get Started'}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Mobile Navigation */}
|
||||
<div className="lg:hidden">
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={cn(!isScrolled && "text-white drop-shadow-md")}
|
||||
>
|
||||
<Menu className="h-6 w-6" />
|
||||
<span className="sr-only">Open menu</span>
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="right" className="w-[300px] sm:w-[400px]">
|
||||
<div className="flex flex-col gap-6 mt-8">
|
||||
<div className="flex items-center gap-3 pb-4 border-b">
|
||||
<div className="relative h-8 w-auto aspect-[2/1]">
|
||||
<Image
|
||||
src="./logos.png"
|
||||
alt="Baima Home Logo"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 语言切换 */}
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="flex items-center gap-2 text-sm font-medium text-[#FF6600] py-2 px-4 bg-orange-50 rounded-lg"
|
||||
>
|
||||
<Globe className="w-4 h-4" />
|
||||
{currentLanguage === 'zh' ? 'Switch to English' : '切换到中文'}
|
||||
</button>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
{navigation.map((item) => {
|
||||
// 检测当前活跃链接
|
||||
const isActive = mounted && currentPath === item.href;
|
||||
return (
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
"text-lg font-medium py-3 px-4 rounded-lg hover:bg-gray-100 transition-colors",
|
||||
isActive ? "text-[#FF6600] bg-orange-50" : "text-gray-800"
|
||||
)}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="pt-4 mt-auto">
|
||||
<Button className="w-full bg-[#FF6600] hover:bg-[#FF8C00] rounded-full" size="lg" asChild>
|
||||
<Link href="/contact#form">
|
||||
{currentLanguage === 'zh' ? '立即咨询' : 'Get Started'} <ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import {
|
||||
CreditCard,
|
||||
Smartphone,
|
||||
Bot,
|
||||
Users,
|
||||
Network,
|
||||
ShoppingBag,
|
||||
LayoutGrid,
|
||||
Coins,
|
||||
Shield,
|
||||
Camera,
|
||||
Share2,
|
||||
Gift
|
||||
} from 'lucide-react'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
|
||||
// 完全基于框图重构的数据结构
|
||||
const categories = [
|
||||
{
|
||||
id: "fintech",
|
||||
title: "高效安全的金融科技支撑",
|
||||
subtitle: "收单-分销-结算 一体化解决方案",
|
||||
items: [
|
||||
{
|
||||
title: "白马智付",
|
||||
icon: CreditCard,
|
||||
features: [
|
||||
"强大的技术能力,保障交易稳定",
|
||||
"多通道AI路由,智能选择最优通道",
|
||||
"AI智慧收款码:支持输入文本",
|
||||
"核心功能:锁粉、自动分佣"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "智能设备",
|
||||
icon: Smartphone,
|
||||
features: [
|
||||
"智慧收银系统硬件支持",
|
||||
"全场景支付:支持主扫、被扫",
|
||||
"绿色积分:支持积分抵扣支付",
|
||||
"设备联网与远程管理"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "ai",
|
||||
title: "简单易用的私域运营工具",
|
||||
subtitle: "AI赋能,让私域运营自动化、智能化",
|
||||
items: [
|
||||
{
|
||||
title: "AI营销数字人",
|
||||
icon: Bot,
|
||||
features: [
|
||||
"数字员工:全天候导购、客服",
|
||||
"AI直播:自动讲解商品,提升转化",
|
||||
"AI营销获客:智能生成内容引流"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "积分与消费券体系",
|
||||
icon: Gift,
|
||||
features: [
|
||||
"消费券:可从供应商进货,可流转他人",
|
||||
"积分权益:兑换AI数字人/直播权益",
|
||||
"企微私域运营:无缝打通微信生态"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "alliance",
|
||||
title: "轻松锁客的异业联盟系统",
|
||||
subtitle: "打破孤岛,实现跨界收益最大化",
|
||||
items: [
|
||||
{
|
||||
title: "锁定客户终身价值",
|
||||
icon: Users,
|
||||
features: [
|
||||
"一次锁定,终身受益",
|
||||
"赚360行的钱:跨行业收益共享",
|
||||
"客户在联盟内任何消费均有关联"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "全维度分佣机制",
|
||||
icon: Network,
|
||||
features: [
|
||||
"赚其他门店的分佣收益",
|
||||
"赚其他供应链的分佣收益",
|
||||
"构建真正的利益共同体"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "infrastructure",
|
||||
title: "统一高效的生态基础设施",
|
||||
subtitle: "以上生态均对外开放,共建数智化底座",
|
||||
items: [
|
||||
{
|
||||
title: "核心系统基建",
|
||||
icon: LayoutGrid,
|
||||
features: [
|
||||
"统一会员系统:跨平台生态融合",
|
||||
"专业小微数智化系统:积分支付年费",
|
||||
"涵盖:收银/会员/营销/外卖/进销存/连锁等"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "数字资产体系 (RWA)",
|
||||
icon: Coins,
|
||||
features: [
|
||||
"RWA数字资产化",
|
||||
"资产转换:积分、抵用券、金币互转",
|
||||
"数字藏品与RWA权益绑定"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "供应链与监控",
|
||||
icon: Shield,
|
||||
features: [
|
||||
"供应链资源:稳定、安全、靠谱",
|
||||
"智能监控巡店设备:远程管理门店",
|
||||
"供应链陆续招募中"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export function ProductList() {
|
||||
return (
|
||||
<div className="space-y-24 py-12">
|
||||
{categories.map((category, catIndex) => (
|
||||
<section key={category.id} id={category.id} className="scroll-mt-24">
|
||||
{/* Section Header */}
|
||||
<div className="flex flex-col md:flex-row md:items-center gap-4 md:gap-6 mb-12">
|
||||
<div className="w-12 h-12 md:w-16 md:h-16 flex items-center justify-center bg-[#FF6600] text-white text-xl md:text-2xl font-bold rounded-2xl shadow-lg shrink-0">
|
||||
0{catIndex + 1}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-[#0A1931] mb-2">{category.title}</h2>
|
||||
<p className="text-sm md:text-base text-gray-500 font-medium">{category.subtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Products Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-8">
|
||||
{category.items.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
>
|
||||
<Card className="h-full border-0 shadow-md hover:shadow-xl transition-all duration-300 bg-white group hover:-translate-y-1">
|
||||
<CardHeader className="flex flex-row items-center gap-4 pb-4 border-b border-gray-50">
|
||||
<div className="w-12 h-12 bg-[#0A1931]/5 rounded-xl flex items-center justify-center text-[#FF6600] group-hover:bg-[#FF6600] group-hover:text-white transition-colors duration-300">
|
||||
<item.icon className="w-6 h-6" />
|
||||
</div>
|
||||
<CardTitle className="text-lg font-bold text-[#0A1931]">{item.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-6">
|
||||
<ul className="space-y-3">
|
||||
{item.features.map((feature, fIndex) => (
|
||||
<li key={fIndex} className="flex items-start text-sm text-gray-600">
|
||||
<span className="w-1.5 h-1.5 bg-[#185ADB] rounded-full mr-3 mt-1.5 shrink-0"></span>
|
||||
<span className="leading-relaxed">{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
||||
import { ChevronDownIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Accordion({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
|
||||
return <AccordionPrimitive.Root data-slot="accordion" {...props} />
|
||||
}
|
||||
|
||||
function AccordionItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
|
||||
return (
|
||||
<AccordionPrimitive.Item
|
||||
data-slot="accordion-item"
|
||||
className={cn("border-b last:border-b-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AccordionTrigger({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
|
||||
return (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
data-slot="accordion-trigger"
|
||||
className={cn(
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
)
|
||||
}
|
||||
|
||||
function AccordionContent({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
|
||||
return (
|
||||
<AccordionPrimitive.Content
|
||||
data-slot="accordion-content"
|
||||
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
|
||||
{...props}
|
||||
>
|
||||
<div className={cn("pt-0 pb-4", className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
)
|
||||
}
|
||||
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Badge({
|
||||
className,
|
||||
variant,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"span"> &
|
||||
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
||||
const Comp = asChild ? Slot : "span"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="badge"
|
||||
className={cn(badgeVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
icon: "size-9",
|
||||
"icon-sm": "size-8",
|
||||
"icon-lg": "size-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"button"> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
data-variant={variant}
|
||||
data-size={size}
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Button, buttonVariants }
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card"
|
||||
className={cn(
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-header"
|
||||
className={cn(
|
||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-title"
|
||||
className={cn("leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-action"
|
||||
className={cn(
|
||||
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-content"
|
||||
className={cn("px-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardFooter,
|
||||
CardTitle,
|
||||
CardAction,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
}
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import useEmblaCarousel, {
|
||||
type UseEmblaCarouselType,
|
||||
} from "embla-carousel-react"
|
||||
import { ArrowLeft, ArrowRight } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
type CarouselApi = UseEmblaCarouselType[1]
|
||||
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
|
||||
type CarouselOptions = UseCarouselParameters[0]
|
||||
type CarouselPlugin = UseCarouselParameters[1]
|
||||
|
||||
type CarouselProps = {
|
||||
opts?: CarouselOptions
|
||||
plugins?: CarouselPlugin
|
||||
orientation?: "horizontal" | "vertical"
|
||||
setApi?: (api: CarouselApi) => void
|
||||
}
|
||||
|
||||
type CarouselContextProps = {
|
||||
carouselRef: ReturnType<typeof useEmblaCarousel>[0]
|
||||
api: ReturnType<typeof useEmblaCarousel>[1]
|
||||
scrollPrev: () => void
|
||||
scrollNext: () => void
|
||||
canScrollPrev: boolean
|
||||
canScrollNext: boolean
|
||||
} & CarouselProps
|
||||
|
||||
const CarouselContext = React.createContext<CarouselContextProps | null>(null)
|
||||
|
||||
function useCarousel() {
|
||||
const context = React.useContext(CarouselContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error("useCarousel must be used within a <Carousel />")
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
function Carousel({
|
||||
orientation = "horizontal",
|
||||
opts,
|
||||
setApi,
|
||||
plugins,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & CarouselProps) {
|
||||
const [carouselRef, api] = useEmblaCarousel(
|
||||
{
|
||||
...opts,
|
||||
axis: orientation === "horizontal" ? "x" : "y",
|
||||
},
|
||||
plugins
|
||||
)
|
||||
const [canScrollPrev, setCanScrollPrev] = React.useState(false)
|
||||
const [canScrollNext, setCanScrollNext] = React.useState(false)
|
||||
|
||||
const onSelect = React.useCallback((api: CarouselApi) => {
|
||||
if (!api) return
|
||||
setCanScrollPrev(api.canScrollPrev())
|
||||
setCanScrollNext(api.canScrollNext())
|
||||
}, [])
|
||||
|
||||
const scrollPrev = React.useCallback(() => {
|
||||
api?.scrollPrev()
|
||||
}, [api])
|
||||
|
||||
const scrollNext = React.useCallback(() => {
|
||||
api?.scrollNext()
|
||||
}, [api])
|
||||
|
||||
const handleKeyDown = React.useCallback(
|
||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key === "ArrowLeft") {
|
||||
event.preventDefault()
|
||||
scrollPrev()
|
||||
} else if (event.key === "ArrowRight") {
|
||||
event.preventDefault()
|
||||
scrollNext()
|
||||
}
|
||||
},
|
||||
[scrollPrev, scrollNext]
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!api || !setApi) return
|
||||
setApi(api)
|
||||
}, [api, setApi])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!api) return
|
||||
onSelect(api)
|
||||
api.on("reInit", onSelect)
|
||||
api.on("select", onSelect)
|
||||
|
||||
return () => {
|
||||
api?.off("select", onSelect)
|
||||
}
|
||||
}, [api, onSelect])
|
||||
|
||||
return (
|
||||
<CarouselContext.Provider
|
||||
value={{
|
||||
carouselRef,
|
||||
api: api,
|
||||
opts,
|
||||
orientation:
|
||||
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
|
||||
scrollPrev,
|
||||
scrollNext,
|
||||
canScrollPrev,
|
||||
canScrollNext,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onKeyDownCapture={handleKeyDown}
|
||||
className={cn("relative", className)}
|
||||
role="region"
|
||||
aria-roledescription="carousel"
|
||||
data-slot="carousel"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</CarouselContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
const { carouselRef, orientation } = useCarousel()
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={carouselRef}
|
||||
className="overflow-hidden"
|
||||
data-slot="carousel-content"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex",
|
||||
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
|
||||
const { orientation } = useCarousel()
|
||||
|
||||
return (
|
||||
<div
|
||||
role="group"
|
||||
aria-roledescription="slide"
|
||||
data-slot="carousel-item"
|
||||
className={cn(
|
||||
"min-w-0 shrink-0 grow-0 basis-full",
|
||||
orientation === "horizontal" ? "pl-4" : "pt-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CarouselPrevious({
|
||||
className,
|
||||
variant = "outline",
|
||||
size = "icon",
|
||||
...props
|
||||
}: React.ComponentProps<typeof Button>) {
|
||||
const { orientation, scrollPrev, canScrollPrev } = useCarousel()
|
||||
|
||||
return (
|
||||
<Button
|
||||
data-slot="carousel-previous"
|
||||
variant={variant}
|
||||
size={size}
|
||||
className={cn(
|
||||
"absolute size-8 rounded-full",
|
||||
orientation === "horizontal"
|
||||
? "top-1/2 -left-12 -translate-y-1/2"
|
||||
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
|
||||
className
|
||||
)}
|
||||
disabled={!canScrollPrev}
|
||||
onClick={scrollPrev}
|
||||
{...props}
|
||||
>
|
||||
<ArrowLeft />
|
||||
<span className="sr-only">Previous slide</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
function CarouselNext({
|
||||
className,
|
||||
variant = "outline",
|
||||
size = "icon",
|
||||
...props
|
||||
}: React.ComponentProps<typeof Button>) {
|
||||
const { orientation, scrollNext, canScrollNext } = useCarousel()
|
||||
|
||||
return (
|
||||
<Button
|
||||
data-slot="carousel-next"
|
||||
variant={variant}
|
||||
size={size}
|
||||
className={cn(
|
||||
"absolute size-8 rounded-full",
|
||||
orientation === "horizontal"
|
||||
? "top-1/2 -right-12 -translate-y-1/2"
|
||||
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
|
||||
className
|
||||
)}
|
||||
disabled={!canScrollNext}
|
||||
onClick={scrollNext}
|
||||
{...props}
|
||||
>
|
||||
<ArrowRight />
|
||||
<span className="sr-only">Next slide</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
type CarouselApi,
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselPrevious,
|
||||
CarouselNext,
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Input }
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
import * as React from "react"
|
||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
||||
import { cva } from "class-variance-authority"
|
||||
import { ChevronDownIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function NavigationMenu({
|
||||
className,
|
||||
children,
|
||||
viewport = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
|
||||
viewport?: boolean
|
||||
}) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Root
|
||||
data-slot="navigation-menu"
|
||||
data-viewport={viewport}
|
||||
className={cn(
|
||||
"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{viewport && <NavigationMenuViewport />}
|
||||
</NavigationMenuPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuList({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.List
|
||||
data-slot="navigation-menu-list"
|
||||
className={cn(
|
||||
"group flex flex-1 list-none items-center justify-center gap-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Item
|
||||
data-slot="navigation-menu-item"
|
||||
className={cn("relative", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const navigationMenuTriggerStyle = cva(
|
||||
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
|
||||
)
|
||||
|
||||
function NavigationMenuTrigger({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Trigger
|
||||
data-slot="navigation-menu-trigger"
|
||||
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}{" "}
|
||||
<ChevronDownIcon
|
||||
className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</NavigationMenuPrimitive.Trigger>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Content
|
||||
data-slot="navigation-menu-content"
|
||||
className={cn(
|
||||
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
|
||||
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuViewport({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute top-full left-0 isolate z-50 flex justify-center"
|
||||
)}
|
||||
>
|
||||
<NavigationMenuPrimitive.Viewport
|
||||
data-slot="navigation-menu-viewport"
|
||||
className={cn(
|
||||
"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuLink({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Link
|
||||
data-slot="navigation-menu-link"
|
||||
className={cn(
|
||||
"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function NavigationMenuIndicator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
|
||||
return (
|
||||
<NavigationMenuPrimitive.Indicator
|
||||
data-slot="navigation-menu-indicator"
|
||||
className={cn(
|
||||
"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
|
||||
</NavigationMenuPrimitive.Indicator>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
NavigationMenu,
|
||||
NavigationMenuList,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuIndicator,
|
||||
NavigationMenuViewport,
|
||||
navigationMenuTriggerStyle,
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Separator({
|
||||
className,
|
||||
orientation = "horizontal",
|
||||
decorative = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
|
||||
return (
|
||||
<SeparatorPrimitive.Root
|
||||
data-slot="separator"
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Separator }
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
||||
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
||||
}
|
||||
|
||||
function SheetTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
||||
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
||||
}
|
||||
|
||||
function SheetClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
||||
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
||||
}
|
||||
|
||||
function SheetPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
||||
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
||||
}
|
||||
|
||||
function SheetOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
||||
return (
|
||||
<SheetPrimitive.Overlay
|
||||
data-slot="sheet-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetContent({
|
||||
className,
|
||||
children,
|
||||
side = "right",
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
||||
side?: "top" | "right" | "bottom" | "left"
|
||||
}) {
|
||||
return (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content
|
||||
data-slot="sheet-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||
side === "right" &&
|
||||
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
||||
side === "left" &&
|
||||
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
||||
side === "top" &&
|
||||
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
||||
side === "bottom" &&
|
||||
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
||||
<XIcon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="sheet-header"
|
||||
className={cn("flex flex-col gap-1.5 p-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="sheet-footer"
|
||||
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
||||
return (
|
||||
<SheetPrimitive.Title
|
||||
data-slot="sheet-title"
|
||||
className={cn("text-foreground font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
||||
return (
|
||||
<SheetPrimitive.Description
|
||||
data-slot="sheet-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Sheet,
|
||||
SheetTrigger,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetFooter,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
return (
|
||||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Textarea }
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { defineConfig, globalIgnores } from "eslint/config";
|
||||
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||
import nextTs from "eslint-config-next/typescript";
|
||||
|
||||
const eslintConfig = defineConfig([
|
||||
...nextVitals,
|
||||
...nextTs,
|
||||
// Override default ignores of eslint-config-next.
|
||||
globalIgnores([
|
||||
// Default ignores of eslint-config-next:
|
||||
".next/**",
|
||||
"out/**",
|
||||
"build/**",
|
||||
"next-env.d.ts",
|
||||
]),
|
||||
]);
|
||||
|
||||
export default eslintConfig;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: 'export',
|
||||
basePath: '',
|
||||
trailingSlash: true, // 添加这一行
|
||||
images: {
|
||||
unoptimized: true,
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: '**',
|
||||
},
|
||||
{
|
||||
protocol: 'http',
|
||||
hostname: 'localhost',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* @LastEditors: whitechiina 1293616053@qq.com
|
||||
* @LastEditTime: 2026-07-15 18:04:31
|
||||
*/
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: "export",
|
||||
trailingSlash: true // 添加这一行
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "baima-home",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.14",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"framer-motion": "^12.26.2",
|
||||
"lucide-react": "^0.562.0",
|
||||
"next": "16.1.1",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"tailwind-merge": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"dotenv": "^17.2.3",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.1.1",
|
||||
"tailwindcss": "^4",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "^5",
|
||||
"vercel": "^50.4.4"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
const config = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
|
@ -0,0 +1,8 @@
|
|||
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
|
||||
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="white" stroke-width="0.5" stroke-opacity="0.3"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#grid)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 345 B |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 128 B |
|
After Width: | Height: | Size: 407 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
||||
|
After Width: | Height: | Size: 385 B |
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts",
|
||||
"**/*.mts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"buildCommand": "npm run build",
|
||||
"outputDirectory": ".next",
|
||||
"framework": "nextjs"
|
||||
}
|
||||