1. 项目定位与技术栈选型:出租车公司的管理痛点在哪里
先说结论:出租车公司的业务管理网站,本质上是一个中小型的多角色信息管理系统,它不像电商平台那样有巨大的并发流量,也不像金融系统那样对事务一致性要求苛刻,但它有一个很典型的特点——角色多、状态多、单据流转链条长。
我做这个项目时,客户是一家拥有约80台出租车的区域性出租车公司。他们的业务现状是这样的:司机每天交班时要到公司填写纸质交接单,调度员用Excel表格记录车辆派单情况,财务月底对账要翻一个月的纸质票据。整个流程不是跑不起来,而是效率极低、对账困难、数据无法沉淀。比如某辆车这个月出了几次事故、哪个司机连续三个月营收下滑、哪笔订单的油补该发没发,这些数据全都散落在纸质单据和个人记忆里。
所以,当我们要用技术手段去解决这类问题时,第一步不是选框架,而是先梳理清楚系统要服务的角色和业务逻辑。这个网站需要解决的核心问题有三个:
- 车辆全生命周期管理:从车辆信息登记、年检提醒、保险到期、维修记录到报废状态,都需要线上化。
- 司机与订单的流转管理:司机排班、派单、交班、营收结算,订单状态要从"待接单"流转到"进行中"再到"已完成",最后进入结算池。
- 多角色协同:管理员、调度员、财务、司机,四类角色看到的页面和能执行的操作完全不同。
基于以上需求,我选择了Node.js + Express + Vue 3 + MySQL这一套技术栈。为什么不用更流行的Java Spring Boot?原因很实际:这个项目的核心业务逻辑是状态流转和CRUD操作,Node.js的异步非阻塞模型处理这类IO密集型任务效率非常高,而且Express生态成熟、上手快。对于一家出租车公司来说,系统后期大概率需要对接GPS定位、计价器数据、支付网关等外部服务,Node.js在这类轻量级接口对接上开发效率极高。
Vue这边,我选的是Vue 3 + Element Plus组合。Vue的响应式数据和组件化开发特别适合管理后台这种"表格多、表单多、联动多"的场景。Element Plus提供开箱即用的表格、弹窗、表单校验组件,能省下大量UI开发时间。有人会说管理后台用React也行,但对我来说,Vue的模板语法和单文件组件结构,在快速迭代业务页面时体验更顺滑。
小提示:如果你的系统未来可能扩展到大型分布式架构,可以考虑Spring Boot;但如果像我一样,目标是"用最快的速度交付一个能真正用起来的系统",Node.js + Vue绝对是一个性价比极高的选择。
技术栈确定后,整个项目的模块划分也就清晰了。我把系统拆成三个主要部分:司机管理模块、车辆管理模块、订单与结算模块,外加系统管理(用户、角色、权限)。下面我会从环境搭建开始,逐一分享每个环节的实现细节和踩坑记录。
2. 开发环境从零踩坑:Node.js安装与npm.ps1执行策略问题
这个章节我把它放在最前面,因为很多新手(包括当年的我)在环境配置阶段就被卡住了,后面所有计划全部泡汤。
先说说Node.js安装的一个容易忽略的细节:安装路径。默认情况下,Windows下的Node.js安装包会装到C:\Program Files\nodejs\或C:\Program Files (x86)\nodejs\,而Program Files目录是有系统权限限制的。如果后续要通过npm全局安装包(比如npm install -g pm2),很容易因为权限不足导致安装失败。所以我的建议是:安装时手动把路径改成D:\nodejs\或C:\nodejs\这样没有空格和系统限制的目录。这个细节在官方文档里不会写,但能在后面省掉很多麻烦。
安装完Node.js之后,紧接着就会遇到热搜词里反复出现的那条经典报错:
npm : 无法加载文件 D:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本这条报错的本质是PowerShell的执行策略(Execution Policy)限制了.ps1脚本的运行。npm本身是一个命令行工具,但它有一个PowerShell脚本包装器(npm.ps1),当你直接用PowerShell执行npm命令时,如果系统执行策略是Restricted,就会拒绝运行。
解决方案有两种:
- 以管理员身份打开PowerShell,执行:
Set-ExecutionPolicy RemoteSigned,然后输入Y确认。这个策略表示"本地脚本可运行,远程脚本必须有签名",是生产环境相对安全的配置。 - 或者干脆在项目里改用
cmd终端来执行npm命令,绕开PowerShell的限制。
我个人的做法是第一种,因为后续还有很多依赖需要全局安装,Restricted状态下连npm run dev都可能被拦。要注意,Set-ExecutionPolicy修改的是当前用户的执行策略,如果你在Windows Server上部署,还需要检查服务账户是否有权限。
环境变量配置是另一个常见坑。Node.js安装包一般会自动把node.exe所在的目录写入PATH,但npm全局安装包的路径(通常是%APPDATA%\npm)有时不会自动加入。如果你执行npm install -g vite之后,命令提示符告诉你"vite不是内部或外部命令",大概率就是这个原因。解决办法:打开"系统属性→环境变量",在PATH里添加上C:\Users\你的用户名\AppData\Roaming\npm,然后重启终端。
再说说npm镜像源的问题。国内直连npm官方源的速度时好时坏,尤其是安装electron这种动辄几百MB的包时很容易超时。我习惯在项目根目录创建一个.npmrc文件,写上:
registry=https://registry.npmmirror.com electron_mirror=https://npmmirror.com/mirrors/electron/第一行把npm镜像切换到国内源,第二行单独指定electron二进制文件的镜像。这样既不会全局污染其他人的环境,又能显著提升安装速度。实测下来,同一个项目依赖的完整安装时间能从15分钟缩短到3分钟左右。
最后检查一下安装结果:
node -v # 输出 v18.18.0 之类的LTS版本号 npm -v # 输出 9.x.x到这里,环境就算准备好了。接下来进入重点项目开发阶段。如果你在Windows上用的是Visual Studio Code,建议把默认终端改成Git Bash或Command Prompt,可以有效避开PowerShell执行策略带来的干扰。
3. 后端API设计:围绕车辆、司机、订单、结算四条主线的数据建模
出租车公司业务管理系统的后端,核心工作就是设计数据表和对应的API接口。我在这里分享两个原则:一是表结构设计要贴近实际业务状态,二是接口要遵循RESTful规范但不要过度设计。
3.1 数据库设计:六大核心表
整个系统的数据模型我规划了六张表:用户表(sys_user)、角色表(sys_role)、司机表(driver)、车辆表(vehicle)、订单表(order_info)和结算表(settlement)。其中订单表是整个系统的"心脏",它的字段设计直接决定了后续功能的复杂度。
以订单表为例,核心字段包括:
CREATE TABLE `order_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '订单编号', `driver_id` bigint(20) DEFAULT NULL COMMENT '接单司机ID', `vehicle_id` bigint(20) DEFAULT NULL COMMENT '车辆ID', `customer_name` varchar(50) DEFAULT NULL COMMENT '乘客姓名', `start_address` varchar(255) DEFAULT NULL COMMENT '起点', `end_address` varchar(255) DEFAULT NULL COMMENT '终点', `order_status` tinyint(4) DEFAULT '0' COMMENT '0待接单 1进行中 2已完成 3已取消', `amount` decimal(10,2) DEFAULT '0.00' COMMENT '订单金额', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_driver_id` (`driver_id`), KEY `idx_order_status` (`order_status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里有一个设计上的小心思:order_status用tinyint类型而不是直接存中文字符串,这样既节省存储空间又方便程序里做状态枚举判断。而idx_driver_id和idx_order_status这两个索引是必要的,因为日常查询基本都围绕"某个司机跑了哪些单"和"当前有哪几单进行中"展开。
3.2 Express路由与业务分层
后端框架我选择Express 4.x,虽然Express 5已经发布,但4.x的中间件生态更稳定,相关踩坑资料也多。项目目录结构按功能模块拆分,而不是按技术层拆分:
server/ ├── routes/ # 路由定义 │ ├── auth.routes.js │ ├── driver.routes.js │ ├── vehicle.routes.js │ └── order.routes.js ├── controllers/ # 业务逻辑 ├── models/ # 数据库操作 ├── middleware/ # JWT鉴权、日志等中间件 └── app.js # 入口文件为什么把路由和业务逻辑分开?因为如果直接把SQL写在路由回调里,不到500行代码就会变得无法维护。分开的好处是:路由只负责请求分发和参数校验,controller处理具体业务逻辑,models封装SQL操作。这样每一层的职责单一,后期加功能或者修bug时能快速定位。
3.3 JWT鉴权与多角色权限控制
出租车管理系统有四类角色:管理员、调度员、财务、司机。不同角色能访问的接口完全不同,比如财务能看到结算信息,司机只能看到自己的订单。我用JWT(JSON Web Token)+ 自定义中间件来实现鉴权。
登录成功后,后端返回一个包含角色信息的JWT Token:
const jwt = require('jsonwebtoken'); // 登录成功后生成token const token = jwt.sign( { userId: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '2h' } );前端每次请求在Authorization头带上这个token,后端用一个全局中间件来解析和校验:
// middleware/auth.js const authMiddleware = (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ message: '未登录' }); try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (err) { return res.status(401).json({ message: 'Token失效,请重新登录' }); } };对于需要"仅管理员可操作"的接口,再包一层requireRole('admin')中间件即可。这样实现的权限控制足够清晰,也容易扩展。
3.4 订单状态流转的业务逻辑
订单模块的后端逻辑是这个系统最复杂的部分,难点不在于增删改查,而在于状态流转的合法性判断。
比如:一个"已完成"的订单不能被重新"派单",一个"进行中"的订单不能直接"取消"。如果在业务逻辑层不加以控制,前端误操作或接口被恶意调用就会产生脏数据。
我的做法是在controller里定义一个状态流转表:
const ORDER_STATUS = { PENDING: 0, // 待接单 PROCESSING: 1, // 进行中 COMPLETED: 2, // 已完成 CANCELLED: 3 // 已取消 }; const TRANSITIONS = { [ORDER_STATUS.PENDING]: [ORDER_STATUS.PROCESSING, ORDER_STATUS.CANCELLED], [ORDER_STATUS.PROCESSING]: [ORDER_STATUS.COMPLETED, ORDER_STATUS.CANCELLED], [ORDER_STATUS.COMPLETED]: [], [ORDER_STATUS.CANCELLED]: [] }; // 每次更新状态前先校验 const canTransition = (current, next) => TRANSITIONS[current]?.includes(next);这样写的好处是,状态与状态之间的合法跳转关系一目了然,即使未来新增一个"退款中"状态,也只需要在表里加一条记录。
3.5 接口响应格式统一
后端接口的返回值格式如果不统一,前端联调时就会很痛苦。我定义了一个通用的响应包装函数:
// utils/response.js const success = (res, data, message = '操作成功') => { res.status(200).json({ code: 200, message, data }); }; const error = (res, message, code = 400) => { res.status(code).json({ code, message, data: null }); };所有controller统一调用这两个函数,前端axios拦截器也就只需要处理一个固定的数据格式。
4. 前端Vue业务页面:路由权限、状态管理与表单交互的实现细节
前端这部分是工作量最大的环节,因为管理后台的页面数量多而且交互密集。我以Vue 3 + Vite + Pinia + Element Plus为技术基础,这里重点聊聊几个容易被忽视的实现细节。
4.1 动态路由与路由守卫
普通的Vue项目路由表是写死的,但出租车管理系统必须根据登录用户的角色动态生成菜单和路由。比如司机账号看不到"车辆管理"菜单,调度员账号看不到"财务结算"菜单。
实现思路分三步:
- 登录成功后,后端返回当前用户的角色信息;
- 前端根据角色,从静态路由表里筛选出有权限的路由,通过
router.addRoute()动态注册; - 在全局路由守卫
router.beforeEach里判断:如果用户已登录但动态路由还没注册,就先注册再放行,否则直接放行。
核心代码如下:
// router/index.js const dynamicRoutesMap = { admin: [...], dispatcher: [...], finance: [...], driver: [...] }; router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (!token && to.path !== '/login') { next('/login'); return; } if (token && !store.rolesLoaded) { // 动态注册路由 const roles = store.userInfo.roles; const routes = roles.flatMap(role => dynamicRoutesMap[role] || []); routes.forEach(route => router.addRoute(route)); store.setRolesLoaded(true); next({ ...to, replace: true }); } else { next(); } });这里的{ ...to, replace: true }是关键小技巧:因为addRoute后当前路由表还不完整,直接next()可能造成"循环重定向"或"路由不匹配"的问题,用这种方式重新触发一次导航就能解决。
4.2 Pinia状态管理:用户信息与系统配置
我选用Pinia作为状态管理库,它比Vuex更轻量,TypeScript支持也更好。在这个项目里,全局状态我拆成了三个store:userStore(用户信息、Token、角色)、orderStore(订单列表的选中状态、筛选条件)、appStore(侧边栏折叠状态、全屏加载动画)。
实际开发中很容易犯的一个错误是把所有接口数据都塞进store里。我后来总结出一个经验:只有那些需要跨组件共享的数据才值得放进store。比如"当前登录司机的本月营收"这个数据,多个页面(首页统计、司机详情、结算列表)都要用到,就应该放进store。而订单详情页的数据只在自身页面展示,直接通过接口获取、用组件本地变量保存即可,没必要全局共享,反而让store变得臃肿。
4.3 Element Plus表格组件的二次封装
管理后台最常见的页面模式是"搜索区 + 表格区 + 分页区"。如果每个列表页都重复写一遍表格和分页逻辑,代码冗余会非常严重。我封装了一个BusinessTable组件,它接收列配置、接口地址、查询参数,内部统一处理加载状态、分页变更、数据刷新。
<!-- components/BusinessTable.vue 简化版 --> <template> <div class="business-table"> <el-table :data="tableData" v-loading="loading"> <el-table-column v-for="col in columns" :key="col.prop" :prop="col.prop" :label="col.label" :width="col.width" /> </el-table> <el-pagination :current-page="currentPage" :page-size="pageSize" :total="total" @current-change="handlePageChange" /> </div> </template>实际在封装时要注意一个细节:el-table-column中如果使用了插槽(比如操作列需要自定义按钮),需要在列配置里增加一个type: 'slot'的标记,然后在组件内部动态渲染对应的具名插槽。这样才能兼顾80%的常规场景和20%的定制场景。
4.4 订单列表的筛选与多条件联动
司机和管理员最常用的功能是订单列表的筛选,筛选条件通常包括:日期范围、订单状态、司机姓名。这里有一个前后端配合的细节——筛选参数如果直接拼在URL里,刷新页面后状态会丢失。
我的解决方法是:把筛选条件存入Pinia的orderStore,刷新页面后从store恢复。具体来说,订单页面的onMounted里先检查store里有没有上次的查询条件,如果有,自动填充到搜索表单并重新发起请求;如果没有,使用默认条件(比如最近7天)。
为了让列表的"查看详情"操作更像真实业务系统,我做了两个内容非常详细的详情抽屉:一个是展示乘客信息、起终点、费用明细、司机信息的订单详情;另一个是展示车辆维修记录的车辆详情。用户点击某行记录时从右侧滑出详情面板,而不是跳转到新页面。这种交互在管理后台里比跳转页面更高效,值得推荐。
4.5 Vite开发环境解决跨域问题
开发阶段最挠头的问题是跨域,尤其是Vue跑在5173端口,后端Express跑在3000端口,浏览器直接请求会有CORS限制。我没有在后端专门配置CORS中间件,而是在Vite的配置文件里设置了代理:
// vite.config.js export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') } } } });这样一来,前端请求的URL写/api/drivers,Vite在开发阶段会自动转发到http://localhost:3000/drivers。而生产环境部署时,我用Nginx的反向代理把/api前缀再次转发到Node服务。这样设计的好处是:前端代码里所有请求路径都带/api前缀,一份代码在开发和生产环境都能正常工作,不需要修改任何环境变量。
5. 前后端联调与异常处理:从接口约定到生产环境验证
前后端联调是项目开发中耗时最长、最容易出问题的阶段。我总结了几个最容易踩的坑,以及对应的解决方案。
5.1 axios请求封装与统一错误处理
我封装了一个request.js作为全局请求入口,核心逻辑包括:
- 请求拦截器里自动从localStorage读取Token并写入
Authorization头; - 响应拦截器里统一处理后端返回的
code字段,如果code不是200,弹出错误提示(用的是Element Plus的ElMessage.error); - 检测HTTP状态码401,表示Token过期,自动清除本地登录状态并跳转登录页。
这里的核心问题是:Token过期后的处理方式要仔细设计。如果只是简单跳转到登录页,用户在填写长表单时突然被踢出去,体验极差。我的方案是:当接口返回401时,先不立即跳转,而是弹出一个"登录已过期,请重新登录"的确认框,用户点击确认后才跳转登录页,并把当前页面的路由记录下来,登录成功后自动跳回原页面。这个细节虽然简单,但用户的满意度会大幅提升。
另外有一个特别容易忽略的地方:文件下载接口的响应格式与JSON接口不同。如果某个接口返回的是Excel文件流,常规的axios拦截器会尝试解析JSON,导致下载失败。我处理的方式是:对下载类的请求关闭统一拦截,单独在组件里处理response.data为Blob对象后用URL.createObjectURL生成临时下载链接。
5.2 跨域问题在生产环境的表现
前面提到开发环境用Vite代理解决跨域,但生产环境如果也忘了处理,会出现一种更隐蔽的报错:请求能到达服务器,服务器也能正常返回,但浏览器拦截了响应,控制台报CORS错误。这种问题排查起来非常费时间,因为它不会显示"请求失败",而是显示"CORS policy: No 'Access-Control-Allow-Origin' header"。
在生产环境,我的处理方案是在Nginx配置里加代理转发:
location /api/ { proxy_pass http://127.0.0.1:3000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }这样浏览器请求的是Nginx的地址(相当于同源请求),Nginx再把请求转发给Node.js服务,完全规避了跨域问题。
5.3 表格数据刷新与缓存一致性
在管理后台里,用户常常会遇到这样的操作流:在A列表页删除了某条记录,然后在B列表页刷新,却发现数据仍然是旧的。如果每个列表页在onMounted时都从接口重新取数,理论上不会有这个问题,但实际中为了性能,我给部分列表页加了keep-alive缓存。这就导致删除操作后,被缓存的页面不会自动更新。
我的解决方法是:在删除、编辑操作成功后,触发一个全局事件(可以是一段简单的mitt事件总线),被缓存的列表页监听这个事件后自动刷新数据。比如:
// 新增/编辑/删除操作成功后 mitt.emit('table-data-changed', { table: 'driver' }); // 列表页 mitt.on('table-data-changed', (payload) => { if (payload.table === 'driver') { fetchDriverList(); } });这样做虽然会在多页面同时监听时多发起几次请求,但能保证用户看到的数据始终是最新的,业务上的误判会大幅减少。
5.4 生产环境验证清单
联调完成不代表结束,我在正式交付前通常有一套完整的验证清单:
- 用无痕浏览器窗口测试未登录状态下的页面跳转是否正常;
- 分别用管理员、调度员、财务、司机四个角色登录,确认菜单和按钮权限无误;
- 在慢网速条件下(DevTools里选择Slow 3G),检查加载动画和错误提示是否合理;
- 提交一次包含非法字符的表单(比如在手机号字段填中文),确认后端的参数校验能正常返回错误信息。
这些验证的点往往是客户真实使用时最容易出问题的位置。一个严谨的交付流程不仅是对用户负责,也是给自己省掉大量售后排查的时间。
6. 部署上线与日常维护:从Windows服务器到进程守护的实践经验
系统的部署环境我选了Linux云服务器,原因很简单:Linux下部署Node.js应用生态更成熟,进程守护、日志管理都有现成工具。下面这些经验是我在多次部署后总结出来的。
6.1 生产环境Node.js与npm配置
在服务器上安装Node.js时,不同Linux发行版的包管理器自带的版本往往不是最新的。比如Ubuntu 20.04自带的Node.js是10.x,而很多新版本依赖(比如Vite 5)要求Node.js 18以上。
推荐用NodeSource源或者nvm来安装指定版本。我的习惯是装nvm,因为后续如果涉及项目升级或同时维护多个项目,nvm可以随时切换Node版本:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash nvm install 18.18.0 nvm alias default 18.18.0Node版本确定后,再配置npm config set registry指向国内镜像,缩短安装时间。
6.2 使用PM2守护进程与日志管理
Node.js应用的进程守护我用的PM2。很多人直接在服务器上用node app.js启动服务,一旦进程崩溃或者服务器重启,服务就再也起不来了。PM2可以作为系统服务管理Node进程,还自带内存监控和日志轮转。
启动命令非常简单:
pm2 start app.js --name taxi-server --env production几个实用操作:
pm2 save # 保存当前进程列表,开机自启 pm2 startup # 生成开机自启脚本 pm2 logs taxi-server # 查看实时日志 pm2 restart taxi-server # 重启服务我的经验是:PM2的日志管理默认是把console.log和错误输出写到同一个文件,但如果项目运行时间长了,日志文件会变得非常大。建议在pm2 start时加上--merge-logs --log-type json参数,或者直接用pm2-logrotate这个扩展来做自动轮转。否则半年后你打开日志目录,发现一个文件有几个GB,排查问题时翻日志都会卡死。
6.3 Nginx反向代理与静态文件托管
前端构建产物是纯静态文件,可以直接用Nginx托管,同时把/api请求反向代理到Node服务。我的Nginx配置核心片段如下:
server { listen 80; server_name taxi.example.com; # 前端静态文件 root /var/www/taxi/dist; index index.html; # 前端路由history模式必须配置 location / { try_files $uri $uri/ /index.html; } # 后端API代理 location /api/ { proxy_pass http://127.0.0.1:3000/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } }这里有一个对Vue应用至关重要的配置:try_files $uri $uri/ /index.html。因为Vue Router用了HTML5 History模式(URL里没有#号),用户在浏览器直接输入http://taxi.example.com/drivers时,Nginx要在后端找不到这个路径的情况下把请求转发给index.html,让Vue Router接管前端路由。如果没有这行配置,刷新任何一个子页面都会报404。
6.4 数据库备份策略
出租车订单数据是公司的核心资产,数据库备份不能马虎。我在服务器上写了一个简单的备份脚本,每天凌晨3点执行MySQL导出并保留最近7天的备份文件:
#!/bin/bash BACKUP_DIR="/var/backups/mysql" DATE=$(date +%Y%m%d) mysqldump -u root -p'密码' taxi_db > ${BACKUP_DIR}/taxi_db_${DATE}.sql find ${BACKUP_DIR} -name "taxi_db_*.sql" -mtime +7 -exec rm {} \;再用cron加入计划任务:
0 3 * * * /usr/local/bin/backup_taxi_db.sh >> /var/log/backup.log 2>&1实际部署中我对这个脚本做了一些优化:导出后用gzip压缩再传一份到另一台远程服务器,避免服务器本身磁盘损坏导致备份全部丢失。对于出租车公司这种小规模业务数据,每天一次增量备份已经足够,要求再高一些的话可以改成每6小时一次。
6.5 交付后的运维心得
系统上线交付后,我总结了几条运维心得:
- 初期一周最重要的是观察错误日志,很多业务逻辑漏洞只有在真实数据量下才会暴露,比如某条SQL在几十条数据时跑得飞快,但数据量到几千条后因为缺少索引变慢。
- Node.js服务的--max-old-space-size参数值得留意,默认内存上限大约1.4GB,如果业务增长很快,在PM2启动命令里指定
node_args: '--max-old-space-size=2048'可以提前规避内存溢出问题。 - 不要轻易在生产环境执行
npm update,版本锁定到package-lock.json的状态,除非有明确的安全漏洞需要修复。
这个项目从需求梳理到正式交付上线,前后花了不到两个月。对于出租车公司业务管理网站这类系统,技术难度并不算高,真正的价值在于你对业务的理解:出租车公司要的不是一个炫酷的驾驶舱仪表盘,而是"司机今天该交多少钱""哪台车该年检了""这个月总营收是多少"这些琐碎问题的快速解答。Node.js和Vue是我用来实现这些答案的工具,而它们恰恰是轻盈、高效又足够灵活的组合。如果你也在规划类似的管理系统项目,希望这篇分享能帮你少走一些弯路,把更多时间花在理清业务逻辑上。