资讯动态

基于 Vue + Node.js + Element UI 的宠物交易管理系统设计与实践

发布时间:2026/9/23 1:43:37 来源:尧图企业网站定制
去年帮一位做宠物用品的朋友改造门店管理系统需求聊到最后变成了一个完整的宠物交易平台。他要的不只是商品上架下架而是把整个交易链路管起来宠物档案、寄养预约、买卖订单、客户回访、库存盘点全部塞进一个后台里。当时手头正好是 Vue 2.6 Node.js Element UI 这套组合项目代号就叫“爱它”。断断续续开发了两个多月踩了无数坑也沉淀了不少可复用的经验这篇博客就把整个项目的设计思路和关键实现拆开来讲清楚。这个系统做出来之后最大的价值不是“能发布宠物信息”这么简单而是把宠物交易里最容易出问题的环节比如同一只宠物被重复下单、买家付款后卖家不发货、疫苗记录和健康档案缺失都用业务流程和代码逻辑给约束住了。整套系统分成前台展示和后台管理两大块前台是宠物展示、详情浏览、在线下单、订单追踪后台是宠物管理、订单审核、客户管理、数据统计。技术栈锁死在 Vue Node.js Element UI数据库用的 MySQL部署在腾讯云轻量服务器上。适合正在做前后端分离项目、想了解完整业务闭环怎么落地的同学参考。1. 从业务梳理到技术落地宠物交易系统到底需要哪些模块很多人一拿到“宠物交易管理系统”这个需求第一反应就是做个宠物信息的增删改查再加个订单表就完事了。真做了才发现交易流程的复杂度远超想象。宠物不是普通商品它有品种、年龄、性别、疫苗状态、健康情况、父母血统这些属性而且每只宠物往往只有一个库存卖掉了就没了。这就意味着订单模块必须做到“锁库存”否则两个人同时下单同一只猫后台数据就对不上了。我在设计这个系统时把业务模块分成了四层。第一层是基础数据层包含用户表、宠物分类表、宠物信息表。用户表里区分了普通用户和管理员宠物分类表做了两级分类比如“猫咪 - 英短”“狗狗 - 金毛”这样前台筛选时可以按大类缩小范围再按小类精确定位。宠物信息表是所有业务的核心字段设计上除了基本名称、价格、图片之外特别加了status字段来标识宠物的状态0 表示草稿未上架1 表示展示中2 表示已被下单锁定3 表示已售出。第二层是交易层包含购物车表和订单表。购物车表很简单就是 user_id 加 pet_id 的关联。订单表稍微复杂一点我把订单状态设计成了待付款、待发货、待收货、已完成、已取消、退款申请、退款完成这几个状态每个状态之间的流转是有严格限制的。比如待付款状态用户主动取消可以管理员后台也可以关闭订单但宠物一旦进入待发货状态库存就彻底锁死了不允许再被其他用户看到。第三层是互动层包含收藏表、评论表和咨询记录表。收藏表用于前台“心仪宠物”功能评论区允许用户对已购买的宠物进行评价咨询记录表则记录买家和卖家之间的沟通信息。第四层是系统管理包含轮播图表、公告表和操作日志表。轮播图和公告都是后台可配置的操作日志记录了管理员的关键操作方便出问题时追溯。这套模块设计的关键逻辑在于先摸清业务场景再设计数据库最后才是写代码。我的经验是如果你拿到需求直接敲代码八成会在开发到一半时发现表结构不够用回头改数据库非常痛苦。宁可前期多花两天把字段、状态、关联关系理清楚也不要后期加班补坑。技术落地方面前端我用的是 Vue 2.6 Element UI Axios Vue Router Vuex后端是 Node.js 的 Express 框架配合 mysql2 连接数据库。前端工程通过 Vue CLI 4.x 创建后端手动搭建目录不引入过多的重型框架保持逻辑清晰。整个项目采用前后端完全分离的开发模式前端跑在 8080 端口后端跑在 3000 端口通过代理和 CORS 解决跨域问题。项目根目录结构前后端分离 . ├── frontend # 前端工程Vue 2.6 │ ├── public │ ├── src │ │ ├── api # 接口请求模块 │ │ ├── assets # 静态资源 │ │ ├── components # 公共组件 │ │ ├── router # 路由配置 │ │ ├── store # Vuex 状态管理 │ │ ├── views # 页面视图 │ │ ├── App.vue │ │ └── main.js │ ├── package.json │ └── vue.config.js └── backend # 后端工程Node.js ├── app.js # 服务入口文件 ├── config # 数据库配置、密钥配置 ├── controller # 控制器层接收请求、返回响应 ├── middleware # 中间件JWT验证、上传处理 ├── routes # 路由定义 ├── service # 业务逻辑层处理核心业务 ├── sql # SQL 初始化脚本 └── utils # 工具函数日期、加密、响应封装这个结构是我在实际项目中验证过比较清晰的一种。小项目不用搞微服务也不用分层分得特别细但至少要把 controller 和 service 分开否则业务逻辑全堆在路由回调函数里后期完全没办法维护。2. 为什么是 Vue Node.js Element UI 这套组合选型时的真实考量很多同学在技术选型时会犹豫到底是 Vue 还是 React后端是用 Node.js 还是 Java Spring BootUI 组件库选 Element UI 还是 Ant Design。我当初选这套组合其实有非常具体的理由不是盲目跟风。先说 Vue。这个项目有前台展示和后台管理两块后台管理部分有大量的表单、表格、弹窗、标签页交互Vue 的双向数据绑定和指令系统在这种场景下非常顺手。特别是 Element UI 的表格组件配合 Vue 的响应式数据做订单列表、宠物列表这类页面几乎不用自己手写 DOM 操作数据和视图自动同步。前台展示部分虽然偏静态但 Vue 的组件化开发方式也能很好地复用宠物卡片组件、分页组件等。再说 Node.js。这里有一个关键点团队的技术栈统一。如果前端用 Vue后端用 Java那就需要维护两套语言体系和两套部署流程。Node.js 让整个项目只需要一种语言前端工程师也能轻松走后端逻辑沟通成本低很多。更重要的是宠物交易系统属于中小型业务系统QPS 峰值不会特别高Node.js 的异步 I/O 和事件循环机制完全够用。Express 作为最经典的 Node.js Web 框架中间件生态丰富上手门槛低配 mysql2 操作 MySQL 数据库非常稳定。Element UI 这套组件库在后台管理系统领域的统治地位不是没有道理的。它的表格组件支持自定义列、多级表头、排序、筛选表单组件自带校验规则弹窗、消息提示、确认框一应俱全。我只需要调整配置项和样式变量就能快速搭出一个专业感很强的后台界面。和 Ant Design 相比Element UI 的 API 设计更符合 Vue 的思维习惯组件属性都是声明式的学起来不费劲。这套组合的局限性也要说清楚。如果是高并发、大数据量的系统Node.js 单线程的弱点就会暴露如果项目对 UI 定制化程度要求极高Element UI 默认样式反而会成为束缚。但对一个宠物交易管理系统来说这个选型的性价比是最高的。我见过太多团队拿 Spring Cloud 那套做这种小项目最后维护成本比开发成本还高。技术选型对比表格 技术维度 Vue Node.js Element UI Java Spring Boot Vue React Express 开发语言 前后端统一 JavaScript/Node.js 前端 JavaScript后端 Java 前后端统一 JavaScript 学习曲线 低中 高Java 体系庞大 中 UI 组件生态 Element UI 极适合后台管理系统 与 Vue 配合也常用 Element UI Ant Design 优秀但风格偏复杂 部署成本 轻量单台服务器 PM2 即可 重需要 JDK 环境、打包配置 轻量同 Node.js 中小型业务适合度 高 一般资源浪费 中高 维护难度 中等 较高两套语言体系 中等3. 数据库与接口层设计先把业务的地基打牢数据库设计是我在整个项目中最重视的部分。一个交易系统数据一致性是最基本的底线。宠物交易系统涉及的实体有用户、宠物、订单、购物车、收藏、评论、公告等下面是我认为最有借鉴意义的核心表设计。用户表是最基础的表我设计了id, username, password, nickname, avatar, phone, email, role, status, create_time这几个字段。密码字段需要注意绝不能明文存储我用的是 bcrypt 加密。role字段区分用户角色0 表示普通用户1 表示管理员后续如果需要商家角色可以扩展为 2。status字段表示账号是否被封禁封禁用户在前台登录时会直接被拦截。宠物信息表是整个系统的核心。字段包括品种分类、名称、描述、价格、图片、性别、年龄、体重、是否绝育、疫苗状态、健康状态、所在地以及最重要的seller_id卖家用户ID和status宠物状态。为了让每只宠物都有独立展示页面我增加了pet_no字段作为宠物编号格式类似PET20250101001这个编号在订单详情和客服沟通中非常有用。订单表的设计花了我最多心思。一个订单必须记录订单编号、下单用户、宠物 ID、宠物快照、交易金额、订单状态、收货人信息、物流单号、下单时间、支付时间、发货时间、完成时间。特别注意“宠物快照”这个设计因为宠物信息可能会被下架或修改但订单里必须保留下单那一刻的宠物名称和价格否则后续发生纠纷时没有依据。订单状态流转我用了一套严格的枚举值来控制状态值含义可操作动作0待付款用户支付或取消订单1待发货管理员发货或关闭订单2待收货用户确认收货或申请退款3已完成用户可评价流程结束4已取消流程结束5退款中管理员同意退款原路退回6退款完成流程结束事务处理上还有一个核心点创建订单时一定要开启数据库事务。订单表插入记录和宠物表更新状态这两个操作必须同时成功或同时失败。如果只插入订单但忘记更新宠物状态为 2就会造成同一只宠物被重复下单。我在service层用connection.beginTransaction()和connection.commit()包裹了这两个操作并在异常时rollback()确保数据一致性。接口层设计遵循 RESTful 风格核心接口包括POST /api/user/register 用户注册 POST /api/user/login 登录JWT 签发 GET /api/pet/list 宠物列表分页 条件筛选 GET /api/pet/detail?idxx 宠物详情 POST /api/pet/add 发布宠物管理员 PUT /api/pet/update 更新宠物信息 DELETE /api/pet/delete?idxx 删除宠物 POST /api/cart/add 加入购物车 GET /api/cart/list 购物车列表 POST /api/order/create 创建订单 GET /api/order/list 订单列表分页 状态筛选 PUT /api/order/status 更新订单状态 POST /api/upload 图片/视频上传 GET /api/comment/list 评论列表 POST /api/comment/add 发表评论所有接口统一返回结构这非常重要。我在utils/response.js里封装了一个标准响应函数格式是{ code: 200, message: success, data: {} }前端 Axios 拦截器统一处理。如果后端每个接口返回格式都不一样前端就要写一堆判断逻辑纯属自找麻烦。4. 前端核心业务模块的实现细节从后台框架到关键页面前端部分是重头戏整个后台管理系统加上前台展示页面加起来有二三十个 Vue 组件。我挑几个有代表性的模块讲实现思路。后台整体布局用的是典型的侧边栏加顶栏结构。侧边栏菜单根据权限动态生成管理员登录后能看到完整的菜单树包括仪表盘、宠物管理、订单管理、用户管理、评论管理、公告管理、系统设置。这块我放弃了 vue-element-admin 那种重型脚手架而是自己手动搭了一个轻量布局用 Element UI 的el-container、el-aside、el-header、el-main组合。路由表拆成了静态路由和动态路由两部分静态路由是登录页、注册页、404 页动态路由是后台业务页面登录后根据角色权限用router.addRoutes动态注册。宠物管理页面是后台使用频率最高的页面我把它分成了宠物列表和发布宠物两个核心视图。宠物列表用el-table展示数据来自分页接口。表格列包含宠物编号、名称、分类、价格、状态、发布时间、操作。宠物状态的展示用el-tag标签区分颜色展示中是蓝色已锁定是橙色已售出是灰色。操作列有编辑、上下架、删除三个按钮上下架操作会调用接口修改宠物状态。这里有一个细节删除操作我做了二次确认弹窗而且删除不是物理删除而是逻辑删除修改is_deleted字段为 1列表查询时默认过滤掉已删除数据这样万一误删还能恢复。发布宠物表单是整个系统中最复杂的表单涉及分类级联选择、图片上传、基础信息填写、健康状态勾选。我用el-form加rules校验图片上传用el-upload组件限制文件类型为 jpg/png大小不超过 5MB上传成功后拿到返回的 URL 地址存入表单。健康状态用复选框组设计成疫苗已打、已驱虫、已绝育、有健康证明四个选项。这个表单的开发让我意识到一个好的表单设计不只是字段有多全而是用户填写时是否顺畅。我把所有必填项都放在前面选填项放后面价格和库存这类数据用数字输入框限制范围从源头减少脏数据。订单管理页面用el-tabs做状态分类默认展示全部订单用户也可以按待付款、待发货、待收货等状态快速筛选。订单列表展示订单号、宠物名称、金额、下单用户、状态、时间、操作。点击操作区的“详情”按钮会弹出一个el-dialog展示订单完整信息包括宠物快照、收货地址、物流信息、状态流转时间线。时间线我用了 Element UI 的el-steps组件把待付款、待发货、待收货、已完成四个节点可视化展示出来用户一看就知道自己的订单走到哪一步了。前台页面虽然看着没有后台复杂但有几个交互点需要打磨。宠物展示页面用卡片列表布局每张卡片展示宠物图片、名称、价格、疫苗状态标签。点击卡片进入详情页详情页除了信息展示外还有收藏按钮、加入购物车按钮、立即购买按钮。立即购买会直接调创建订单接口确认后跳转到支付页面。支付页是模拟的因为接入真实微信支付需要商户号我用了 mock 接口模拟支付流程页面样式按照真实的微信支付扫码界面设计。Axios 封装是前端工程化的基础工作。我在src/api/request.js里创建了一个 Axios 实例设置baseURL为/api通过 Vue CLI 的代理把请求转发到后端 3000 端口。请求拦截器从 localStorage 取出 token放到请求头Authorization字段里。响应拦截器统一处理后端返回的code如果是 200 就直接返回data如果是 401 就清除登录信息跳回登录页如果是其他错误就Message.error提示用户。这个封装做一次整个项目所有请求都受益不用每个页面重复写错误处理逻辑。// 前端 axios 封装核心代码src/api/request.js import axios from axios import { Message } from element-ui import router from /router const service axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截携带 token service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }, error Promise.reject(error)) // 响应拦截统一处理状态码 service.interceptors.response.use(response { const res response.data if (res.code ! 200) { if (res.code 401) { localStorage.removeItem(token) router.push(/login) } Message.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res.data }, error Promise.reject(error)) export default service5. 开发中踩过的坑与修复实录这些问题是搜索热度最高的实战难题项目开发过程中遇到的坑非常多结合搜索热度来看下面几个问题出现的频率极高几乎每个 Vue Node.js 开发者都会碰到。第一个坑是 Windows 环境下 npm 命令无法执行。很多同学在安装完 Node.js 后打开 PowerShell 执行npm -v结果报错“无法加载文件 D:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本”。这个问题的根源是 PowerShell 的执行策略默认是 Restricted禁止运行任何 .ps1 脚本。npm 本身是个命令行工具但在 PowerShell 下调用时会走 npm.ps1 这个脚本文件于是被拦截了。解决办法有两种我推荐最简单的一种使用 CMD 命令行工具代替 PowerShellCMD 不会执行 .ps1 脚本所以不会报错。如果你坚持用 PowerShell可以执行Set-ExecutionPolicy RemoteSigned然后输入 Y 确认将执行策略改为允许本地脚本运行。需要注意这个修改需要管理员权限否则也会报错。这个坑看似小但对新手来说真的能卡一下午。第二个坑是 Element UI 表格固定列变透明。这个问题非常诡异现象是设置了fixedright的表格操作列在滚动时偶尔会出现背景透明、文字和按钮叠在下面的数据上完全没法看。搜索“elementui 报表的固定列有时候会变透明”的同学应该都遇到过。这个问题的本质是 Element UI 的固定列原理它会克隆一份表格放在右侧用绝对定位覆盖在原表格上方当滚动时两个层都要刷新有时浏览器渲染出现 bug导致固定列的层级或背景色丢失。我的解决办法是给固定列单独加背景色和层级在全局样式中覆盖.el-table__fixed-right { background-color: #fff; } .el-table__fixed-right::before { background-color: #fff; } .el-table th.el-table__cell { background-color: #fff; }这套样式修复了固定列透明的问题核心思路是给固定列的容器和表头强制加上不透明的背景色阻止下方内容透出来。如果是深色主题的后台把#fff换成对应的背景色即可。第三个坑是 Node.js 环境变量配置。很多同学明明安装了 Node.js在 CMD 里却提示不是内部或外部命令。这是因为安装时没有勾选“Add to PATH”选项或者安装路径中包含中文/空格导致系统无法正确解析。解决方法有两种重新安装时勾选加入 PATH或者手动配置环境变量在系统变量的 Path 中添加 Node.js 的安装目录比如D:\dev\nodejs\。配置完后重新打开命令行窗口执行node -v验证。第四个坑是前后端联调时的跨域问题。前端跑在 8080后端跑在 3000前端直接请求后端接口会报 CORS 错误。我在早期开发时图省事直接安装cors插件并开启全部跨域const cors require(cors) app.use(cors())但这种方式在生产环境非常危险等于允许任何源访问你的接口容易引来恶意请求。后来我改成了白名单模式const cors require(cors) const whitelist [http://localhost:8080, http://127.0.0.1:8080, https://admin.xxx.com] app.use(cors({ origin: function (origin, callback) { if (!origin || whitelist.indexOf(origin) ! -1) { callback(null, true) } else { callback(new Error(Not allowed by CORS)) } }, credentials: true }))开发环境实际上也可以完全不用 CORS 插件通过 Vue CLI 的 devServer 代理转发即可。我在vue.config.js中配置了代理module.exports { devServer: { proxy: { /api: { target: http://localhost:3000, changeOrigin: true, pathRewrite: { ^/api: } } } } }这样前端请求/api/pet/list会被代理到http://localhost:3000/pet/list浏览器看到的是同源请求不会触发跨域限制。同时后端接口统一挂在一个子路由下代理配置也清晰。生产环境则通过 Nginx 反向代理把/api路径转发到 Node.js 服务效果一样。第五个坑和流媒体播放有关。项目中有一个宠物视频展示模块管理员可以上传宠物的视频介绍前台页面在线播放。我最初用的video标签直接播放 mp4 格式但视频文件一多、码率一高加载就变得很慢。后来了解到 HLS 流媒体协议可以按需加载切片配合hls.js或video.js在浏览器端播放.m3u8直播流。不过宠物系统里的视频是点播场景不是直播所以我最终没有引入完整的 HLS 方案而是对视频做了压缩转码上传限制单文件大小不超过 50MB。这里想提醒大家的是在 Vue 中播放 m3u8 流时要用hls.js库并通过video标签的src指向.m3u8地址且必须确保后端接口允许跨域或走代理否则浏览器会被 CORS 拦截导致黑屏。这个坑在移动端小程序的 web-view 里尤其明显我调试了很久才发现是服务端没加 CORS 响应头。6. 安全设计、部署上线和后续优化方向一个交易系统最怕什么最怕用户数据泄露和交易数据被篡改。所以安全设计绝对不能马虎。我在这个项目里做了三层安全防护。第一层是身份认证。用户注册时密码用 bcrypt 加密存储登录成功后后端签发 JWT Token。Token 的有效期设置为 24 小时保存在前端 localStorage 中。每次请求时携带 Token后端通过中间件解析 Token 验证身份。这里需要特别说一下JWT 的密钥一定不能硬编码在代码里我是从环境变量中读取的。而且 Token 只能用来验证身份绝不能把敏感信息直接明文放在 Token 的 payload 里。第二层是接口权限控制。管理员接口和用户接口要区分开我在后端中间件里做了角色校验只有 role 为 1 的管理员才能访问/api/admin/下的接口普通用户访问直接返回 403。前端虽然隐藏了管理后台的入口但不能只依赖前端隐藏后端必须做真正的权限校验否则别人直接请求接口地址就能绕过。第三层是数据安全。数据库操作全部使用参数化查询或预处理语句防止 SQL 注入。文件上传做了类型白名单和大小校验只允许 jpg、png、mp4 等指定格式文件名用时间戳加随机数重命名避免路径穿越攻击。部署上线我用的是 Nginx 加 PM2 的组合。前端代码执行npm run build打包生成 dist 文件夹把 dist 文件夹放到服务器上Nginx 配置 root 指向它。后端代码上传到服务器后用 PM2 启动app.jsPM2 会作为守护进程管理 Node.js 应用进程崩溃了会自动重启。Nginx 配置了/api路径的反向代理转发到本地 3000 端口server { listen 80; server_name your-domain.com; root /var/www/aitaidist; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:3000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }关于后续的优化方向我梳理了三个优先级比较高的点一是引入 Redis 缓存宠物列表和分类信息属于读多写少的冷数据用 Redis 缓存可以大幅减少数据库压力二是把支付功能对接微信支付或支付宝沙箱环境替换掉现在的模拟支付三是增加消息推送订单状态变化时通过 WebSocket 或短信通知用户提升交易闭环的体验。这个项目上线后实际运行了几个月整体稳定性不错真正帮我朋友把线下零散的单子收拢到了线上统一管理。做这类管理系统我的个人体会是技术栈不是越新越好业务梳理和数据结构设计才是决定成败的关键。现在回头看开发周期里超过一半的时间其实花在业务分析和踩坑修复上真正写业务代码的时间并不长。如果你正准备做一个类似的管理系统建议先把业务流程图和数据表设计画好再开始写第一行代码这条路会顺很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价