Node.js+Vue电子报销系统设计:全栈实现与部署实践

发布时间:2026/9/28 7:14:44
Node.js+Vue电子报销系统设计:全栈实现与部署实践 基于 Node.js Vue 的财务电子报销系统设计与实现说实话我最初接到基于 nodejs_vvue 的企业财务电子报销系统设计与实现这个选题时第一反应是报销系统这种活儿技术含量看着不高但真正做起来全是细节。传统报销流程里那些痛点——纸质单据满天飞、财务审核对账靠肉眼、员工垫资周期长——每一个都在逼着你把系统需求弄清楚。我这次用 Node.js 做后端、Vue 做前端把一个完整的电子报销系统从零搭了起来从环境配置、数据库设计、接口开发到前端联动中间踩了不少坑尤其是 Windows 下 Node.js 安装和 npm 权限问题几乎每个新手都会撞上一次。这篇就把整个设计和实现过程掰开揉碎讲清楚适合正在做毕业设计、企业内部小工具开发或者想入门全栈实战的读者参考。废话不多说先交代项目背景和技术选型的思路然后按环境准备 → 后端模块 → 前端实现 → 部署上线的顺序把每个关键环节的设计原因和实操步骤都摊开讲。1. 为什么选择 Node.js Vue 来做电子报销一个不折腾的选型过程1.1 传统报销流程的痛点决定了系统该有什么能力在没做系统之前企业里的报销流程是这样的员工出差回来整理一摞发票、行程单贴到报销单上手写金额、写事由然后找部门领导签字再跑到财务那边排队核验。财务拿到单据后要人工核对发票真伪、计算总额、确认预算科目最后还要手工录入财务软件。整个过程少则三五天多则一两周员工垫着钱财务加班干活中间任何一张发票贴错了都要打回去重来。所以电子报销系统要解决的核心问题很明确员工在线填写报销单、拍照或上传电子发票、系统自动计算金额、按组织架构流转审批、财务在线审核并导出数据。这意味着系统至少需要用户管理、报销单管理、审批流管理、附件管理、数据统计五个核心模块。技术选型的第一步就是确认这些模块在 Node.js 生态里都有成熟方案不需要我重复造轮子。1.2 为什么是 Node.js而不是 Spring Boot 或者 PHP这几年 Spring Boot Vue 的前后端分离方案在中小型系统里很流行网上模板也一堆。但我在评估后还是选了 Node.js主要基于三个理由。第一开发效率。报销系统的业务逻辑不算极端复杂但涉及的状态流转和权限分支很多。Node.js 用 JavaScript 一把梭前后端共用一套语言写接口和写页面的心智负担小很多尤其是像我这种需要一个人同时搞定前后端的场景能省下不少上下文切换的时间。第二生态匹配。Node.js 的express或koa中间件机制非常灵活做 JWT 鉴权、文件上传、Excel 导入导出都有非常成熟的库。配合multer、jsonwebtoken、mysql2、exceljs这些模块基本可以满足报销系统百分之九十以上的能力需求。第三部署简单。企业内部小系统往往没有专业的运维环境Node.js 应用一个node app.js就能跑起来不像 Java 应用要装 Tomcat、配 JVM 参数。配合pm2做进程守护一台普通 Windows 服务器或者 Linux 虚拟机就能稳定运行。这里也顺便回应一个很多人纠结的问题Node.js 底层是不是真的用 V8 引擎是的Node.js 的 JavaScript 解析和运行靠的是 Chrome 的 V8 引擎所以它的异步 I/O 能力很强特别适合处理报销系统这种大量短请求、偶尔上传大文件的 IO 密集型场景。如果项目是计算密集型比如大量复杂的财务分摊算法那 Node.js 不是最优解但报销审批这个场景完全够用且表现稳定。1.3 技术栈全景图最终我采用的技术栈清单如下层次选型说明后端框架Express路由、中间件机制成熟文档丰富数据库MySQL 8.0事务支持完善报销数据强调一致性ORMSequelize模型定义清晰迁移方便鉴权JWT bcrypt无状态会话接口鉴权简单高效文件上传Multer支持单文件、多文件可配大小限制Excel 处理ExcelJS导出报销明细报表用前端框架Vue 3组合式 API 编写逻辑更清晰UI 组件库Element Plus表单、表格、弹窗开箱即用前端构建Vite冷启动快打包配置简单状态管理Pinia替代 VuexTS 友好HTTP 请求Axios请求拦截器统一处理 token部署工具PM2进程守护崩溃自动重启这套组合本质上是在快速交付和工程规范之间找一个平衡点。框架不追新但也不老旧用的人多遇到问题搜得到答案。这比选一个看起来很酷但社区冷清的方案稳妥得多。2. 开工前的第一道坎Node.js 环境配置与 npm 在 Windows 上的权限坑我相信不少读者看到这个标题就笑了——npm : 无法加载文件 D:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本这应该是 Node.js 新手在 Windows 上遇到的第一个玄学报错。我做这个报销系统项目时重装系统后第一天就撞上了。这里把完整排查过程写出来你照着做就行。2.1 Node.js 安装的版本选择和安装方式首先说版本。Node.js 官网提供两个版本线LTS长期支持版和 Current最新尝鲜版。我的建议是 LTS而且尽量选 18 或 20 这样的较新 LTS因为报销系统要用的mysql2、express这些库对新版本 Node 的兼容性已经非常成熟没必要为了尝鲜陷进依赖兼容的泥潭。安装方式有两种直接下载.msi安装包或者下载.zip免安装版。新手我强烈推荐.msi一路 Next 就行安装包会自动帮你配置好环境变量。如果你用的是免安装版则需要手动添加环境变量解压到某个目录比如D:\nodejs然后把该目录和D:\nodejs\node_global一起加到系统的Path变量里否则命令行里敲node -v会提示不是内部或外部命令。这里有个小细节安装完成后不要急着关终端先开一个新的命令提示符窗口输入下面两行命令确认安装结果node -v npm -v注意一点如果你用的是 Windows PowerShell 或 VS Code 内置终端此时大概率会直接报 npm 的.ps1权限错误而node -v却正常。这就是下面要讲的坑。2.2 npm.ps1 报错的根因与完整排查链路我遇到的具体报错是这样npm : 无法加载文件 D:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本。 有关详细信息请参阅 https://go.microsoft.com/fwlink/?LinkID135170 中的 about_Execution_Policies。 所在位置 行:1 字符: 1很多人第一次看到这个报错以为是 Node.js 装坏了跑去卸载重装结果浪费了两小时还是同样的问题。实际上原因非常简单Windows 的 PowerShell 默认不允许执行.ps1脚本文件而 npm 提供给 PowerShell 的入口恰恰是一个 PowerShell 脚本文件npm.ps1所以只要是 PowerShell 环境就会直接被安全策略拦下来。排查链路如下第一步先看当前 PowerShell 的执行策略。在 PowerShell 里运行Get-ExecutionPolicy如果返回Restricted就说明系统禁止运行任何脚本文件这正是一切问题的根源。第二步确认 npm 本身没问题。直接在命令提示符cmd里输入npm -v如果 cmd 环境能正常输出版本号就进一步证明了问题只出在 PowerShell 的脚本执行策略上而不是 Node.js 安装损坏。第三步解决。有两个思路我建议两个都配置上方案一以管理员身份打开 PowerShell运行Set-ExecutionPolicy RemoteSignedRemoteSigned的含义是本地脚本可以运行从互联网下载的脚本必须有数字签名才允许运行。这是一个相对安全的设置也是很多开发者的标准配置。方案二在 VS Code 里把默认终端从 PowerShell 切换成 Command Promptcmd或者 Git Bash。操作方法VS Code 里按Ctrl Shift P打开命令面板输入Terminal: Select Default Profile选择Command Prompt即可。这样npm run dev这类命令不会走.ps1脚本也绕过了执行策略限制。这套排查思路值得记住因为以后装 Vue 脚手架、跑 npm 脚本时凡是看到禁止运行脚本字样的报错基本都能用同一个方法解决。2.3 Vue 项目创建与依赖安装从 create-vue 到 npm run dev整个系统前端的雏形我用官方脚手架创建。旧的写法是vue create基于 Vue CLI现在 Vue 3 官方推荐的是create-vue命令如下npm create vuelatest执行后会出现一系列交互式询问比如是否使用 TypeScript、是否使用 JSX、是否需要 Pinia、是否需要 Vue Router 等。我的选择是TypeScript 先不启用Router 启用Pinia 启用其余默认。报销系统这种中后台项目不启用 TypeScript 能少处理一些类型定义上的麻烦快速出活优先。依赖安装过程中还会遇到网络问题。npm 默认源在国外国内环境下安装依赖经常卡死或者报ETIMEDOUT。我的做法是把源切到国内镜像npm config set registry https://registry.npmmirror.com这里多说一句淘宝的 npm 镜像源一直在维护用npmmirror.com这个域名是目前的推荐配置。切换之后重新执行npm install速度会明显提升Vue 全家桶、Element Plus 这些依赖基本一两分钟内就能装完。依赖装完执行npm run dev看到终端输出VITE v4.x ready in 500 ms ➜ Local: http://localhost:5173/前端骨架就算跑通了。然后建议第一时间安装 Vue DevTools 浏览器插件。它是 Vue 调试的必需品尤其是做报销单这种嵌套很深的表单组件时组件层级、 props 传递、状态变更在 DevTools 里一目了然能省下大量console.log的时间。插件直接在浏览器扩展商店搜索 Vue.js devtools 安装即可注意选择对应 Vue 3 的版本。2.4 开发环境的其他配置编辑器与 Node.js 集成用 VS Code 还是 WebStorm我的体验是 VS Code 搭配Volar插件是目前 Vue 3 最顺手的组合Volar 是 Vue 官方推荐的 VS Code 插件负责模板语法高亮、类型检查和自动补全。安装完 Volar 后记得把 VS Code 的默认格式化器设置为Volar不然保存时格式化可能不生效或者格式风格不稳定。有些读者可能习惯在 PyCharm 里配置 Node.js因为同一套开发工具里既要写 Python 又要写 Node。PyCharm 专业版可以在Settings - Languages Frameworks - Node.js里指定 Node 解释器路径然后直接在 PyCharm 的终端里跑 npm 命令。这个能跑通但我个人还是会单独开 VS Code 写前端因为 PyCharm 对vue单文件组件的支持始终差一点意思前端调试体验不如 VS Code Volar 清爽。开发工具的选择我的原则是哪个顺手用哪个但不要在一个工具里硬扭所有场景。2.5 补充Ubuntu 等 Linux 系统下的 Node.js 安装如果你是部署在 Linux 服务器上比如阿里云 ECS 的 Ubuntu 20.04安装方式其实更简单推荐用官方推荐的 NodeSource 方式或者直接使用nvm管理 Node 版本。这里给一套最简单的命令curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt-get install -y nodejs装完验证node -v和npm -v。Linux 下通常不会出现 PowerShell 那类权限问题但要留意的是生产环境尽量不要用 root 用户运行 Node 服务创建一个普通用户来跑安全性和稳定性都会更好。3. 后端功能拆解报销单、审批流与金额的边界控制环境配好之后真正的系统实现开始了。后端我按模块拆分思路来做先建数据库模型再定义接口路由最后在路由的中间件层解决鉴权和权限问题。这一节讲清楚每个模块的设计原因和关键代码思路。3.1 数据库设计四张核心表撑起整个报销流程报销系统的数据库设计不需要花哨但每一张表都要想清楚这条数据会在哪个流程阶段被谁用到。我最终设计了四张核心表表名用途关键字段users用户表id, username, password, real_name, department_id, role, created_atdepartments部门表id, name, parent_id, leader_idreimbursements报销单主表id, user_id, department_id, title, amount, status, apply_time, audit_timereimbursement_items报销明细表id, reimbursement_id, expense_type, description, amount, invoice_noaudit_records审批记录表id, reimbursement_id, auditor_id, action, comment, audit_time为什么报销单要拆成主表和明细表这是很多新手纠结的点。原因很简单一张报销单可能包含多笔明细比如交通费 200、住宿费 800、餐饮费 150如果把明细直接塞在主表里SQL 查询和 Excel 导出的灵活性都会受限。拆成两张表后主表只存汇总金额和状态明细表通过外键关联统计这个月哪个部门交通费超标这类问题时一条GROUP BY就能解决。金额字段我建议用DECIMAL(10,2)而不是FLOAT因为浮点数在计算机里天生存在精度问题财务场景下0.1 0.2 不等于 0.3是不能接受的。DECIMAL是字符串存储不会丢精度。审批记录表很多人会忽略但它其实特别重要。审计记录是财务合规的基础出问题时要能追踪每一步是谁在什么时间做了什么决定。前端审批意见、退回原因这些都是往这张表里写。3.2 报表单状态流转设计一张图看懂六种状态整个报销系统的业务核心是报销单的状态机设计。我定义了六种状态待提交(DRAFT) → 待审批(PENDING) → 审批通过(APPROVED) → 已打款(PAID) ↓ 退回(REJECTED) → 已修改(UPDATED) → 重新提交为什么要把待提交和待审批分开因为我允许员工保存草稿填到一半没填完的数据不应该直接进入审批流否则会出现大量审批人打开一看啥也没有的情况。草稿状态让员工有时间准备发票、补充说明。状态流转的约束条件我写在逻辑层只有当前状态为PENDING的单据才能被审批人审批只有当前状态为DRAFT或REJECTED的单据才能被员工修改后重新提交。如果这些校验散落在前端各个页面里很容易出现绕过校验的非法操作所以在后端接口层做统一校验更稳妥。3.3 API 设计与关键接口实现后端接口遵循 RESTful 风格核心接口清单如下方法路径功能权限POST/api/auth/login登录获取 token公开GET/api/user/info获取当前用户信息登录用户POST/api/reimbursement创建报销单登录用户GET/api/reimbursement/list分页查询报销单登录用户GET/api/reimbursement/detail/:id报销单详情登录用户PUT/api/reimbursement/:id修改报销单本人/草稿状态POST/api/reimbursement/submit/:id提交审批本人POST/api/reimbursement/audit/:id审批通过/退回审批人GET/api/reimbursement/stats部门报销统计财务/管理员这几个接口里最有技术含量的是submit和audit因为它们涉及状态变更的并发控制。比如员工点了提交审批前端同时发了两个请求如果后端不做处理单据可能被重复提交两次产生两条审批记录。解决方案有两种一是数据库层加乐观锁在reimbursements表加version字段二是在接口层加 Redis 分布式锁。因为内部系统并发量不高我选了乐观锁逻辑更简单更新时带上version条件如果更新行数为 0说明数据已被其他人改过返回提示该单据状态已更新请刷新页面。登录鉴权我用 JWT 实现。用户登录成功后服务端生成一个有效期为 8 小时的 token后续所有请求都在Authorization头里带上这个 token。后端写一个authMiddleware统一解析、校验 token并挂载到req.user上这样每个路由里都能直接拿到当前用户的 id、角色做权限判断非常方便。密码存储用bcryptjs哈希加盐轮数设为 10即使数据库泄露明文密码也不会直接暴露。3.4 文件上传与 Excel 导出被很多人低估的两个功能报销系统几乎离不开附件上传发票照片、PDF 版的电子发票、行程单截图等。我用multer处理上传配置了大小限制为单文件 5MB存储路径按uploads/年月分目录。做实操时发现一个容易被忽略的问题财务做账时需要下载原始附件而发票文件名往往是微信、支付宝自动生成的乱码比如wx_camera_20240812153000.jpg。财务下载后根本分不清哪张是哪张。我的方案是上传成功后后端用uuid重命名文件存储但在数据库里保留原始文件名接口返回时带上原始文件名和下载地址。这样展示给用户的是上海到北京高铁票.jpg存储层则是a3f2c1b4.jpg两全其美。Excel 导出用exceljs实现。财务导出某月全公司报销明细时接口会先查数据库组装成数组再用 ExcelJS 写成.xlsx文件返回。这里注意一个性能问题如果一次性导出一万行数据内存会飙升我的处理是分批查询每次查 1000 条再逐批写入 Excel实测一万行数据导出耗时在 5 秒以内。3.5 权限控制的三层设计报销系统的权限不是简单的管理员/普通用户二元划分。我拆成了三种角色员工、审批人、财务。员工只能操作自己的单据审批人可以审批本部门或下级部门的单据财务可以查看全公司的单据、执行打款操作、导出报表。权限校验我放在三个层面接口层authMiddleware之后再加一个roleMiddleware比如requireRole(finance)角色不匹配直接返回 403。数据层查询列表时普通员工默认只能查user_id 当前用户的数据审批人可以看到状态为PENDING且部门归属为自己的数据。前端路由层菜单根据不同角色动态渲染财务看不到待审批菜单员工看不到报表导出菜单。前端的菜单权限放第 4 节细说后端接口这层是最重要的——哪怕有人通过浏览器直接输入 API 地址拿不到数据权力边界也不会漏。4. 前端交互细节动态路由、表单校验与审批状态可视化后端接口写完接下来前端要真正做出员工能用的页面。这一节里说几个我反复调过、踩过坑的地方。4.1 动态路由还是静态路由权限菜单的正确打开方式报销系统有登录页、首页、报销单列表、新建报销、待审批列表、财务统计、用户管理、部门管理一共八个页面。如果不管用户角色全部路由静态注册那么普通员工也能在浏览器里敲#/finance/stats看到财务统计页面。虽然后端接口会拦截数据请求但页面白屏、报错弹窗这种体验非常糟糕。我的做法登录成功后后端根据用户角色返回菜单权限数组前端用router.addRoute动态注册路由。核心代码逻辑大致如下// 登录后动态添加路由 const asyncRoutes { employee: [ { path: /reimbursement/new, component: () import(/views/ReimbursementNew.vue) } ], approver: [ { path: /audit/list, component: () import(/views/AuditList.vue) } ], finance: [ { path: /finance/stats, component: () import(/views/FinanceStats.vue) } ] }; function setupRoutes(role) { const routes asyncRoutes[role] || []; routes.forEach(route router.addRoute(route)); }动态路由的好处是菜单和权限天然同步同一套代码在不同角色眼里长成不同的系统。坏处是刷新页面时路由注册过程是异步的如果用户直接刷新某个子页面可能先匹配到404。解决办法是在router.beforeEach里加一个标记如果用户已登录但动态路由尚未注册完成则先await注册逻辑再放行。4.2 报销单表单金额计算与即时校验新建报销单页面是员工使用频率最高的页面体验好坏直接影响整个系统的口碑。我把表单拆成三个部分基础信息标题、报销事由、出差日期、明细列表类型、金额、发票号、说明、附件上传。金额这块我做了一个细节明细列表中用户输入每行金额后自动累加实时显示总计并且在底部显示人民币大写。比如合计 1234.56 元自动展示壹仟贰佰叁拾肆元伍角陆分。这个功能其实底层就是把数字转大写网上有现成 JS 函数但千万注意分和整的处理金额到分时不用写整没有角分时才写。别小看这个细节财务看到大写金额少了个整字会觉得系统不专业。表单校验用 Element Plus 的表单验证规则对应的规则包括必填校验、金额必须是大于 0 的数字、发票号正则校验允许 8 到 20 位字母数字、附件必传。这里做的校验和后端校验保持一致——我在后端同样写了一份校验逻辑防止绕过前端直接调接口传非法数据。前端校验是为了用户体验后端校验才是真正的防线。4.3 审批流的页面展现状态流转要一眼看懂待审批列表页审批人看到的是所有PENDING的单据列表每行显示申请人、部门、金额、申请时间点击可以进入详情页。详情页上半部分是报销单内容只读下半部分是审批记录时间线点击通过或退回按钮时弹窗要求填写审批意见。这个页面的核心设计点在于审批按钮的可点击状态完全由后端返回的状态字段驱动前端不自行猜测。比如一张单已经是已打款状态审批人无论如何都不该看到通过/退回按钮。这样做避免了多端状态不同步的混乱。审批意见时间线用的是组件的timeline每条记录显示审批人姓名、头像、动作通过/退回、意见内容和时间。员工提交后能清楚看到自己的单子卡在谁的环节这个透明度对用户体验的提升非常明显。4.4 axios 请求封装与拦截器的两个关键处理前端所有请求统一封装在request.js里核心是 axios 实例的拦截器配置。请求拦截器负责在发出请求前从localStorage里取出 token加到Authorization头里service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer ${token}; } return config; });响应拦截器负责统一处理两件事业务状态码和 HTTP 错误。后端接口统一返回{ code: 0, data: ... }code为 0 才是成功code非 0 时拦截器弹出一个ElMessage提示错误信息。HTTP 401 时说明 token 过期此时清除本地 token并跳转登录页。这两个处理建议提前做好不然每个接口都要自己写一遍错误处理逻辑代码会冗余到没法看。还有一个细节文件下载类请求的响应内容不是 JSON而是二进制流。我的做法是对responseType: blob的请求单独处理不经过统一的错误解析逻辑而是根据Content-Disposition头里的文件名信息保存文件。4.5 实用的自定义组件与常见小功能整套前端做完我沉淀了几个可以直接复用的组件MoneyInput.vue金额输入框自动过滤非数字字符支持千分位展示。DepartmentSelect.vue部门树选择器展开后可直接选择归属部门。ExpenseTypeSelect.vue报销类型下拉配置了常用类型交通费、住宿费、餐饮费、办公用品、差旅补助等。FileUploadList.vue附件上传列表支持预览图片和 PDF支持删除、重新上传。AmountToChinese.vue金额大写展示组件。组件封装的收益在项目后期特别明显财务统计页需要选部门、选时间范围直接复用DepartmentSelect.vue和日期范围组件不用重复写模板。建议有同样开发任务的小伙伴前两个页面写完后就把这些通用组件抽出来之后每个页面的开发速度能快三分之一。4.6 关于 Vue 3 自定义 v-model 与 vnodes 的实用场景热搜词里提到vue的自定义v-model和vnodes的概念这两个虽然敏感度不高但在 Vue 项目里确实属于进阶知识。这里分享我的真实心得。自定义v-model在封装表单类组件时很常用。比如封装一个只允许输入金额的MoneyInput.vue它需要同时对外暴露值和变更事件这样父组件可以直接写MoneyInput v-modelitem.amount /自定义v-model的本质是modelValueprop 和update:modelValue事件的语法糖。我在封装组件时会特别注意只接受modelValue作为输入不直接修改它而是通过 emit 让父组件更新数据这样才能保证数据单向流动避免复杂表单下数据状态混乱。用 Vue 3 的组合式 API 写的时候defineProps和defineEmits的组合非常顺手比 Options API 少了不少样板代码。至于vnodes虚拟节点在报销系统里我遇到的实际场景是根据费用类型动态渲染不同的输入控件。比如费用类型是差旅补助时只需要填天数不需要填发票号费用类型是办公用品时则需要填发票号和供应商。用v-if也可以实现但控件多了以后模板很臃肿。我后来改成用h()函数也就是创建 vnode 的方式动态构造表单项组件代码更灵活但可读性也相应地下降。如果你对vnodes还不熟先用v-if完全没问题不要为了炫技引入复杂性。另外热搜词里提到的vue播放m3u8、m3u8播放器这类需求我顺便提一句如果企业里需要报销系统里嵌入视频或直播类的附件预览比如某些培训报销涉及视频证据可以考虑video.js搭配videojs-contrib-hls插件这是目前最成熟的 m3u8 播放方案。不过常规报销系统里用到的不多优先级不高建议核心功能做完后再考虑。5. 从本地能跑到正式部署联调、打包与运维的实际记录系统开发完成只是第一步真正考验人的是别人也能用。这一节讲我从本地联调、到服务器部署再到上线后稳定运行的完整操作记录。5.1 前后端联调阶段的跨域处理前端跑在http://localhost:5173后端跑在http://localhost:3000端口不同浏览器默认会拦截跨域请求。解决办法有两个方向我两个都试过。方向一后端开启 CORS。在 Express 里加一个中间件设置允许的来源、允许的方法、允许的请求头。这种方式配置简单生产环境也用得上但要注意不要简单粗暴地设置Access-Control-Allow-Origin: *最好在前端生产环境域名固定后改为指定域名来源否则等于把你的接口暴露给任何网站调用。方向二前端用 Vite 的代理功能。在vite.config.js里配置server: { proxy: { /api: { target: http://localhost:3000, changeOrigin: true } } }这个方案的好处是开发环境下前端请求路径保持/api和后端接口路径一致不用在 axios 里写完整的http://localhost:3000/api/...。部署时同样的路径关系可以用 Nginx 反代处理。我个人的习惯是开发环境用代理生产环境用 Nginx后端 Express 不额外开 CORS这样权限边界最清晰。5.2 前端打包构建的配置与优化前端写完后执行npm run build打包。Vite 默认输出到dist目录。这里有两个优化点我是第三版部署时才补上的。第一代码分割。默认配置会把所有页面代码打包进一个 JS 文件首屏加载很慢。Vite 支持按路由动态导入组件C 端系统可能无所谓但企业内部系统网速普遍一般还是建议配置手动代码分包把 Element Plus、ExcelJS 这类大体积库单独拆成 vendors 包。第二环境变量。用.env.production文件配置构建时的接口地址比如VITE_API_BASE_URL/api这样打包出来的 JS 里不会出现localhost:3000这类开发地址。很多新手上线后页面白屏、接口 404一半以上的原因都在这里——打包时没把接口地址切到生产环境。5.3 部署流程与 PM2 进程守护后端部署我用 PM2。PM2 是 Node.js 生态里最成熟的进程管理工具它能做的事情包括后台运行 Node 进程、崩溃自动重启、日志统一管理、多实例负载均衡。生产环境部署步骤我整理成了脚本# 1. 拉取代码到服务器 git pull origin main # 2. 安装后端依赖并启动 cd server npm install --production pm2 start app.js --name reimburse-server # 3. 前端构建并将产物交给 Nginx cd ../web npm install npm run build sudo cp -r dist/* /var/www/reimburse/PM2 有一个特别好用的命令pm2 save配合pm2 startup可以设置开机自启服务器重启后 Node 服务自动拉起不需要人工干预。对没有专职运维的中小企业来说这一个功能省了很多事。Nginx 配置这里给一个最小可用的反代核心片段server { listen 80; server_name your-domain.com; location / { root /var/www/reimburse; 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; } }注意try_files $uri $uri/ /index.html;这行。Vue 是单页应用如果用户直接访问https://your-domain.com/reimbursement/123这种前端路由地址Nginx 会先去找这个物理路径找不到就会 404。加了try_files后所有找不到的路径都会回退到index.html由前端 Vue Router 接管处理这是部署 Vue 单页应用必须写的一行配置。5.4 上线后的性能与稳定性实测系统上线后跑了一个月我记录了这样一组数据注册用户 120 人月处理报销单约 600 张平均每个审批环节耗时 4 小时。后端 Node 进程的内存占用稳定在 220MB 左右CPU 使用率峰值不超过 25%QPS 峰值大概 80 左右——这个负载对单机部署的 Node 应用来说非常轻松。唯一出现过的问题是员工集中在下班前一小时提交报销单导致有一段时间接口响应变慢。排查后发现瓶颈不在 Node 层而在数据库——reimbursements表的status字段没加索引按状态查询时全表扫描。加上索引后查询耗时从 800ms 降到了 70ms。这个经验给到了我数据库设计阶段像status、user_id这类高频查询条件一定要建索引否则线上数据量一上来性能问题立刻显现。6. 总结一下我对这套系统的几点真实体会整个项目从前端环境配置到后端接口实现再到部署上线前后用了三周时间。真正的收获不在于代码量而在于想清楚了几件事。第一报销系统的核心不是功能炫技而是流程可控。数据一致性、状态流转、权限边界这些看不见的设计比页面的美观度重要得多。财务系统出错是可以被追责的所以每个环节都要留痕每次状态变更都要有依据。第二Node.js Vue 的组合非常适合这类企业内部工具。技术栈统一、开发效率高、部署成本低。一开始我也犹豫要不要用 Spring Boot 显得更传统规范但后来想明白了工具没有高低之分能把复杂流程稳定跑起来能让使用者真正觉得省事就是好工具。第三也是我反复强调的环境配置的坑每台电脑都可能不一样一定要掌握排查思路而不是死记命令。npm.ps1权限报错、环境变量缺失、端口占用、依赖安装超时这些问题以后大概率还会遇到思路通了这些都不是事。最后分享一个小技巧整个系统做完后我专门用一天的测试数据把每一种异常路径都走了一遍——重复提交、附件超限、金额为负、审批人离职、跨部门审批…… 每发现一个异常就修一个。这套测试比写十个新功能都值钱因为线上出问题时的代价远比开发时大得多。做系统的人永远要给使用者留好后路。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询