资讯动态

基于Node.js与Vue3的中医药材管理系统开发实战与答辩指南

发布时间:2026/10/15 14:17:36 来源:尧图企业网站定制
又到了一年一度的大作业季节前端方向的项目选题里“管理系统”永远是最稳也最卷的方向。但同样是管理系统有的交上去平平无奇有的就能让答辩老师多问两句、多给几分。区别在哪里很大程度在于选题的业务深度和完成度。今天聊一个我近期帮一位学弟梳理过的项目——基于Node.js Vue 3的中医药材管理系统附带完整开发文档。这个标题看起来平平无奇但它其实踩准了好几个加分点业务领域有辨识度、技术栈是目前前端就业市场的主流组合、开发文档齐全还能体现工程化意识。如果你也在准备类似的前端大作业、毕设或者个人项目练手这篇文章值得看完我会把从需求拆解、技术选型、核心功能实现到开发文档撰写和答辩避坑的完整思路都拆开讲透。1. 项目拆解与需求分析1.1 大作业到底在考查什么先想清楚一件事大作业不是让你造一个商业级产品而是在有限时间通常一到两个月内证明你掌握了从零搭建一个前后端分离应用的能力。老师眼里的大作业有三个核心考察点一是功能完整度登录、增删改查、检索、权限这些基本盘不能少二是技术栈选型合理能解释清楚为什么用这个不用那个三是交付物规范除了代码还要有能讲清楚设计思路的开发文档。很多人在这第一步就栽了——选了个满大街都是的“图书管理系统”“学生信息管理系统”功能做得再全答辩时老师也审美疲劳。中医药材管理系统就不一样它天然带业务属性药材有分类、有性味归经、有功效主治、有库存和价格这些领域概念能撑起比普通CRUD更丰富的功能设计也方便在答辩时讲出业务逻辑而不是干巴巴地说是“增删改查”。1.2 功能边界怎么划大作业最忌讳的就是一上来想做一个“全功能的中医药电商平台”需求越滚越大最后代码没写完、文档全是PPT截图。一个靠谱的做法是先把核心业务流程画出来然后按优先级砍需求。我当时帮这位学弟约定的功能范围是这样的模块功能点优先级用户模块注册、登录、Token鉴权、退出登录必须做药材管理药材信息的增删改查、分页列表必须做分类管理药材按功效分类、分类的维护必须做检索筛选按名称关键字、性味、归经、价格区间筛选建议做库存管理入库、出库、库存数量联动加分项数据看板统计各分类药材数量、库存总值加分项操作日志记录关键操作行为加分项必须做的三项决定你能不能及格建议做和加分项决定你能不能拿高分。实践下来以药材管理和检索为核心把增删改查做好、做细文档再写得规范一点大作业拿个好成绩基本就稳了。1.3 用户场景与角色设计既然带“管理系统”角色设计不能省。我的建议是至少分两类角色管理员和普通用户。管理员能维护药材数据、管理库存、查看操作日志普通用户能浏览、检索、查看详情但不能修改数据。这样设计的好处有两个。第一功能天然多了一层权限控制的实现这是答辩时一个很实在的亮点第二表格设计时多一张用户表、多一个角色字段前后端写起来复杂度并没有增加多少但功能叙事完整度直接上一个台阶。大作业做的是“系统”不是“页面集合”这一点想清楚后面所有设计都会顺很多。2. 技术选型为什么是Vue3 Node.js2.1 前端为什么押注Vue3现在高校前端课程里Vue还是绝对的主流但很多课程教的还是Vue2的选项式API。这个项目选Vue3一方面是想跟上行业现状另一方面是Vue3的组合式API确实是更适合真实项目组织代码的方式——它把同一业务逻辑的代码聚在一起不用再像选项式那样把data、methods、computed拆到天南海北阅读和维护都舒服得多。实际开发中我用的是script setup语法糖配合组合式API的ref、reactive、computed、watch这些核心API页面代码比Vue2时期少了不少样板。比如药材列表页同一个搜索条件既要在模板里绑定输入框又要触发接口请求用watch监听搜索条件对象配合防抖函数几行代码就搞定了。另外Vue3的生态现在已经非常成熟Element Plus组件库的表格、表单、弹窗、分页全都有现成组件搭出规范的后台管理界面速度非常快。对大作业来说这能帮你把时间省出来去做真正重要的业务逻辑和文档而不是纠结于下拉框的样式。2.2 后端为什么用Node.js后端选Node.js最大的理由不是性能天花板有多高而是“语言统一”。前后端都是JavaScript/TypeScript对做前端大作业的同学来说心智负担直接砍半。你不需要先学一门新的后端语言比如Java的Spring那一套才能把后端跑起来中间层的接口定义、数据校验、路由写法思路跟前端写JS的习惯是一致的这在大作业的项目周期里是一个很实际的效率优势。Node.js生态里的Web框架我建议优先考虑Express。它足够轻量中间件机制清晰路由写法直观教程和现成案例也最多。团队里如果已经有同学在用Koa那Koa也可以但自己一个人写大作业的话Express是稳妥的选择——文档多、坑少、遇到问题搜一下就有现成答案。2.3 前端配套工具箱前端单靠Vue3本身还不够还需要几样标配Vue Router做SPA的路由管理登录页、后台布局页、药材管理页都要靠它组织起来路由守卫用来做未登录跳转这是权限功能的核心链路。PiniaVue3官方推荐的状态管理库比Vuex的TypeScript友好度更好写法也简单。项目中我拿它存用户登录信息、Token、侧边栏折叠状态这类全局数据。AxiosHTTP请求库统一封装请求实例设置baseURL请求拦截器自动塞Token响应拦截器统一处理业务错误码和网络异常。Element Plus上面说过的组件库表格、表单、消息提示、对话框都能直接拿来用做后台管理界面的效率神器。这一套组合下来前后端技术栈总共就四个核心依赖加一个框架面试或答辩被问到“你选型的理由”时每个选择都能给出清晰的业务和技术考量而不是“大家都用我也用”。3. 核心功能设计与实操实现3.1 登录注册与Token鉴权登录注册是管理系统的门面也是答辩时最容易演示的功能之一。实现思路是前端把账号密码用POST请求发给后端/api/auth/login接口后端校验成功后返回一个token我用的是jsonwebtoken签发的JWT前端把Token存到localStorage或Pinia里后续每次请求通过Axios请求拦截器自动带上。后端校验Token的中间件是整个鉴权逻辑的核心逻辑并不复杂前端把Token放在Authorization: Bearer token请求头里中间件取出Token用密钥校验签名和过期时间解析出用户ID和角色挂到req.user上后面的接口就能按角色做授权判断。// 后端鉴权中间件简要示例 const jwt require(jsonwebtoken); function authRequired(req, res, next) { const header req.headers.authorization || ; const token header.startsWith(Bearer ) ? header.slice(7) : null; if (!token) { return res.status(401).json({ code: 401, message: 请先登录 }); } try { const payload jwt.verify(token, process.env.JWT_SECRET || your-secret); req.user payload; next(); } catch (err) { return res.status(401).json({ code: 401, message: 登录已过期请重新登录 }); } }注意这里写的JWT_SECRET在生产环境下必须放在环境变量或者配置文件里不要硬编码到源码中。大作业虽然不需要上生产但在文档里最好提一句明文存储密钥的风险评审老师很吃这一套。前端这边用路由守卫做页面访问控制创建Vue Router实例时在beforeEach全局守卫里判断目标路由是否需要登录需要的话再检查Pinia/localStorage里有没有Token没有就强制跳转到登录页。注意这里不要只看有没有Token最好还要调用后端接口校验Token是否有效否则用户手动改一下localStorage就能伪造登录态这是一个常见的安全漏洞踩过一次坑就记住了。3.2 中药材信息管理与梯度表单校验中药材管理是整个系统的核心业务模块本质是CRUD但形式上可以做得比普通学生管理更有辨识度。药材表的字段设计很关键我是按这样来的字段名含义示例值name药材名称黄芪alias别名黄耆、绵芪category_id分类外键补虚药nature性味甘、微温meridian归经脾、肺经efficacy功效主治补气升阳固表止汗price价格元/斤32.50stock库存斤120image图片URL/uploads/huangqi.jpgdescription详细介绍长文本增删改查的实现用Element Plus的el-tableel-dialogel-form可以说是标准操作。表格展示药材列表点“新增”弹一个空表单对话框点“编辑”弹一个回填了当前行数据的表单对话框底部提交按钮触发新增或更新接口。这里有一个非常值得做的细节表单校验。Element Plus的el-form支持配置rules做表单验证比如名称必填、价格必须是正数、库存不能为负。我还做过一版梯度校验——新增时所有字段必填编辑时只校验关键字段这个设计在答辩时讲出来老师会觉得你考虑到了真实业务场景下的用户操作体验而不是机械地做表单。3.3 多条件检索与筛选单张数据表的CRUD容易被做得平平无奇多条件检索是拉开差距的地方。中医药材天然有“性味”“归经”“功效分类”这些业务维度筛选条件一多就比单纯按名称搜索有看头了。我的实现方式是前端搜索区放三个控件关键字输入框匹配名称或别名、分类下拉框来自分类表、性味多选框甘、苦、辛、咸、酸。三个条件之间用AND逻辑组合点击“查询”按钮后一次性把条件对象发给后端后端在SQL查询里动态拼WHERE条件。// 后端查询逻辑示例使用mysql2/promise async function listHerbs(query) { const { keyword, categoryId, nature, page 1, pageSize 10 } query; const conditions []; const params []; if (keyword) { conditions.push((name LIKE ? OR alias LIKE ?)); params.push(%${keyword}%, %${keyword}%); } if (categoryId) { conditions.push(category_id ?); params.push(categoryId); } if (nature) { conditions.push(nature LIKE ?); params.push(%${nature}%); } const where conditions.length ? WHERE ${conditions.join( AND )} : ; const sql SELECT * FROM herbs ${where} LIMIT ? OFFSET ?; const offset (page - 1) * pageSize; params.push(Number(pageSize), offset); const [rows] await db.query(sql, params); return rows; }注意老教材里很多是拿字符串拼接SQL最怕用户输入特殊字符导致报错或注入。上面示例用的是参数化查询SQL语句里用占位符用户输入只作为参数传入不会参与SQL结构拼装这是写后端接口最基本的底线要求。即使是大作业也建议用mysql2这类支持参数化的库把防注入的意识写进文档里。分页这里很建议用后端分页而不是一次查出全部数据再在前端用el-pagination切页。后端分页返回的总数据量和总页数可以让前端准确渲染分页器同时也模拟了真实接口的性能压力处理答辩时可以顺带解释一下LIMIT和OFFSET的作用很加分的点。3.4 数据看板与可视化如果前面的功能都很流畅时间还来得及强烈建议加一个数据看板页用echarts画两个图这是大作业“一眼高大上”的捷径。我当时设计的是一个饼图显示各分类的药材数量占比一个柱状图显示库存量Top10的药材。新增和编辑药材时接口调用成功后触发布局里更新统计数据的fetchStatistics()方法看板就会自动联动刷新这样演示时不用手动刷新页面就能看到数据变化效果非常直观。script setup import * as echarts from echarts; import { onMounted, ref, nextTick } from vue; const categoryPieRef ref(null); async function fetchStatistics() { const res await get(/api/statistics/category-count); // 用返回的 name/value 数组结构直接配给 echarts 的 series.data } onMounted(async () { await fetchStatistics(); nextTick(() { const chart echarts.init(categoryPieRef.value); chart.setOption({ /* 饼图配置 */ }); }); }); /script如果说CRUD展示的是“你会写业务代码”看板展示的就是“你会用数据服务业务决策”的意识。哪怕只是两张图整个项目的完成度和叙事层次都会不一样。注意echarts的初始化一定要在DOM渲染完成之后要么放在onMounted里要么配合nextTick否则图表容器还没有尺寸画出来会是空白或错乱的。4. 数据库设计与接口规范4.1 数据表结构设计数据库是整个系统所有功能的底座表设计不合理后面写接口处处别扭。这个项目最少要有三张表user、category、herb。如果做了库存变动记录再加一张stock_log。设计表的时候有几点经验值得说不要用中文做表名字段名。这事在学校作业里很常见图省事直接建了个药材表但一旦前后端联调或者写SQL查询编码问题会让你各种抓狂。规范做法是统一用英文小写下划线命名比如herb_name、created_at。时间字段不能少。created_at和updated_at这两列在真实项目里是标配大作业里加上它一是方便排查数据问题二是文档里能解释清楚审计需求又是一个小亮点。价格和库存用DECIMAL和INT不要用FLOAT。DECIMAL(10,2)可以避免浮点误差药材单价出现0.10.2不等于0.3这种精度问题在库存统计和价格计算时会让人很崩溃。外键关系要理解但别滥用。herb.category_id关联category.id这个外键关系可以做但实际查询时我基本都是用JOIN查出来再组装而不是靠数据库外键约束去强撑。大作业阶段理解外键含义并把关联关系体现在字段设计上比在数据库里加一堆外键约束更实用。4.2 RESTful接口设计规范前后端分离的项目接口设计统一规范很重要。直接贴一份我常用的接口清单方法路径功能鉴权POST/api/auth/register注册否POST/api/auth/login登录否GET/api/herbs药材列表分页筛选是GET/api/herbs/:id药材详情是POST/api/herbs新增药材管理员PUT/api/herbs/:id更新药材管理员DELETE/api/herbs/:id删除药材管理员GET/api/categories分类列表是POST/api/stock/in入库管理员POST/api/stock/out出库管理员GET/api/statistics/category-count分类数量统计是统一返回格式也很重要我当时定义的是{ code: 0, data: ..., message: success }code非0表示出错。前端Axios响应拦截器里统一判断code非0就弹出ElMessage.error这样每个页面就不用重复写错误处理逻辑了。在文档里把这个约定写清楚整个项目会显得组织度很高。4.3 前后端联调与常见协作细节大作业联调阶段最常见的问题是端口冲突、跨域和接口路径对不上。本地开发时前端Vite默认跑在5173端口后端Node跑在3000前端请求3000端口必然遇到跨域问题。解决办法很简单在Vite配置文件里配置代理把/api前缀的请求代理到后端地址同时后端用cors中间件放开跨域限制两边各配一次开发时就不会再被浏览器拦截。// vite.config.js 片段 export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:3000, changeOrigin: true } } } });联调时我发现一个特别容易掉坑的点前端返回的时间字段是带T的ISO字符串表格里直接展示很难看例如“2025-06-01T08:30:00.000Z”。解决方式是在前端封装一个格式化函数或者让后端在SELECT时就直接用DATE_FORMAT格式化好再返回。两种我都试过更推荐后端格式化——前端少写代码数据到哪都统一。5. 开发文档的撰写方法与答辩准备5.1 开发文档写什么、怎么写标题里专门带了“开发文档”四个字这其实是个很对的选择。很多大作业代码写得还行但文档只是把需求说明书复制粘贴一遍那就太可惜了。一份好的开发文档应该包括下面五块项目概述用一两段话说清楚系统是做什么的、面向谁、解决什么问题不要上来就贴截图。需求分析包含角色分析、功能模块列表、每个模块的简单说明。可以画用例图答辩PPT里画文档里用表格逐条列清楚就行核心是让人看完知道系统有哪些功能、每个功能怎么走。技术栈说明逐个列技术点并说明为什么选它。这一块前面已经介绍过文档里也按这个思路写就行——要的是“选型理由”不是技术名词堆砌。系统设计包括架构图、数据库表结构建表SQL、接口文档路径、参数、返回示例。接口文档是这块的重头戏每个接口把请求参数和响应例子写清楚方便后面联调和答辩时自己快速回忆。部署与运行说明只写清楚如何在本地把项目跑起来——npm install装依赖、初始化数据库、改.env配置、启动后端、启动前端。大作业的上机演示环节老师最依赖这部分必须写得傻瓜级直白。文档篇幅不用追求越长越好但目录结构要清晰标题层级要规范代码块要格式正确。给文档加一个目录页会在观感上非常加分。5.2 答辩现场怎么讲这个项目答辩时老师问得最多的是“项目哪里最难”“你自己写了多少”“某某功能怎么实现的”。话术上不要讲“这个系统实现了完整的增删改查”而要讲“我实现了多条件的组合筛选并且用参数化查询解决了拼接SQL的注入风险”。同样是说功能后者听起来就像做过真实项目。演示顺序也有讲究先进登录页登录再带老师看一眼数据看板然后加一条药材数据回看板确认数据变化再去列表页用筛选条件找到这条数据最后进库存模块做一个入库操作。这一套流程走下来所有核心功能都能自然演示到节奏感很强不容易被老师打断问偏题。6. 实操避坑指南我踩过的那些坑6.1 依赖版本不一致Vue3的项目出现过最莫名其妙的问题是“装好了依赖启动直接报错”这种大概率是依赖版本冲突。比如Element Plus和Vue的版本号大版本没对齐。我的经验是package.json里锁版本号不要装最新版时就顺手用了^前缀安装了大版本未知的版本启动报错先看npm install时候有没有警告信息有警告就是版本不匹配的信号。建议使用固定的版本组合{ vue: ^3.3.0, vue-router: ^4.2.0, pinia: ^2.1.0, element-plus: ^2.4.0, axios: ^1.5.0 }6.2 mysql2模块的异步写法用mysql2连着数据库时报错很多同学发现自己明明写过await db.query()却报了“Cannot read properties of undefined”。这个问题的根源是mysql2默认是回调式API要用Promise得先调用.promise()方法或者直接引入mysql2/promise。这个坑很隐蔽文档里搜的时候关键词也不太好搜我在这里记录下来希望能给后来人省点时间。// 正确写法 const mysql require(mysql2/promise); const db await mysql.createConnection({ host: localhost, user: root, password: password, database: chinese_herb_db });6.3 图片上传的静态资源访问药材图片上传功能看起来简单实际有两个小坑。一是上传接口要用multer中间件处理multipart/form-data二是上传保存的图片要先确认目录存在否则会报ENOENT错误。还有上传完图片后前端要能访问到后端要把上传目录做成静态资源公开否则图片路径返回了但浏览器打不开。下面这段代码是后端静态资源的配置方式const express require(express); const path require(path); const app express(); app.use(/uploads, express.static(path.join(__dirname, uploads)));6.4 页面刷新后Token失效的问题如果用Pinia存Token而页面一刷新Pinia状态就丢了用户就得重新登录这在大作业演示时很容易出糗。解决办法是持久化Token存localStoragePinia里初始化时先从localStorage读取。当然localStorage只适合存JWT这类临时访问凭证如果存用户密码或真实个人信息就没有任何安全性可言了。我习惯用一个简化的方式处理刷新后先判断localStorage里有Token同时拿着Token去请求一次/api/auth/profile接口成功就把用户信息塞回Pinia失败就清掉localStorage并跳登录页。6.5 别在README里放“开发中”最后一个建议纯属个人经验项目文档的README或开发文档封面不要写“开发中”“持续更新中”这种话。大作业交上去就是最终状态要展现的是“已完成、能运行、有文档”的完整面貌。如果还有未完成的功能宁可把入口隐藏掉也比明晃晃地写着“TODO”更稳妥。7. 写在最后的体会这套系统从需求拆解到前后端实现、再到开发文档完善我陪学弟全程走了一遍。我最深的感受是一个大作业项目的评分上限往往不由“写代码的技术难度”决定而由“你做没做完整”决定。技术栈选得再酷如果功能缺了一块、文档里没有部署说明那前面的优势都会被扣分反过来一个不算新颖的项目只要功能完整闭环、每个选择都有理由、文档能顺利带着另一个人跑起来成绩都不会差。具体到这个项目上把业务领域的细节做进功能里——分类、性味、归经、库存流转——就是天然的区分度。哪怕代码量并没有那么多答辩时打开看板那两张图、演示一把多条件筛选老师的注意力就会从“你是不是抄的”转移到“这个功能怎么实现的”上你已经赢了大半。如果时间充裕这个项目还有两个完全可以继续扩展的方向一是把系统迁移成电子处方与问诊记录管理让中医馆的日常业务链条更完整二是接入图表报表模块做得更贴近经营决策。不过这些都是后话先把当前版本从头到尾打磨完整才是交给老师和自己的最好答卷。

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

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

免费获取报价 →
↑