今年又陆续接到好几个“网上销售网站”方向的咨询,题目几乎都是同一个模板:基于Nodejs+Vue+ElementUI的网上产品销售网站的设计与实现。
说实话,这类选题是典型的全栈入门题里最有代表性的一个。技术栈生态完整、前后端分离思路清晰、功能边界明确,做出来既能讲清楚 Vue 组件通信,也能说清 Node.js 接口设计,放到毕业设计或者简历项目里都非常合适。但我也看了不少同学做这个题目时卡住的地方,基本不是业务逻辑太难,而是栽在环境配置、组件使用细节、前后端联调这三类问题上。
这篇文章我会按自己做过类似项目的顺序来写:先说动手前必须定下来的架构和数据库表设计,再讲后端 Node.js 接口的分层实现,然后说 Vue + ElementUI 前端工程怎么搭、页面怎么做,最后盘一盘最容易让新手崩溃的配置和联调问题。你按这个顺序一步步做,基本不会走偏。
1. 整体架构与数据库设计:动手前先吃掉这三块
1.1 技术选型为什么这样搭配,Vue 2 还是 Vue 3 要分清
这个题目的核心能力其实就是“买卖双方通过网站完成商品浏览、下单、订单查看”,属于典型的中小型管理系统加电商逻辑。后端用 Node.js,核心价值有两点:
- 开发语言统一,前端同学不用额外学 Java 那套工程结构;
- Express 中间件生态成熟,搭接口、配跨域、做鉴权都非常快。
如果你会 Spring Boot,也可以做这个题目,但既然题面写的是 Nodejs,那就老老实实把 Express 和 MySQL 打通。
有一个关键点必须先提醒:ElementUI 这个组件库只对应 Vue 2,Vue 3 对应的组件库叫 Element Plus。很多新手在这个地方反复踩坑,下载 ElementUI 之后启动报错,或者页面直接空白。所以如果你的题目里明确写了“ElementUI”,建议直接用 Vue 2.6 + Element UI 2.15.x 这套组合,这也是目前各种管理系统毕设里最稳的组合。
Node.js 版本建议装 16 到 18 之间的 LTS 版本。版本太高的话,有些旧脚手架依赖会有兼容警告,太低的话新版本 npm 语法又不支持。我实际测试下来,Node 16.20 配合 Vue CLI 5 和 Express 4,基本不会出什么幺蛾子。
当然,如果你对 Vue 3 更熟,也可以把题目里的 ElementUI 理解成 Element Plus 来做。组件名称大部分一样,少量 API 有差异,文章后面我会标注这些差异点。
1.2 前端、后端、数据库三块的目录与信息流设计
先不要急着写代码,我把整个系统的信息流画一遍:用户打开浏览器访问前端页面,前端通过 axios 请求 Node.js 后端接口,后端操作 MySQL 数据库,把数据返回给前端,前端渲染到 ElementUI 组件里。
所以项目从物理上拆成两块:
code复制online-shop/
├── client # Vue 前端工程
│ ├── public
│ └── src
│ ├── api
│ ├── assets
│ ├── components
│ ├── router
│ ├── store
│ ├── views
│ ├── App.vue
│ └── main.js
└── server # Node.js 后端工程
├── routes
├── controllers
├── models
├── middleware
├── sql
├── app.js
├── package.json
└── .env
client 放前端,server 放后端,两个目录各自有 package.json。开发时开着两个终端,一个跑 npm run serve,一个跑 node app.js。
在动手之前把目录拆好,最大的好处是后面答辩或者写报告时,你可以很清楚地讲出“前端负责展示和交互,后端负责业务逻辑和数据持久化”。这本身就是前后端分离架构的核心得分点。
数据库设计建议单独建一个 online_shop 库,字符集选 utf8mb4。这个字符集必须用 utf8mb4,不是 utf8,否则用户填了个生僻字或者表情符号,插入数据库就直接报错,很多新手会在订单备注功能上遇到这个诡异的乱码问题。
1.3 核心数据库表到底建哪几张
按电商系统通常的最小闭环来设计,一般需要六张核心表。我以实际可用的字段清单为例,你直接抄也行,适当扩展也可以:
- user 用户表:id、username、password、nickname、avatar、phone、create_time。注意 password 不要存明文,存 bcrypt 加密后的字符串。
- category 商品分类表:id、name、sort。商品按分类展示是后台管理的常见要求。
- product 商品表:id、category_id、name、description、price、stock、image、sales、status。price 字段我用 decimal(10,2),不用 float,避免浮点数算钱出问题。
- cart 购物车表:id、user_id、product_id、quantity、selected。selected 用来标记勾选状态,方便后面的结算逻辑。
- orders 订单表:id、order_no、user_id、total_amount、status、address、create_time。status 可以设计成多个订单状态。
- order_item 订单明细表:id、order_id、product_id、product_name、product_image、price、quantity。
这几张表之间的关联关系也简单清晰:用户下单时把购物车里选中的商品捞出来,计算总价生成 orders,再把每个商品拆成 order_item 明细。这样订单表不冗余,明细表又能记录下单那一刻的商品快照。
订单状态这里我习惯用一个整数表示:0 待付款、1 待发货、2 待收货、3 已完成、4 已取消。后端接口里统一处理这个状态枚举,前端再映射成 ElementUI 的 tag 标签颜色。实际做毕设时,“支付”通常用模拟状态代替,不需要真的接第三方支付接口。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 后端 Node.js 接口怎么分层:从用户鉴权到商品模块
2.1 初始化 Express 项目并处理跨域
先建一个 server 目录,在里面执行 npm init -y,然后安装基础依赖:
bash复制npm install express mysql2 cors body-parser jsonwebtoken bcryptjs
Express 4 目前还是最主流的选择。安装的时候注意一定要把 mysql2、cors、jsonwebtoken、bcryptjs 列出来,这几个分别负责数据库连接、跨域处理、登录令牌、密码加密。
后端入口文件我习惯叫 app.js,内容大致如下:
javascript复制const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const app = express();
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// 静态资源,后续存储图片用
app.use('/uploads', express.static(__dirname + '/uploads'));
// 路由注册
app.use('/api/user', require('./routes/user'));
app.use('/api/product', require('./routes/product'));
app.use('/api/cart', require('./routes/cart'));
app.use('/api/order', require('./routes/order'));
app.listen(3000, () => {
console.log('server running at http://localhost:3000');
});
cors() 这个中间件不要省略,它就是用来解决“前端 http://localhost:8080 访问后端 http://localhost:3000 被浏览器拦截”问题的。很多人第 1 次请求就报跨域,根本原因其实就是没加这个中间件。
在正常项目中我还会把数据库连接单独抽一个 db.js:
javascript复制const mysql = require('mysql2');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: '你的数据库密码',
database: 'online_shop',
waitForConnections: true,
connectionLimit: 10,
});
module.exports
