React + Node.js + MySQL全栈网站系统开发教程
我来帮你建立一个基于React + Node.js + MySQL的完整网站系统。 ## 🚀 项目架构 ``` website-system/ ├── frontend/ # React前端 ├── backend/ # Node.js后端 └── database/ # 数据库脚本 ``` ## 💻 后端代码 (Node.js + Express) ### 安装后端依赖 ```bash mkdir backend && cd backend npm init -y npm install express cors mysql2 body-parser multer ``` ### 后端主文件 `server.js` ```javascript const express = require('express'); const cors = require('cors'); const bodyParser = require('body-parser'); const multer = require('multer'); const path = require('path'); const db = require('./db'); const app = express(); const PORT = 5000; // 中间件配置 app.use(cors()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use('/uploads', express.static('uploads')); // 文件上传配置 const storage = multer.diskStorage({ destination: './uploads/', filename: (req, file, cb) => { cb(null, Date.now() + path.extname(file.originalname)); } }); const upload = multer({ storage }); // API: 获取所有内容 app.get('/api/contents', async (req, res) => { try { const [rows] = await db.query('SELECT * FROM contents'); res.json(rows); } catch (error) { res.status(500).json({ error: error.message }); } }); // API: 更新内容 app.put('/api/contents/:id', async (req, res) => { try { const { id } = req.params; const { title_zh, title_en, content_zh, content_en } = req.body; await db.query( 'UPDATE contents SET title_zh=?, title_en=?, content_zh=?, content_en=? WHERE id=?', [title_zh, title_en, content_zh, content_en, id] ); res.json({ message: '更新成功' }); } catch (error) { res.status(500).json({ error: error.message }); } }); // API: 上传图片 app.post('/api/upload', upload.single('image'), (req, res) => { if (!req.file) { return res.status(400).json({ error: '没有上传文件' }); } res.json({ filename: req.file.filename, url: `/uploads/${req.file.filename}` }); }); app.listen(PORT, () => { console.log(`后端服务运行在 http://localhost:${PORT}`); }); ``` ### 数据库连接文件 `db.js` ```javascript const mysql = require('mysql2/promise'); const pool = mysql.createPool({ host: 'localhost', user: 'root', password: 'your_password', database: 'website_system', waitForConnections: true, connectionLimit: 10, queueLimit: 0 }); module.exports = pool; ``` ## 🗄️ 数据库脚本 `database.sql` ```sql CREATE DATABASE website_system; USE website_system; CREATE TABLE contents ( id INT PRIMARY KEY AUTO_INCREMENT, section VARCHAR(50) NOT NULL, title_zh VARCHAR(200), title_en VARCHAR(200), content_zh TEXT, content_en TEXT, image_url VARCHAR(500), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- 初始化数据 INSERT INTO contents (section, title_zh, title_en, content_zh, content_en) VALUES ('hero', '欢迎访问我们的网站', 'Welcome to Our Website', '这是一个简单的演示网站', 'This is a simple demo website'), ('about', '关于我们', 'About Us', '我们是一家技术公司', 'We are a technology company'); ``` ## 🎨 前端代码 (React) ### 安装前端依赖 ```bash npx create-react-app frontend cd frontend npm install axios react-i18next i18next ``` ### 前端主文件 `App.js` ```jsx import React, { useState, useEffect } from 'react'; import axios from 'axios'; import AdminPanel from './AdminPanel'; import './App.css'; import './i18n'; import { useTranslation } from 'react-i18next'; function App() { const [contents, setContents] = useState([]); const [showAdmin, setShowAdmin] = useState(false); const { t, i18n } = useTranslation(); useEffect(() => { fetchContents(); }, []); const fetchContents = async () => { try { const response = await axios.get('http://localhost:5000/api/contents'); setContents(response.data); } catch (error) { console.error('获取内容失败:', error); } }; const changeLanguage = (lang) => { i18n.changeLanguage(lang); }; return ( <div className="App"> {/* 语言切换按钮 */} <div className="language-switcher"> <button onClick={() => changeLanguage('zh')}>中文</button> <button onClick={() => changeLanguage('en')}>English</button> </div> {/* 内容展示区 */} <div className="content-area"> {contents.map(item => ( <div key={item.id} className="content-block"> <h2> {i18n.language === 'zh' ? item.title_zh : item.title_en} </h2> <p> {i18n.language === 'zh' ? item.content_zh : item.content_en} </p> {item.image_url && ( <img src={`http://localhost:5000${item.image_url}`} alt="content" /> )} </div> ))} </div> {/* 管理按钮 */} <button onClick={() => setShowAdmin(true)} className="admin-btn" > 后台管理 </button> {/* 后台管理面板 */} {showAdmin && ( <AdminPanel contents={contents} onClose={() => setShowAdmin(false)} onUpdate={fetchContents} /> )} </div> ); } export default App; ``` ### 后台管理组件 `AdminPanel.js` ```jsx import React, { useState } from 'react'; import axios from 'axios'; function AdminPanel({ contents, onClose, onUpdate }) { const [editingContent, setEditingContent] = useState(null); const [editForm, setEditForm] = useState({}); const handleEdit = (content) => { setEditingContent(content.id); setEditForm({ title_zh: content.title_zh, title_en: content.title_en, content_zh: content.content_zh, content_en: content.content_en }); }; const handleSave = async (id) => { try { await axios.put(`http://localhost:5000/api/contents/${id}`, editForm); onUpdate(); setEditingContent(null); } catch (error) { console.error('保存失败:', error); } }; const handleImageUpload = async (id, file) => { const formData = new FormData(); formData.append('image', file); try { await axios.post('http://localhost:5000/api/upload', formData); onUpdate(); } catch (error) { console.error('上传失败:', error); } }; return ( <div className="admin-panel"> <div className="admin-header"> <h2>后台管理</h2> <button onClick={onClose}>关闭</button> </div> <div className="admin-content"> {contents.map(content => ( <div key={content.id} className="edit-block"> {editingContent === content.id ? ( <div className="edit-form"> <input type="text" value={editForm.title_zh} onChange={(e) => setEditForm({ ...editForm, title_zh: e.target.value })} placeholder="中文" /> <input type="text" value={editForm.title_en} onChange={(e) => setEditForm({ ...editForm, title_en: e.target.value })} placeholder="英文" /> <textarea value={editForm.content_zh} onChange={(e) => setEditForm({ ...editForm, content_zh: e.target.value })} placeholder="中文内容" /> <textarea value={editForm.content_en} onChange={(e) => setEditForm({ ...editForm, content_en: e.target.value })} placeholder="英文内容" /> <input type="file" onChange={(e) => handleImageUpload(content.id, e.target.files[0])} /> <div className="edit-buttons"> <button onClick={() => handleSave(content.id)}>保存</button> <button onClick={() => setEditingContent(null)}>取消</button> </div> </div> ) : ( <div className="content-preview"> <h3>{content.title_zh}</h3> <p>{content.content_zh}</p> <button onClick={() => handleEdit(content)}>编辑</button> </div> )} </div> ))} </div> </div> ); } export default AdminPanel; ``` ## 🌐 国际化配置 `i18n.js` ```javascript import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; const resources = { zh: { translation: { "admin": "后台管理", "close": "关闭", "edit": "编辑", "save": "保存", "cancel": "取消" } }, en: { translation: { "admin": "Admin Panel", "close": "Close", "edit": "Edit", "save": "Save", "cancel": "Cancel" } } }; i18n.use(initReactI18next).init({ resources, lng: 'zh', fallbackLng: 'en', interpolation: { escapeValue: false } }); export default i18n; ``` ## 🎯 启动项目 1. 创建数据库并导入脚本 2. 启动后端:`cd backend && node server.js` 3. 启动前端:`cd frontend && npm start` ######[AI问答 | 2381点数解答 | 2026-06-21 13:03:06]
- C++ 代码实现:查找链表最大节点并移至末尾(字节豆包 | 549点数解答 | 2024-10-14 22:55:13)339
- 单链表插入节点代码:从问题剖析到完美修正全解析(字节豆包 | 907点数解答 | 2025-12-23 13:26:58)59
- "A* Search Algorithm in Java: Code Improvements and Heuristic Calculation"(讯飞星火 | 688点数解答 | 2024-10-27 10:06:00)259
- 详解Java代码:A*搜索算法实现最短路径查找(讯飞星火 | 584点数解答 | 2024-10-27 10:07:19)294
- A* 算法:高效实现路径搜索的 Java 代码解析(GPT | 1121点数解答 | 2024-10-27 10:09:56)303
- 编程实战:输入正整数 n 构建链表并删除奇数值结点(字节豆包 | 485点数解答 | 2024-11-23 14:50:39)211
- C语言:实现两个非降序链表交集新链表S3的构造与代码解析(字节豆包 | 462点数解答 | 2024-12-05 22:30:36)233
- 单链表删除节点代码报错原因剖析与解决方案(字节豆包 | 1001点数解答 | 2025-12-23 10:46:21)61
- Node - Sass 安装报错与 502 Bad Gateway:NPM 问题大揭秘 ( | 552点数解答 | 2024-02-22 09:55:10)416
- "Node - Sass Installation Errors: Reasons, Troubleshooting, and Solutions"(阿里通义 | 337点数解答 | 2024-02-22 09:55:21)451
- 解决npm安装node - sass报错:多种实用方法大揭秘 (讯飞星火 | 141点数解答 | 2024-02-22 09:55:21)320
- 解决npm安装node - sass@6.0.0报错的实用方法(讯飞星火 | 184点数解答 | 2024-02-22 10:23:20)350