☰
Node.js + Vue构建养老院膳食与护工评价管理系统实战
2026/10/7 12:01:28 网站建设 项目流程

做养老院管理系统这个项目之前,我其实在“到底要不要用 Node.js + Vue 来做”这件事上纠结了挺久。毕竟市面上随手能搜到的毕设教程、开源模板里,Spring Boot + Vue 的组合占了绝大多数,Node.js + Vue 的完整案例反而少一些。真正让我下定决心用这套技术栈的,是养老院膳食和护工评价管理场景里那些“轻业务、重交互、快迭代”的需求特点——一个面向养老院的信息化管理系统,核心是把膳食计划、护工考核、老人反馈串成一条完整的数据链,Node.js 后端开发的高效和 Vue 组件化开发的体验,在这个体量下恰好是匹配的。

这篇文章不打算写成那种“系统功能列表 + 界面截图”式的流水账。我想把从需求拆解、技术选型、数据库设计、前后端实现,到环境配置和实际部署过程中那些真正花过时间、踩过坑的东西讲清楚。如果你正在做类似的管理系统项目,无论是毕设还是公司内部工具,这篇应该能帮你省下不少试错的成本。

1. 这个系统到底在解决什么问题:养老院日常管理的三块硬骨头

在做任何技术设计之前,得先弄明白业务老师真正头疼的是什么。我前期去一家民办养老院蹲了半天,跟负责后勤的院长助理聊完才意识到,膳食、护工评价这类管理,核心不是“记录”而是“协调”。

1.1 膳食管理的核心痛点是“计划-执行-反馈”闭环断裂

养老院的膳食和普通食堂最大的区别在于:就餐对象是特殊人群。糖尿病老人要吃低糖餐、高血压老人要低盐、牙口不好的要软食,还有一部分卧床老人需要流食。每天几百号人的饮食需求,如果只靠食堂阿姨的记忆和纸质菜谱,必然会出现荤素搭配失衡、营养餐送错、家属来问“今天老人吃了什么”答不上来的情况。

一个合格的膳食管理系统,需要把“每周菜谱制定 → 按老人忌口生成送餐名单 → 用餐反馈记录 → 下周菜谱调整”这条链路打通。这个系统里,膳食模块不是简单的菜品增删改查,而是要充分考虑计划的提前量。比如食堂管理员每周五得排好下周的菜谱,系统要能自动校验“糖尿病老人今天吃的主食是否含糖过高”,或者提醒“这周鱼虾类蛋白质已经连续两天没安排了”。

1.2 护工评价的核心痛点是“评价维度碎片化”

护工评价这件事,传统做法是季度末发纸质打分表,护理部主任凭印象打分,家属意见最多挂在院长办公室的意见簿上。这里面的问题很明显——评价不透明、指标不统一、结果没有沉淀。有些养老院试过用 Excel 做评分汇总,但收集上来的打分表维度五花八门,有人评“态度”,有人评“卫生”,最后统计的人只能凭感觉加权。

所以护工评价模块一定要做到三件事:评价指标可配置、评价来源可区分、评价结果可追溯。指标可配置的意思是,管理员可以自己定义“生活照料、卫生清洁、服务态度、应急处理”这些维度下面具体包含哪些细项、每个细项占多少权重。来源可区分是指,护工组长、护士长、老人家属、老人本人(或者通过家属代评)都可以发起评价,不同角色的评分权重在汇总时不一样。结果可追溯是指,任何人看到一名护工的 90 分总分时,能一层层点下去,看到这 90 分是由哪几项加出来的。

1.3 “中心”这个定位:把分散的信息收拢到同一张管理视图上

标题里“评价中心”这个说法,在我看来不是指一个页面,而是整套系统的设计基调。它意味着管理员打开系统首页,就能看到一周内每个护工的评分变化曲线、食堂各窗口的菜品满意度、待处理的新增评价件数。它不是给护工自己看绩效的,而是给管理层做决策用的驾驶舱。理解了这点,后面在数据库冗余字段设计和前端首页可视化组件的取舍上,都会有一个明确的方向。

所有角色在这个系统里的职责边界也要先理清。我最终划分了四种角色:系统管理员(管账号和基础数据)、食堂管理员(管菜品和排餐)、护士长/组长(管护工评分审核)、老人家属(提交评价与浏览膳食记录)。角色权限的差异,直接决定了后端接口的鉴权粒度。

2. 技术选型复盘:为什么是 Node.js + Vue 而不是其他搭配

说实话,这个项目如果让我用 Spring Boot 写,也不会有什么技术障碍。但为什么最终选了 Node.js + Express + Vue 这套组合,是基于开发效率、部署成本、学习曲线三个维度的综合判断。

2.1 前后端分离的整体架构和 Node.js 的定位

整个系统采用经典的前后端分离架构:Vue 负责页面渲染和用户交互,Node.js 负责提供 RESTful API 和业务逻辑处理,MySQL 做数据持久化。这个架构的好处是前后端可以并行开发——我先把接口文档用 Apifox 定义好,前端同事(或者自己写前端时)照着文档 mock 数据,两边都不互相卡脖子。

Node.js 在这个项目里的角色很明确:轻量级 API 网关 + 业务逻辑执行体。Express 4.x 作为 Web 框架,配上 Sequelize 做 ORM,在单机部署、日均几百次请求的系统里性能完全够用。很多人担心 Node.js 处理高并发不行,但这个系统的真实并发量可能还不如一个小型博客——养老院一个管理后台,峰值也就几十个人同时操作,Node.js 的非阻塞 I/O 模型反而在文件上传、数据导出这类任务上有天然优势。

2.2 和 Spring Boot 的取舍逻辑

我知道很多人会劝你“还是用 Spring Boot 吧,资料多,答辩好过”。这个观点有道理,但要注意一个前提:资料多对应的是“前人踩过的坑都写在博客里了”,而 Node.js 的技术栈更贴近前端生态,如果你本身 Vue 是主力,再花时间学 Java 那套注解、依赖注入,学习成本会翻倍。我选 Node.js 的一个重要理由就是:可以完全用 JavaScript 一套语言打通前后端,类型定义、数据格式不需要在脑子来回切换语言。

另外一个现实因素是部署成本。毕业设计或者小型项目的最终交付,通常是一台 2核4G 的云服务器。Node.js 应用打包后可能就 50MB,启动只需要node app.js,配合 PM2 进程守护,内存占用稳定在 300MB 左右。而 Spring Boot 应用一个 fat jar 动辄 100MB 起步,JVM 默认配置下随便跑起来就要占用 1G 内存。对于养老院这种舍不得买高配服务器的甲方,Node.js 的轻量优势非常实在。

2.3 Vue 2 还是 Vue 3、要不要上组件库

这个项目用的是 Vue 3 + Composition API + Element Plus。选 Vue 3 不是因为追新,而是因为<script setup>语法让组件的逻辑组织能力明显改善。比如护工评价页有一个“评价项动态增减”的交互,用 Options API 写要维护 data、methods、computed 三块内容来回跳,用<script setup>可以在一个作用域里直接把所有相关逻辑放在一起,开发和维护都省心。

组件库直接选了 Element Plus,因为这种管理系统大量依赖表格、表单、弹窗、日期选择器,自己从零造轮子纯属浪费时间。这套组件库在栅格布局、表单校验、表格分页上都有成熟方案,能让前端开发周期缩短至少 40%。版本选择上建议锁一个稳定版本,比如 element-plus 2.4.x,不要一上来就 latest,避免刚装完发现和某个插件的依赖冲突。

2.4 数据库和 ORM 的选型

数据库选了 MySQL 8.0,这是最不容易出错的选择。ORM 层我用的是 Sequelize 6。选它的原因是:模型定义方式直观、支持 migration 进行表结构版本管理、和 Express 配合成熟。如果你不想写 SQL 语句操作数据库表,Sequelize 的 Model 定义和查询方法能在很大程度上提升开发速度。当然,代价是复杂查询的 SQL 优化能力会被 ORM 遮蔽,所以在这个项目里,凡是涉及多表统计的查询,我仍然会写原生 SQL 而不是硬用 ORM 拼。

3. 数据库设计与核心表结构:从“菜品-排餐-评价”这条主线说起

数据库设计是这种管理系统项目中最见功力的一环。表建得好不好,直接决定后面写接口是顺滑还是痛苦。我的设计原则是:核心业务表不要偷懒省字段,关联关系尽量清晰,统计需要的冗余字段适度增加。

3.1 用户角色表和权限控制的前置设计

用户表(users)只保留最基本的字段:id、username、password(md5 + salt 加密存储)、real_name、phone、role(admin/kitchen/nurse/family)、elder_id(关联被监护老人)、status、create_time。

这里有个细节要注意:护工和家属这两个角色,在业务上并不是直接对应用户表那么简单。一个护工要关联到她负责的床位区域,一个家属账号要关联到某位老人,才能在提交评价时自动带出老人信息和所在区域。所以我在用户表之外单独设置了caregiver_profile表存放护工的工号、负责区域、入职时间、当前状态,在family_binding表存放家属和老人的绑定关系。这样用户表只做统一登录鉴权,各角色的扩展属性分表存放,避免一张表字段过多、逻辑混乱。

3.2 膳食模块的核心表链路

膳食管理我拆了四张表,形成一个清晰的从菜谱到用餐记录的链路:

  • dishes(菜品表):id、name、category(早/中/晚/加餐)、ingredients、calories、tag(低盐/低糖/软食/流食/普通)、image_url、status、create_time。标签字段特别重要,后续自动排餐校验能否匹配老人忌口,全靠这个标签。
  • weekly_menus(周菜谱表):id、week_start_date、dish_id、day_of_week(1-7)、meal_type(breakfast/lunch/dinner)、quantity、create_time。这张表存储的是“本周某天某餐吃哪道菜”的排期。
  • elder_meal_plans(老人用餐计划表):id、elder_id、meal_type、dietary_restriction、breakfast_rule/lunch_rule/dinner_rule(关联 dish_id 或允许替换规则)、status。这张表表达的是“特定老人每餐吃什么、有什么忌口”。
  • meal_records(用餐记录表):id、elder_id、dish_id、meal_type、record_date、is_completed、feedback_score、feedback_content、create_time。老人用餐后记录实际吃了什么、满意度如何。

这套表的逻辑关系稍微绕一点,但确实能支撑完整的业务闭环:食堂管理员在本周初通过操作界面复制上周菜谱 → 系统自动检查有多少老人当前餐次对应的菜品打上了“低盐”标签,却匹配了限制盐分的老人 → 管理员手动调整 → 生成老人们的周排餐计划 → 每天护工端在送餐时勾选“实际用餐状态”并代老人记录满意度 → 月末按菜品汇总满意度。

3.3 护工评价模块的表结构及评分权重设计

护工评价是另一条主线,我设计了三张表支撑灵活的指标配置:

  • evaluation_items(评价项表):id、item_name(比如“生活照料-协助洗澡”)、category(生活照料/卫生清洁/服务态度/应急处理)、parent_id(支持二级分组)、score_rule(扣分制或积分制)、max_score、weight、status、create_time。
  • evaluation_records(评价记录表):id、elder_id、caregiver_id、evaluator_id(评价人)、evaluator_role(nurse/family/manager)、period(2024-05)、total_score、suggestion、status(待核/已核)、audit_by、create_time。
  • evaluation_record_items(评价记录明细表):id、record_id、item_id、item_score、remark。这张表存放每条评价记录中每个细项的得分,方便追溯。

权重设计这个功能,我在前端是一个“评分模板配置器”,管理员拖动滑块调整各维度权重,系统自动校验权重总和必须等于 100%,后端在提交模板时也会再次校验。这样避免了“管理员不小心把权重调到 120%”导致的总分异常。总分的计算规则是:各维度得分 = 该维度细项得分之和 ÷ 该维度细项数量,总分 = 三个维度得分分别乘以权重再相加。护工月度绩效分则取当月所有有效评价记录的平均值。

3.4 一个容易被忽略的关联设计:区域与床位

养老院的管理通常以“楼层/区域”为维度,比如三楼住的是半自理区,四楼是失能区。护工负责的区域、老人居住的区域,在做统计时经常要按区域筛选。所以我在老人表(elders)中增加了building、floor、room_no、bed_no四个字段,并在护工评价统计接口中直接用这个字段做分组查询。这个设计虽然简单,但在后续按楼层导出月度护理质量报表时,能省掉大量关联查询的复杂度。

4. 后端接口实现细节:Node.js + Express 下的业务逻辑拆解

后端总的来说是一套标准的 Express 项目结构,代码组织上按“路由层 - 控制器层 - 服务层 - 模型层”分层。我不建议把所有逻辑都堆在路由回调函数里,那种写法在项目超过 20 个接口后维护起来极其痛苦。

4.1 目录结构设计和服务层拆分

我最终的目录结构大致是这样的:

server/ app.js # 入口文件,初始化 Express、中间件、路由 config/ db.js # 数据库连接配置 jwt.js # JWT 密钥和过期时间 models/ user.js dish.js weeklyMenu.js elderMealPlan.js mealRecord.js evaluationItem.js evaluationRecord.js evaluationRecordItem.js routes/ auth.js dish.js menu.js mealRecord.js evaluation.js statistics.js controllers/ authController.js dishController.js menuController.js mealRecordController.js evaluationController.js statisticsController.js services/ mealPlanService.js evaluationScoreService.js statisticsService.js middlewares/ auth.js role.js upload.js utils/ response.js crypto.js

服务层的意义在于:把复杂的业务逻辑从控制器中抽离出来。比如“生成下周菜谱”这个行为,控制器只负责接收参数和返回结果,真正的逻辑在mealPlanService.js里——它需要先查这周的菜品供给情况、老人忌口表、上个月的菜品满意度,然后综合判断后自动生成新菜谱。这样写的好处是,如果以后要改成“智能排菜单”,只需要改服务层函数,接口层完全不用动。

4.2 JWT 登录鉴权的实现思路

登录鉴权我用的是 JWT,登录成功返回一般会签发的信息包括 userId、username、role,有效期为 7 天。中间件里做两件事:验证 token 是否过期、解码后把用户信息挂在req.user上供后续接口使用。

const jwt = require('jsonwebtoken'); function authMiddleware(req, res, next) { const token = req.headers.authorization?.split(' ')[1]; if (!token) { return res.status(401).json({ code: 401, message: '未登录' }); } try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (err) { return res.status(401).json({ code: 401, message: '登录已过期,请重新登录' }); } }

角色控制中间件role.js稍微扩展了一下,接受一个角色数组参数。比如requireRole(['admin', 'nurse'])表示只有管理员和护士长角色能访问该接口。这个中间件在服务层之上确保:家属不能直接改菜品库,食堂管理员看不到护工评价的审核按钮,权限边界清清楚楚。

4.3 膳食排期的核心接口:自动生成和校验

自动生成一周菜谱这个接口是整个膳食模块比较核心的接口之一。设计逻辑其实不复杂,但需要细心处理边界条件。

// mealPlanService.js 中自动排餐的核心逻辑 async function generateWeeklyMenu(startDate) { const weekStart = moment(startDate).startOf('isoWeek'); const weekEnd = moment(weekStart).add(6, 'days'); // 1. 获取上个月菜品满意度 Top 10 const topDishes = await getTopDishesByFeedback(30); // 2. 获取当前所有老人的忌口集合 const elderlyRestrictions = await getDietaryRestrictions(); // 3. 初始化周菜单模板(默认参考上周) const lastWeekMenu = await getLastWeekMenu(weekStart.subtract(7, 'days')); // 4. 逐日逐餐生成:优先复用上周结构,用 Top 菜品替换连续重复超过2次的菜品 const weeklyPlan = []; for (let day = 1; day <= 7; day++) { const dayPlan = { day_of_week: day, meals: [] }; for (const mealType of ['breakfast', 'lunch', 'dinner']) { let dish = await pickDishForMeal(topDishes, lastWeekMenu, day, mealType); const conflictCount = await checkRestrictionConflict(dish, elderlyRestrictions); if (conflictCount > 0) { // 有忌口冲突,替换为同标签下满意度次高菜品 dish = await findAlternativeDish(dish.tags, elderlyRestrictions); } dayPlan.meals.push({ meal_type: mealType, dish_id: dish.id }); } weeklyPlan.push(dayPlan); } // 5. 批量插入 weekly_menus 表 await bulkCreateWeeklyMenu(weeklyPlan, weekStart); return weeklyPlan; }

这里有几个容易出问题的点。第一是“本周”的定义要统一,我用的是 ISO 周规则,周一到周日,避免和“自然周日到周六”混淆导致排期错位。第二是替换菜品时要保证替换后的菜品的营养标签能覆盖原菜品的约束,比如原菜品是“低盐”,找替代品时就限定tag LIKE '%低盐%'。第三是替换后的菜品尽量不要和同一周的另外两次重复,这也是我之前踩过的坑——系统自动排餐排出了连续三天晚餐都吃土豆烧肉,被食堂阿姨电话投诉。

4.4 护工评价的算分逻辑和防作弊设计

评价算分我在evaluationScoreService.js里实现,核心逻辑是取该护工当月所有“已审核”评价记录,按评价人身份加权计算。

async function computeMonthlyScore(caregiverId, period) { const records = await EvaluationRecord.findAll({ where: { caregiver_id: caregiverId, period, status: 'approved' }, include: [{ model: EvaluationRecordItem, as: 'items' }] }); if (records.length === 0) return 0; const weights = { nurse: 0.5, // 护士长评分权重 50% family: 0.3, // 家属评分权重 30% manager: 0.2 // 管理员评分权重 20% }; let total = 0; let weightSum = 0; for (const record of records) { const roleWeight = weights[record.evaluator_role] || 0.2; // 每条记录的总分已经存入 record.total_score total += record.total_score * roleWeight; weightSum += roleWeight; } return weightSum > 0 ? Math.round((total / weightSum) * 100) / 100 : 0; }

防作弊的设计体现在两点:一是评价记录从提交到生效,必须经过护士长“审核”,审核之前不算入月度绩效,避免家属心情不好时随手打零分直接拉低护工绩效;二是同一个家属对同一位护工在同一个周期内只允许提交一次评价,后端在校验时如果发现已有evaluator_id + caregiver_id + period重复,就直接返回“本期已评价过”的提示。

5. 前端页面与交互实现:Vue 组件化让复杂页面变简单

前端这边我用 Vue 3 + Vue Router 4 + Pinia + Element Plus,按角色区分路由和菜单。整体页面数量不多,但每个页面的交互密度不小,尤其是膳食排餐和评价配置这两个页面。

5.1 路由设计和权限控制的前端落地

路由拆成了静态路由和动态路由两部分。静态路由只有登录页和 404 页,其余全部走动态路由——用户登录后,后端根据角色返回可访问的路由列表,前端拿到后通过router.addRoute动态挂载。

// permission.js 路由守卫核心逻辑 router.beforeEach(async (to, from, next) => { const userStore = useUserStore(); if (userStore.token) { if (to.path === '/login') { next('/dashboard'); } else { if (!userStore.hasLoadedRoutes) { const accessRoutes = await userStore.fetchAndGenerateRoutes(); accessRoutes.forEach(route => router.addRoute(route)); next({ ...to, replace: true }); } else { next(); } } } else { if (to.path === '/login') { next(); } else { next('/login'); } } });

动态路由带来的一个坑是刷新页面后路由会丢失,因为 Pinia 里的状态是内存态,刷新就清空了。解决方案是在fetchAndGenerateRoutes请求时把路由数据顺便缓存一份到 localStorage,刷新时先读缓存再请求后端确认,避免“刷新即白屏”的问题。

5.2 周菜谱编辑器的组件化拆分

膳食排餐页面是整个前端最复杂的页面。它是一个 7 列(周一到周日)× 3 行(早中晚餐)的网格,每个格子是一个菜品选择弹窗。我把它拆成了三个组件:WeekCalendarMenu.vue(外层网格容器)、MenuCell.vue(单个格子)、DishSelectorDialog.vue(菜品选择弹窗)。

MenuCell接收两个关键 props——dayOfWeek和mealType,内部根据这两个值去调用接口查询当前格子已经安排的菜品。用户点击格子时弹出DishSelectorDialog,弹窗内部从/api/dishes拉取候选菜品列表,按分类标签筛选,选中后调用更新接口,同时用 Element Plus 的ElMessage给出成功状态反馈。

5.3 Axios 统一封装和接口请求的细节

Axios 封装是我觉得前端工程化的基础必做项。统一配置 baseURL 指向/api,开发环境通过 Vite 的代理转发到后端 3000 端口,生产环境由 Nginx 做反向代理。请求拦截器里自动带上Authorization: Bearer头,响应拦截器统一处理错误码:401 跳登录页、500 弹出“服务异常,请稍后重试”、业务错误码直接ElMessage.error(message)展示后端返回的提示文字。

// axios.js 封装的响应拦截器核心 service.interceptors.response.use( response => { const res = response.data; if (res.code !== 0) { ElMessage.error(res.message || '请求失败'); if (res.code === 401) { userStore.logout(); router.push('/login'); } return Promise.reject(new Error(res.message)); } return res.data; }, error => { ElMessage.error(error.message || '网络错误'); return Promise.reject(error); } );

5.4 用餐记录和评价数据的可视化呈现

数据可视化我用的是 ECharts 5 按需引入的方式。首页仪表盘放了三个图:折线图展示最近 30 天老人用餐满意度趋势、横向柱状图展示各区域护工月度综合评分排名、饼图展示本周菜品满意度分布。ECharts 在 Vue 3 里的使用方式没什么特殊的,关键点是在onUnmounted钩子里调用chart.dispose(),否则频繁切路由会内存泄漏。另一个经验是不要直接用echarts.init去初始化同一个 DOM 节点两次,Vue 组件销毁重建时容易报 “Initialize failed: invalid dom” 的错误。

6. 从零搭建到跑通:环境配置和开发调试的实战避坑

这个标题下其实有一半的初学者,卡住的往往不是业务代码,而是环境搭建。尤其是 Windows 环境下,Node.js 和 npm 的问题格外多。我在这个项目里遇到并解决的几类问题,基本覆盖了网上搜索热度最高的那些坑。

6.1 Node.js 安装与环境配置中最高频的报错

很多人在 Windows 上装完 Node.js 之后,试图在 PowerShell 里运行npm install,结果直接飘红:npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本。这个问题的本质是 PowerShell 执行策略默认是 Restricted,不让运行 .ps1 脚本。解决方案有两种,我推荐用第一种,第二种的开法不太想提,因为它很容易引发性子急的同学顺手把机器的执行策略调到 Unrestricted,导致安全问题。

说回正经方案。你不需要去动系统级权限,只需要把默认终端从 PowerShell 切换成 Git Bash,或者在项目目录下直接用 CMD(命令提示符)运行 npm 命令。如果确实想在 PowerShell 里操作,有一个更稳妥的办法:打开 PowerShell,输入Set-ExecutionPolicy -Scope CurrentUser ExecutionPolicy RemoteSigned,只对当前用户放开本地脚本执行权限,效果可持续到这台机器重装系统。

另外一个高频问题是 Node.js 版本不匹配导致编译失败。Vue 3 + Vite 项目要求 Node 版本不低于 16.14,我建议直接装 18 LTS 或 20 LTS。用nvm-windows管理 Node 版本也是个好习惯,开发时按项目切版本,避免不同项目互相污染依赖。

6.2 Vue 项目初始化和依赖安装时的崩溃现场

前端项目初始化的命令是npm create vue@latest,但这个命令在 npm 源默认指向国外镜像时,下载速度非常感人,经常“转圈半小时然后超时”。我处理的办法是先把 npm 源切到国内镜像,npm config set registry https://registry.npmmirror.com。切完源再初始化,速度能快一个数量级。

依赖安装时另一个常见问题是node_modules里有玄学冲突,比如 Element Plus 和某个低版本vite-plugin-vue-setup-extend同时存在时,编译会报奇怪的.vue文件解析错误。我的暴力解法是:删除node_modules和package-lock.json,先只装核心依赖确认能跑起来,再逐个安装功能依赖,装一个跑一次npm run dev。虽然效率低一点,但能精确定位是哪一次安装引入了冲突。

6.3 前后端联调阶段最容易卡住的跨域和代理问题

开发阶段前后端是分端口跑的,Vue 默认跑在 5173,Node.js 跑在 3000。如果不做任何处理,前端页面里fetch('/api/dishes')会直接请求到 5173 这个端口,然后 404。标准解法是在vite.config.js里配置开发代理:

// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') } } } });

配置代理的时候要注意changeOrigin: true这个参数。如果不加,某些请求带上原始 Host 头,后端如果做了 Host 校验就会 403。另外,如果你用了history模式路由且不是 SPA 的根路径刷新,Nginx 那边要加try_files $uri $uri/ /index.html;,否则一刷新就 404 白屏。这个坑我记忆犹新,第一次部署时盯着白屏看了半天,打开控制台才发现是静态资源路径全偏了。

6.4 组件源码发给别人后运行不起来的问题

网上经常有人问“Vue 项目源码怎么发给别人才能跑起来”,这个问题背后的本质是依赖不完整或环境差异。我的经验是:保证项目里保留package.json和package-lock.json,把node_modules目录加进.gitignore,接收方拿到后先执行npm ci而不是npm install,前者会严格按照 lock 文件安装依赖,避免版本浮动。另外,项目里的绝对路径配置要改成相对路径,比如base: './',否则发给别人换目录跑,图片和路由全挂。

7. 部署到服务器:让系统真正跑在养老院的电脑上

系统开发完成只是第一步,真正让它在养老院的服务器上稳定运行,还涉及到部署方式、数据库初始化和进程守护这几件麻烦事。

7.1 前后端分离部署的思路和细节

我的部署方案分两层:前端用 Nginx 托管打包后的静态文件,后端用 PM2 守护 Node.js 进程。具体步骤是先跑npm run build生成dist目录,把dist里的内容上传到服务器指定目录,比如/var/www/meal-system/dist;后端整包上传到/opt/meal-system/server,安装依赖后执行node app.js验证能不能起来,再交给 PM2 托管。

Nginx 配置里关键的两块:一是location /指向前端静态文件目录,二是location /api/做反向代理到http://127.0.0.1:3000。跨域问题在 Nginx 层解决,比在后端配 CORS 干净得多。

7.2 数据库初始化和历史数据迁移的取舍

MySQL 8.0 安装完毕后,需要执行数据库初始化脚本。我建议利用 Sequelize 的 migration 机制管理表结构变更,而不是手动建表。但项目赶进度的时候,直接sequelize.sync({ force: true })一把梭也是合理的——前提是你清楚这会清空所有表数据。我自己的做法是第一版开发时用 sync 自动建表,确认表结构没问题之后,把建表 SQL 导出来存一份,上线时手工执行那批 SQL,避免线上误跑 sync 重置数据。

这个系统的数据量不大,但养老院方面可能会要求把历史纸质台账录入系统。这个问题不要提前做——需要甲方先搞清楚录入范围,再评估是用 Excel 导入功能一键导入,还是安排实习生手工录入。我在交付时做了一个简易的 Excel 导入接口,字段校验仔细一点,能省下后期大量补录工作。

7.3 进程守护、日志和备份的三个小动作

Node.js 应用裸跑是很危险的事情,一崩溃就全站 502。我直接用 PM2 托管,并设置了开机自启。日志方面把 PM2 的输出重定向到日志文件,按日期切割,方便排查问题。数据库备份用 cron 每天凌晨两点执行一次mysqldump,简单粗暴,但足以应对数据丢失的极端情况。

我还建议在服务器上配置一个健康检查脚本,每分钟自动请求一次系统的健康检查接口,如果连续 3 次失败就自动重启 Node 服务。这个脚本用 shell 写不到 30 行,但对系统的稳定性提升是实打实的。

到这里,这套养老院膳食护工评价中心管理系统的主要技术脉络就梳理完了。如果让我再重做一次,我会在一开始就把“食堂排餐的自动替换算法”设计得更完善一些,而不是在食堂阿姨电话投诉之后才补上“连续三天不能重菜”的约束。开发这类管理系统的最大感受是:技术实现本身并没有多难,难的永远是提前理解业务现场的真实约束——比如老人忌口不能开玩笑、护工评分得让双方都信服、食堂阿姨不会用复杂交互。这些靠的是跟业务人员多聊几次,把他们的真实工作流程变成系统里的默认逻辑。希望这篇分享能帮你少走一些弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询