Browse Source

重写README.md文件

master
科技小王子 3 months ago
parent
commit
a2d32660ee
  1. 280
      README.md
  2. 2
      src/api/layout/index.ts
  3. 77
      src/app/layout.tsx
  4. 22
      src/components/layout/Header.tsx
  5. 142
      src/components/sections/CasesSection.tsx
  6. 86
      src/components/sections/PartnersSection.tsx

280
README.md

@ -1,36 +1,282 @@
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).
# WEBSOFT 企业官网
## Getting Started
> 基于 Next.js 15 + TypeScript 构建的现代化企业官网系统,集成 CMS 内容管理功能
First, run the development server:
[![Next.js](https://img.shields.io/badge/Next.js-15.3.5-black?style=flat-square&logo=next.js)](https://nextjs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue?style=flat-square&logo=typescript)](https://www.typescriptlang.org/)
[![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-4.0-38B2AC?style=flat-square&logo=tailwind-css)](https://tailwindcss.com/)
[![React](https://img.shields.io/badge/React-19.0-61DAFB?style=flat-square&logo=react)](https://reactjs.org/)
## 🌟 项目特色
- **🚀 现代化技术栈** - Next.js 15 + React 19 + TypeScript + Tailwind CSS v4
- **📱 响应式设计** - 完美适配桌面、平板、手机等各种设备
- **🎨 精美UI界面** - 现代化设计风格,用户体验优秀
- **⚡ 高性能优化** - SSR/SSG、图片优化、代码分割等性能优化
- **🔧 CMS集成** - 与后端CMS系统深度集成,内容动态管理
- **🌐 多语言支持** - 支持中英文切换,国际化友好
- **📊 SEO优化** - 动态metadata、结构化数据、搜索引擎友好
- **🔐 类型安全** - 完整的TypeScript类型定义,开发更安全
## 🏗️ 技术架构
### 前端技术栈
- **框架**: Next.js 15 (App Router)
- **语言**: TypeScript 5.0
- **UI库**: React 19
- **样式**: Tailwind CSS v4
- **构建工具**: Turbopack (开发环境)
- **代码规范**: ESLint + Prettier
### 后端集成
- **API服务**: https://cms-api.websoft.top/api
- **内容管理**: CMS系统集成
- **文件存储**: OSS对象存储
- **多租户**: 支持多租户架构
## 📁 项目结构
```
next-10398/
├── src/
│ ├── app/ # Next.js App Router 页面
│ │ ├── layout.tsx # 根布局组件
│ │ ├── page.tsx # 首页
│ │ ├── globals.css # 全局样式
│ │ └── ...
│ ├── components/ # React 组件
│ │ ├── layout/ # 布局组件
│ │ │ ├── Header.tsx # 头部导航
│ │ │ └── Footer.tsx # 底部信息
│ │ └── sections/ # 页面区块组件
│ │ ├── HeroSection.tsx # 英雄区域
│ │ ├── FeaturesSection.tsx # 产品服务
│ │ ├── CasesSection.tsx # 客户案例
│ │ ├── PartnersSection.tsx # 合作伙伴
│ │ └── ContactSection.tsx # 联系我们
│ ├── api/ # API 接口封装
│ │ ├── cms/ # CMS相关API
│ │ ├── system/ # 系统管理API
│ │ ├── layout/ # 布局相关API
│ │ └── modules/ # 业务模块API
│ ├── config/ # 配置文件
│ │ └── setting.ts # 系统配置
│ └── utils/ # 工具函数
│ ├── request.ts # HTTP请求封装
│ ├── common.ts # 通用工具
│ └── token-util.ts # Token管理
├── public/ # 静态资源
├── docs/ # 项目文档
├── package.json # 项目依赖
├── next.config.ts # Next.js配置
├── tailwind.config.js # Tailwind配置
├── tsconfig.json # TypeScript配置
└── README.md # 项目说明
```
## 🚀 快速开始
### 环境要求
- Node.js 18.0 或更高版本
- npm 9.0 或更高版本
### 安装依赖
```bash
# 克隆项目
git clone <repository-url>
cd next-10398
# 安装依赖
npm install
```
### 环境配置
创建 `.env.local` 文件:
```env
# API 配置
NEXT_PUBLIC_API_URL=https://cms-api.websoft.top/api
NEXT_PUBLIC_UPLOAD_URL=https://cms-api.websoft.top/api/upload
# 租户配置
NEXT_PUBLIC_TENANT_ID=10398
# 其他配置
NODE_ENV=development
```
### 启动开发服务器
```bash
# 启动开发服务器 (使用 Turbopack)
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.
打开 [http://localhost:3000](http://localhost:3000) 查看网站效果。
### 构建生产版本
```bash
# 构建生产版本
npm run build
# 启动生产服务器
npm run start
```
## 🎨 页面功能
### 首页模块
1. **Hero区域** - 主视觉展示,包含主标题、技术标签和服务图标
2. **产品服务** - 四大核心服务展示(全栈开发、企业应用、移动开发、云服务)
3. **客户案例** - 8个精选案例展示,支持分类和链接跳转
4. **合作伙伴** - 知名企业合作伙伴展示
5. **联系我们** - 联系方式和公司信息展示
### 响应式设计
- **桌面端**: 1200px+ 多列布局,完整功能展示
- **平板端**: 768px-1199px 适配布局,保持良好体验
- **移动端**: <768px 单列布局触摸友好
## 🔧 核心功能
### 动态内容管理
- **网站信息**: 从CMS系统动态获取网站基本信息
- **导航菜单**: 支持多级导航,动态配置
- **页面内容**: 所有内容支持后台管理和实时更新
- **多语言**: 支持中英文内容切换
### SEO优化
- **动态Metadata**: 根据CMS数据生成页面标题、描述、关键词
- **结构化数据**: 支持搜索引擎结构化数据
- **图片优化**: 自动图片压缩和懒加载
- **性能优化**: SSR/SSG、代码分割、缓存策略
### API集成
- **统一封装**: 完整的API请求封装,支持类型安全
- **错误处理**: 统一的错误处理机制
- **认证管理**: 自动token管理和刷新
- **多租户**: 支持多租户架构
## 🛠️ 开发指南
### 添加新页面
```typescript
// src/app/new-page/page.tsx
export default function NewPage() {
return (
<div>
<h1>新页面</h1>
</div>
);
}
```
### 创建新组件
```typescript
// src/components/NewComponent.tsx
interface NewComponentProps {
title: string;
content: string;
}
export default function NewComponent({ title, content }: NewComponentProps) {
return (
<div className="p-4">
<h2 className="text-xl font-bold">{title}</h2>
<p>{content}</p>
</div>
);
}
```
### API调用示例
```typescript
// 获取网站信息
import { getSiteInfo } from '@/api/layout';
const siteInfo = await getSiteInfo();
console.log(siteInfo.websiteName);
// 使用现代化API
import { request } from '@/api';
const result = await request.get('/cms/article/list', {
page: 1,
limit: 10
});
```
## 📱 部署指南
### Vercel 部署 (推荐)
1. 将代码推送到 GitHub
2. 在 [Vercel](https://vercel.com) 导入项目
3. 配置环境变量
4. 自动部署完成
### 其他平台部署
```bash
# 构建静态文件
npm run build
# 部署 .next 目录到服务器
```
## 🔍 性能优化
- **图片优化**: Next.js Image组件自动优化
- **代码分割**: 自动按路由分割代码
- **缓存策略**: 合理的缓存配置
- **CDN加速**: 静态资源CDN分发
- **懒加载**: 图片和组件懒加载
## 🤝 贡献指南
1. Fork 项目
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 创建 Pull Request
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.
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情
## Learn More
## 📞 联系我们
To learn more about Next.js, take a look at the following resources:
- **官网**: https://websoft.top
- **邮箱**: contact@websoft.top
- **电话**: 0771-5386339
- **地址**: 广西南宁市
- [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
- [Next.js](https://nextjs.org/) - React 全栈框架
- [Tailwind CSS](https://tailwindcss.com/) - 实用优先的CSS框架
- [TypeScript](https://www.typescriptlang.org/) - JavaScript的超集
- [React](https://reactjs.org/) - 用户界面库
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.
**WEBSOFT** - 构建现代Web应用 🚀

2
src/api/layout/index.ts

@ -24,7 +24,7 @@ export async function getTenantInfo(): Promise<Company> {
/**
* https://cms-api.websoft.top/api
*/
export async function getSiteInfo() {
export async function getSiteInfo(): Promise<CmsWebsite> {
const res = await request.get<ApiResult<CmsWebsite>>(
MODULES_API_URL + '/cms/cms-website/getSiteInfo',
{

77
src/app/layout.tsx

@ -2,20 +2,80 @@ import type { Metadata } from "next";
import "./globals.css";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import { getSiteInfo } from "@/api/layout";
export const metadata: Metadata = {
title: "企业官网 - 专业的企业解决方案服务商",
description: "我们致力于为企业提供全方位的数字化转型服务,以创新技术驱动业务增长,助力企业在数字时代取得成功。",
keywords: "企业官网,数字化转型,企业服务,解决方案,技术服务",
authors: [{ name: "企业官网" }],
// 生成动态metadata
export async function generateMetadata(): Promise<Metadata> {
// 默认的metadata
const defaultMetadata: Metadata = {
title: "WEBSOFT - 构建现代Web应用",
description: "基于现代前端框架,为企业提供高性能、可扩展的Web应用解决方案。我们专注于用户体验和技术创新,助力企业数字化转型。",
keywords: "Web应用,Vue,React,前端开发,企业数字化,技术服务",
authors: [{ name: "WEBSOFT" }],
robots: "index, follow",
openGraph: {
title: "企业官网 - 专业的企业解决方案服务商",
description: "我们致力于为企业提供全方位的数字化转型服务,以创新技术驱动业务增长,助力企业在数字时代取得成功。",
title: "WEBSOFT - 构建现代Web应用",
description: "基于现代前端框架,为企业提供高性能、可扩展的Web应用解决方案。我们专注于用户体验和技术创新,助力企业数字化转型。",
type: "website",
locale: "zh_CN",
},
};
};
try {
console.log('Attempting to fetch site info for metadata...');
const siteInfo = await getSiteInfo();
console.log('Site info fetched successfully:', siteInfo);
const title = siteInfo.websiteName || defaultMetadata.title as string;
const description = siteInfo.comments || defaultMetadata.description as string;
const keywords = siteInfo.keywords || defaultMetadata.keywords as string;
const siteLogo = siteInfo.websiteLogo;
const siteUrl = siteInfo.domain ? `https://${siteInfo.domain}` : undefined;
return {
title,
description,
keywords,
authors: [{ name: siteInfo.websiteName || "WEBSOFT" }],
robots: "index, follow",
icons: siteLogo ? {
icon: siteLogo,
apple: siteLogo,
} : undefined,
openGraph: {
title,
description,
type: "website",
locale: siteInfo.lang || "zh_CN",
siteName: siteInfo.websiteName,
url: siteUrl,
images: siteLogo ? [
{
url: siteLogo,
width: 1200,
height: 630,
alt: siteInfo.websiteName,
}
] : undefined,
},
twitter: {
card: "summary_large_image",
title,
description,
images: siteLogo ? [siteLogo] : undefined,
},
alternates: siteUrl ? {
canonical: siteUrl,
} : undefined,
};
} catch (error) {
console.error('Failed to fetch site info for metadata:', error);
console.log('Using default metadata instead');
// 如果API调用失败,返回默认的metadata
return defaultMetadata;
}
}
export const viewport = {
width: 'device-width',
@ -27,6 +87,7 @@ export default function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="zh-CN">
<body className="antialiased">

22
src/components/layout/Header.tsx

@ -1,10 +1,13 @@
'use client';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import {getSiteInfo} from "@/api/layout";
import {CmsWebsite} from "@/api/cms/cmsWebsite/model";
const Header = () => {
const [config, setConfig] = useState<CmsWebsite>({})
const [isMenuOpen, setIsMenuOpen] = useState(false);
const pathname = usePathname();
@ -24,6 +27,19 @@ const Header = () => {
return pathname.startsWith(href);
};
const reload = () => {
getSiteInfo().then(data => {
console.log(data,'data')
if(data){
setConfig(data);
}
})
}
useEffect(() => {
reload();
}, []);
return (
<header className="bg-white shadow-sm border-b border-gray-100 sticky top-0 z-50">
<div className="container mx-auto px-4">
@ -31,7 +47,7 @@ const Header = () => {
{/* Logo */}
<div className="flex-shrink-0">
<Link href="/" className="text-xl font-bold text-gray-800">
WEBSOFT
= {config?.websiteName} =
</Link>
</div>

142
src/components/sections/CasesSection.tsx

@ -0,0 +1,142 @@
'use client';
import Image from 'next/image';
const CasesSection = () => {
const cases = [
{
id: 1,
title: '某实训基地管理系统',
description: '基于Vue.js开发的实训基地管理系统,提供学员管理、课程安排、设备管理等功能,界面简洁美观,操作便捷高效。',
image: '/case1.jpg',
tags: ['Vue.js', '管理系统', '教育'],
link: '/cases/training-base'
},
{
id: 2,
title: '重工机械企业官网',
description: '为重工机械企业打造的现代化官网,展示企业实力和产品信息,采用响应式设计,支持多语言切换。',
image: '/case2.jpg',
tags: ['企业官网', '响应式', '多语言'],
link: '/cases/heavy-industry'
},
{
id: 3,
title: '福建文旅APP',
description: '福建省文化旅游移动应用,集成景点介绍、路线规划、在线预订等功能,为游客提供便捷的旅游服务。',
image: '/case3.jpg',
tags: ['移动应用', '文旅', '小程序'],
link: '/cases/fujian-travel'
},
{
id: 4,
title: '中外合作办学平台',
description: '中外合作办学信息管理平台,支持学生申请、课程管理、成绩查询等功能,促进国际教育交流合作。',
image: '/case4.jpg',
tags: ['教育平台', '国际合作', '管理系统'],
link: '/cases/international-education'
},
{
id: 5,
title: '数字化校园解决方案',
description: '为高等院校提供的数字化校园整体解决方案,包含教务管理、学生服务、校园生活等多个模块。',
image: '/case5.jpg',
tags: ['数字校园', '教务系统', '一体化'],
link: '/cases/digital-campus'
},
{
id: 6,
title: '智慧工厂管理系统',
description: '工业4.0智慧工厂管理系统,实现生产流程数字化、设备监控智能化、质量管理自动化。',
image: '/case6.jpg',
tags: ['工业4.0', '智慧工厂', 'IoT'],
link: '/cases/smart-factory'
},
{
id: 7,
title: '小程序开发平台',
description: '一站式小程序开发平台,提供可视化编辑器、组件库、发布管理等功能,降低小程序开发门槛。',
image: '/case7.jpg',
tags: ['小程序', '开发平台', '可视化'],
link: '/cases/miniprogram-platform'
},
{
id: 8,
title: '企业级ERP系统',
description: '面向中大型企业的ERP系统,涵盖财务管理、人力资源、供应链管理等核心业务模块。',
image: '/case8.jpg',
tags: ['ERP', '企业管理', '一体化'],
link: '/cases/enterprise-erp'
}
];
return (
<section className="py-16 lg:py-24 bg-gray-50">
<div className="container mx-auto px-4">
<div className="text-center mb-16">
<h2 className="text-3xl lg:text-4xl font-bold text-center mb-4"></h2>
<p className="text-lg text-gray-600 text-center mb-12 max-w-2xl mx-auto">
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{cases.map((caseItem) => (
<div key={caseItem.id} className="bg-white rounded-lg shadow-sm overflow-hidden hover:shadow-md transition-shadow duration-300">
<div className="relative h-48 bg-gray-200">
<Image
src={caseItem.image}
alt={caseItem.title}
fill
className="object-cover"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDMwMCAyMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIzMDAiIGhlaWdodD0iMjAwIiBmaWxsPSIjRjNGNEY2Ii8+CjxwYXRoIGQ9Ik0xMjUgNzVIMTc1VjEyNUgxMjVWNzVaIiBmaWxsPSIjOUNBM0FGIi8+CjxwYXRoIGQ9Ik0xMDAgMTAwSDIwMFYxMDBIMTAwWiIgZmlsbD0iIzlDQTNBRiIvPgo8L3N2Zz4K';
}}
/>
</div>
<div className="p-6">
<h3 className="text-lg font-semibold mb-2 hover:text-blue-600 transition-colors duration-300">
{caseItem.title}
</h3>
<p className="text-gray-600 text-sm mb-4 line-clamp-3">
{caseItem.description}
</p>
<div className="flex flex-wrap gap-2 mb-4">
{caseItem.tags.map((tag, index) => (
<span
key={index}
className="inline-block px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 rounded-full"
>
{tag}
</span>
))}
</div>
<a
href={caseItem.link}
className="text-blue-600 text-sm font-medium hover:text-blue-700 transition-colors duration-300"
>
</a>
</div>
</div>
))}
</div>
<div className="text-center mt-12">
<a
href="/cases"
className="inline-flex items-center px-6 py-3 text-sm font-medium text-blue-600 bg-blue-50 rounded-lg hover:bg-blue-100 transition-colors duration-300"
>
<svg className="ml-2 w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clipRule="evenodd" />
</svg>
</a>
</div>
</div>
</section>
);
};
export default CasesSection;

86
src/components/sections/PartnersSection.tsx

@ -0,0 +1,86 @@
'use client';
import Image from 'next/image';
const PartnersSection = () => {
const partners = [
{
name: '阿里云',
logo: '/partners/aliyun.png',
description: '云计算服务合作伙伴'
},
{
name: '腾讯云',
logo: '/partners/tencent.png',
description: '云服务技术合作'
},
{
name: '华为',
logo: '/partners/huawei.png',
description: '企业级解决方案合作'
},
{
name: '百度',
logo: '/partners/baidu.png',
description: 'AI技术合作伙伴'
},
{
name: '字节跳动',
logo: '/partners/bytedance.png',
description: '前端技术合作'
}
];
return (
<section className="py-16 lg:py-24 bg-white">
<div className="container mx-auto px-4">
<div className="text-center mb-16">
<h2 className="text-3xl lg:text-4xl font-bold text-center mb-4"></h2>
<p className="text-lg text-gray-600 text-center mb-12 max-w-2xl mx-auto">
</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-8 items-center">
{partners.map((partner, index) => (
<div key={index} className="flex flex-col items-center group">
<div className="w-24 h-24 bg-gray-100 rounded-lg flex items-center justify-center mb-4 group-hover:bg-gray-200 transition-colors duration-300">
<Image
src={partner.logo}
alt={partner.name}
width={80}
height={80}
className="object-contain filter grayscale group-hover:grayscale-0 transition-all duration-300"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiBmaWxsPSIjRjNGNEY2Ii8+CjxwYXRoIGQ9Ik0zNSAzMEg0NVY1MEgzNVYzMFoiIGZpbGw9IiM5Q0EzQUYiLz4KPHBhdGggZD0iTTMwIDQwSDUwVjQwSDMwWiIgZmlsbD0iIzlDQTNBRiIvPgo8L3N2Zz4K';
}}
/>
</div>
<h3 className="text-sm font-medium text-gray-900 mb-1">{partner.name}</h3>
<p className="text-xs text-gray-500 text-center">{partner.description}</p>
</div>
))}
</div>
{/* 合作统计 */}
<div className="mt-16 grid grid-cols-1 md:grid-cols-3 gap-8 text-center">
<div>
<div className="text-3xl font-bold text-blue-600 mb-2">50+</div>
<div className="text-gray-600"></div>
</div>
<div>
<div className="text-3xl font-bold text-blue-600 mb-2">200+</div>
<div className="text-gray-600"></div>
</div>
<div>
<div className="text-3xl font-bold text-blue-600 mb-2">5+</div>
<div className="text-gray-600"></div>
</div>
</div>
</div>
</section>
);
};
export default PartnersSection;
Loading…
Cancel
Save