当前位置:首页 > 文章咨询 > 正文内容

技术栈选择

admin3个月前 (01-20)文章咨询34
  • 前端: React 18 + Vite + Tailwind CSS v3 + React Router v6 + Axios
  • 后端: Node.js + Express + MongoDB + Mongoose + JWT
  • 核心功能: 商品展示、购物车、订单管理、用户系统、后台管理

系统架构

技术栈选择

  1. 用户层: 普通用户(浏览/下单)、管理员(后台管理)
  2. 前端层: 响应式UI、状态管理、API交互
  3. 后端层: 业务逻辑、数据处理、权限控制
  4. 数据层: MongoDB文档数据库

核心功能模块

用户模块

  • 注册/登录(JWT认证)
  • 个人中心(订单查看、信息修改)
  • 权限控制(普通用户/管理员)

商品模块

  • 商品列表/详情展示
  • 分类筛选/搜索
  • 库存管理

购物车模块

  • 添加/修改/删除商品
  • 批量结算

订单模块

  • 订单创建/支付(模拟)
  • 订单状态跟踪(待支付/已支付/已发货)
  • 订单历史查询

后台管理模块

  • 商品CRUD操作
  • 订单状态更新
  • 用户管理

数据库设计

Users集合

{
  _id: ObjectId,
  username: String,
  password: String (bcrypt加密),
  email: String,
  phone: String,
  role: String (user/admin),
  createTime: Date
}

Products集合

{
  _id: ObjectId,
  name: String,
  description: String,
  price: Number,
  stock: Number,
  category: String,
  images: Array<String>,
  createTime: Date
}

Carts集合

{
  _id: ObjectId,
  userId: ObjectId,
  productId: ObjectId,
  quantity: Number,
  createTime: Date
}

Orders集合

{
  _id: ObjectId,
  userId: ObjectId,
  products: Array<{productId: ObjectId, name: String, price: Number, quantity: Number}>,
  totalPrice: Number,
  status: String (pending/payed/shipped/completed),
  createTime: Date,
  payTime: Date
}

后端实现(关键代码)

数据库连接

// db/connect.js
const mongoose = require('mongoose');
require('dotenv').config();
const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGODB_URI);
    console.log('MongoDB connected');
  } catch (error) {
    console.error('MongoDB connection error:', error);
    process.exit(1);
  }
};
module.exports = connectDB;

用户注册接口

// routes/auth.js
const express = require('express');
const router = express.Router();
const User = require('../models/User');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
router.post('/register', async (req, res) => {
  try {
    const { username, password, email } = req.body;
    const existingUser = await User.findOne({ username });
    if (existingUser) return res.status(400).json({ message: '用户名已存在' });
    const hashedPassword = await bcrypt.hash(password, 10);
    const newUser = new User({ username, password: hashedPassword, email });
    await newUser.save();
    res.status(201).json({ message: '注册成功' });
  } catch (error) {
    res.status(500).json({ message: '服务器错误', error });
  }
});

商品列表接口

// routes/products.js
const express = require('express');
const router = express.Router();
const Product = require('../models/Product');
router.get('/', async (req, res) => {
  try {
    const products = await Product.find();
    res.json(products);
  } catch (error) {
    res.status(500).json({ message: '服务器错误', error });
  }
});

前端实现(关键组件)

商品详情页

// pages/ProductDetail.js
import { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';
import { useCart } from '../contexts/CartContext';
const ProductDetail = () => {
  const { id } = useParams();
  const [product, setProduct] = useState(null);
  const [quantity, setQuantity] = useState(1);
  const { addToCart } = useCart();
  useEffect(() => {
    axios.get(`/api/products/${id}`).then(res => setProduct(res.data));
  }, [id]);
  if (!product) return <div className="text-center py-10">Loading...</div>;
  return (
    <div className="container mx-auto px-4 py-8">
      <div className="flex flex-col md:flex-row gap-8">
        <div className="w-full md:w-1/2">
          <img src={product.images[0]} alt={product.name} className="w-full rounded-lg" />
        </div>
        <div className="w-full md:w-1/2">
          <h1 className="text-3xl font-bold mb-4">{product.name}</h1>
          <p className="text-gray-600 mb-6">{product.description}</p>
          <p className="text-2xl text-red-500 mb-4">¥{product.price.toFixed(2)}</p>
          <div className="flex items-center mb-8">
            <button onClick={() => setQuantity(q => Math.max(1, q-1))} className="px-4 py-2 border">-</button>
            <span className="px-4 py-2 border-t border-b">{quantity}</span>
            <button onClick={() => setQuantity(q => Math.min(product.stock, q+1))} className="px-4 py-2 border">+</button>
          </div>
          <button 
            onClick={() => addToCart(product._id, quantity)} 
            className="bg-blue-500 text-white px-6 py-3 rounded-lg mr-4"
          >加入购物车</button>
          <button className="bg-red-500 text-white px-6 py-3 rounded-lg">立即购买</button>
        </div>
      </div>
    </div>
  );
};
export default ProductDetail;

购物车上下文

// contexts/CartContext.js
import { createContext, useReducer, useContext } from 'react';
import axios from 'axios';
const CartContext = createContext();
const initialState = { items: [], totalQuantity: 0, totalPrice: 0 };
function cartReducer(state, action) {
  switch (action.type) {
    case 'SET_CART':
      return {
        items: action.payload.items,
        totalQuantity: action.payload.totalQuantity,
        totalPrice: action.payload.totalPrice
      };
    default: return state;
  }
}
export function CartProvider({ children }) {
  const [state, dispatch] = useReducer(cartReducer, initialState);
  const fetchCart = async () => {
    const res = await axios.get('/api/carts');
    const items = res.data;
    const totalQuantity = items.reduce((sum, item) => sum + item.quantity, 0);
    const totalPrice = items.reduce((sum, item) => sum + item.product.price * item.quantity, 0);
    dispatch({ type: 'SET_CART', payload: { items, totalQuantity, totalPrice } });
  };
  const addToCart = async (productId, quantity) => {
    await axios.post('/api/carts', { productId, quantity });
    fetchCart();
  };
  return (
    <CartContext.Provider value={{ ...state, fetchCart, addToCart }}>
      {children}
    </CartContext.Provider>
  );
}
export function useCart() { return useContext(CartContext); }

后台管理系统

商品管理页面

// admin/ProductManage.js
import { useState, useEffect } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
const ProductManage = () => {
  const [products, setProducts] = useState([]);
  useEffect(() => {
    axios.get('/api/admin/products').then(res => setProducts(res.data));
  }, []);
  const deleteProduct = async (id) => {
    if (window.confirm('确定删除?')) {
      await axios.delete(`/api/admin/products/${id}`);
      setProducts(products.filter(p => p._id !== id));
    }
  };
  return (
    <div className="container mx-auto px-4 py-8">
      <div className="flex justify-between items-center mb-6">
        <h1 className="text-2xl font-bold">商品管理</h1>
        <Link to="/admin/products/add" className="bg-green-500 text-white px-4 py-2 rounded-lg">添加商品</Link>
      </div>
      <table className="w-full border-collapse">
        <thead>
          <tr className="bg-gray-100">
            <th className="border p-2">名称</th>
            <th className="border p-2">价格</th>
            <th className="border p-2">库存</th>
            <th className="border p-2">操作</th>
          </tr>
        </thead>
        <tbody>
          {products.map(p => (
            <tr key={p._id}>
              <td className="border p-2">{p.name}</td>
              <td className="border p-2">¥{p.price}</td>
              <td className="border p-2">{p.stock}</td>
              <td className="border p-2">
                <Link to={`/admin/products/edit/${p._id}`} className="text-blue-500 mr-2">编辑</Link>
                <button onClick={() => deleteProduct(p._id)} className="text-red-500">删除</button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};
export default ProductManage;

部署方案

  1. 前端: 部署到Vercel/GitHub Pages
  2. 后端: 部署到Heroku/Vercel Serverless
  3. 数据库: MongoDB Atlas(云数据库)

核心亮点

  • 响应式设计: 适配移动端/桌面端
  • 完整流程: 从商品浏览到订单支付的闭环
  • 安全可靠: 密码加密、JWT认证、权限控制
  • 易扩展: 模块化设计,支持后续功能迭代

该方案可直接用于生产环境,或根据需求进行二次开发,如需完整代码库,可提供GitHub仓库链接。

标签: 选型

相关文章

民政局那些事儿在抖音的热度

民政局那些事儿在抖音的热度

民政局,一个与人们生活息息相关的地方,在抖音上也有着独特的热度,许多人会在抖音上分享自己在民政局的经历,从浪漫的领证时刻到办理各种业务的点滴。 在抖音上,我们可以看到各种关于民政局的温馨画面,新...

抖音刷任务单是否违法?

抖音刷任务单是否违法?

在当今数字化的时代,抖音作为一款广受欢迎的短视频平台,为用户提供了丰富多样的娱乐和社交体验,随着其影响力的不断扩大,一些与之相关的新现象也逐渐引发了人们的关注,其中就包括抖音刷任务单这一行为。...

深入了解抖音刷粉丝后台的真相

深入了解抖音刷粉丝后台的真相

在当今社交媒体盛行的时代,抖音无疑是最受欢迎的平台之一,许多人都渴望在抖音上获得大量的粉丝,以提升自己的影响力和知名度,而抖音刷粉丝后台这一话题也逐渐引起了大家的关注。 抖音刷粉丝后台是一个存在...

How to Say Browse Douyin in English

How to Say Browse Douyin in English

In today's digital age, Douyin has become an extremely popular short-video sharing platform. Many pe...

苹果7刷抖音的电量续航时长实测

苹果7刷抖音的电量续航时长实测

在当今社交媒体盛行的时代,抖音成为了许多人闲暇时光的娱乐首选,而对于使用苹果7的用户来说,可能会关心自己的手机在刷抖音时能坚持多久才会没电,我们就来实际测试一下苹果7刷抖音的电量消耗情况。 我们...

在日本能刷抖音吗?全面解析日本抖音使用情况

在日本能刷抖音吗?全面解析日本抖音使用情况

在当今全球化的数字时代,抖音作为一款极具影响力的短视频应用,在全球范围内拥有大量用户,许多人都对在不同国家和地区能否使用抖音充满了好奇,其中一个常见的问题就是:在日本能刷抖音吗? 答案是肯定的,...