☰
C# .NET与Vue仓库管理系统实战:前后端分离架构与部署
2026/10/27 2:39:06 网站建设 项目流程

简介:这是一套基于C# .NET Web API与Vue.js实现的前后端分离式仓库管理系统源码,面向需要快速落地企业级库存管理模块的开发者、毕业设计学生及求职项目储备者,解决传统单体架构复用性差、前后端耦合度高、权限与跨域处理不规范等实际开发痛点。资源共243个文件,涵盖112个C#后端逻辑文件(含JWT鉴权过滤器、Swagger接口文档配置)、19个Vue组件页面、20个CSS样式与17个JS交互脚本,以及Web.config跨域配置、Global.asax启动项、.sln解决方案等核心工程文件,压缩包仅9.48MB,轻量易部署。已有767人学习下载,代码结构清晰,完整呈现Bearer Token登录流程(含Token生成、响应封装、控制器级Options预检处理)、Swagger自动生成API文档、Web API跨域集成等关键实践细节,可直接用于二次开发或作为.NET+Vue全栈学习范例。 做仓库管理系统这几年,前后端分离已经成为标配,但很多朋友在真正动手时还是会被一些细节卡住。比如接口怎么设计才算合理、权限怎么控制才够安全、Vue端和.NET后端到底怎么联调最顺畅。这套基于C# .NET + Vue的仓库管理系统,我把PC端和server端完整走了一遍,从数据库设计到接口开发再到前端页面,踩了不少坑,也沉淀了一套可以直接抄作业的方案。无论你是刚接触前后端分离的新手,还是想找一套完整参考的开发者,这篇内容都能给你实在的帮助。

1. 项目背景与整体架构设计

1.1 为什么选择C# .NET + Vue这套组合

先聊点实在的。仓库管理系统这种业务型项目,选型最怕两头不到岸。纯前端方案撑不起复杂业务逻辑,纯后端又没法做出好的交互体验。C# .NET做服务端,稳定性高、并发处理能力强,尤其在企业内部系统里有天然优势——大部分公司IT环境都是Windows,部署维护成本低。Vue作为前端框架,上手曲线平缓、生态成熟,配合Element UI这类组件库,做后台管理系统的效率非常高。

前后端分离架构下,后端只需要提供标准化的RESTful API,前端完全通过HTTP请求进行数据交互,互不干扰。这套系统里我用了.NET 8 + Vue 3,整体表现非常稳。接口响应时间普遍在50ms以内,即便在万级数据量的库存表上做分页查询,也没有明显卡顿。

适合参考这套方案的读者,我建议是这几类:已经在做.NET后端想补前端能力的、做Vue前端想理解后端接口设计的、以及需要一套完整前后端分离实战案例来做毕设或公司项目的。技术栈其实不算新,但前后端分离的完整链路——从环境搭建到联调部署——是很多教学资源覆盖不到的。

1.2 仓库管理系统的核心业务模块拆解

仓库管理系统听起来简单,真正拆解起来业务模块不少。我把它分成六大核心模块:用户权限、基础档案、入库管理、出库管理、库存管理和报表统计。每个模块不是独立存在的,它们之间通过单据和库存表互相联动。

用户权限模块负责登录认证、角色分配、菜单权限控制;基础档案管理物料信息、供应商、客户、仓库和货位;入库管理处理采购入库、退货入库、生产入库;出库管理对应销售出库、领料出库、退货出库;库存管理则是核心中的核心,实时更新库存快照、锁定库存、库存预警;报表统计对进出库数据进行汇总分析,给管理层做决策参考。

模块划分的核心思路是"单据驱动、库存联动"。所有库存变化必须通过单据触发,不允许直接修改库存表。这一点非常关键,因为仓库系统最怕数据不透明,库存一旦可以随便改,后面整个报表都是废的。我在设计数据库时就定了规矩:库存流水只增不改,库存当前值只是流水的最新快照。

1.3 前后端分离的数据流设计思路

前后端分离后,最大的变化是数据流从"页面刷新获取"变成了"接口异步请求"。前端Vue通过axios发起HTTP请求,后端Web API接收请求并处理业务逻辑,从数据库读取数据后返回JSON,前端再渲染到页面上。

这套系统的数据流向我梳理成一条主线:前端页面 → 路由守卫校验登录态 → Axios请求拦截器附带Token → 后端JWT认证中间件验证身份 → 控制器接收参数 → 业务逻辑层处理 → EF Core访问数据库 → 返回统一JSON格式 → 前端响应拦截器解析数据 → 页面组件更新视图。

为了规范这套流程,我在后端定义了一个统一响应格式:成功时返回{ code: 200, data: ..., message: "操作成功" },失败时返回对应错误码和提示信息。这样前端拦截器只要判断code是否为200,统一处理成功和异常,不用每个页面单独写错误判断。

2. 后端:C# .NET Web API核心实现

2.1 项目结构与分层设计

后端我用的是经典三层架构基础上演进的方案,按职责拆成四个项目:Api层(控制器和启动配置)、Service层(业务逻辑)、Repository层(数据访问)、Models层(实体类和DTO)。这样做的好处是各层职责清晰,后期维护不用到处找代码。

实际结构是这样的:

Solution/ ├── Warehouse.Api/ # Web API层 │ ├── Controllers/ # 控制器 │ ├── Middleware/ # 中间件(异常处理、JWT) │ └── Program.cs ├── Warehouse.Service/ # 业务逻辑层 │ ├── IInventoryService.cs │ └── InventoryService.cs ├── Warehouse.Repository/ # 数据访问层 │ ├── InventoryRepository.cs │ └── DbContext.cs ├── Warehouse.Models/ # 实体模型与DTO │ ├── Entities/ │ └── DTO/

分层设计的核心价值是"可替换性"。比如你前期用EF Core,后期觉得性能不够想换Dapper,只需要改Repository层的实现,Service层完全不用动。我实际做项目时经常遇到这种需求变更,分层带来的便利是实实在在的。

2.2 数据库设计与EF Core映射

数据库是仓库管理系统的根基。我的表设计遵循三个原则:主键统一用雪花ID(不用自增ID,方便分库分表和数据迁移)、所有业务表都带创建时间和操作人字段、关键表设计唯一约束防止重复数据。

核心表结构我整理了一张,工作量最大的就是这几张:

表名核心字段说明
UsersId, UserName, PasswordHash, RoleId用户表,密码存哈希不存明文
RolesId, RoleName, Permissions角色表,权限用JSON格式存储
MaterialsId, MaterialCode, MaterialName, Spec, UnitId物料档案表
WarehousesId, WarehouseName, Location仓库表
StockId, MaterialId, WarehouseId, Quantity, LockedQuantity库存快照表
StockFlowId, MaterialId, WarehouseId, ChangeType, ChangeQuantity, BeforeQuantity, AfterQuantity库存流水表
InboundOrderId, OrderNo, SupplierId, Status, TotalAmount入库单主表
InboundItemId, OrderId, MaterialId, Quantity, Price入库单明细表

EF Core映射我用的是Fluent API写实体配置,没用Data Annotation,因为Fluent API可以把所有配置集中管理,表结构变更时不用满世界找特性标记。下面这段是库存表的配置示例:

public class StockConfiguration : IEntityTypeConfiguration<Stock> { public void Configure(EntityTypeBuilder<Stock> builder) { builder.ToTable("Stock"); builder.HasKey(x => x.Id); builder.Property(x => x.Quantity).HasPrecision(18, 2); builder.HasIndex(x => new { x.MaterialId, x.WarehouseId }).IsUnique(); } }

注意那个联合唯一索引,这是防止相同物料在相同仓库出现两条库存记录的兜底措施。我在实际运维中确实遇到过因为并发导致重复数据的情况,这个约束能保证即使代码出bug也不会污染数据。

2.3 JWT认证与权限控制详解

仓库管理系统几乎没有不需要登录就能访问的页面,认证和权限是安全底线。我选用JWT(JSON Web Token)而不是传统的Session方案,原因在于前后端分离场景下,JWT天然无状态,服务器不需要保存会话信息,非常适合分布式部署。

JWT生成逻辑在登录接口中,验证用户名密码后生成Token,包含用户ID、用户名、角色ID和过期时间,用密钥签名后返回给前端。前端在后续请求的Header中携带Authorization: Bearer {token},后端中间件自动解析验证。

var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.Role, user.RoleId.ToString()) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"])); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _config["Jwt:Issuer"], audience: _config["Jwt:Audience"], claims: claims, expires: DateTime.Now.AddHours(8), signingCredentials: credentials); return new JwtSecurityTokenHandler().WriteToken(token);

过期时间我设置了8小时,工厂上班时间正好覆盖。这里有个容易被忽略的点:密钥长度必须满足HmacSha256的最低要求,至少16个字符,否则运行时会直接抛异常。不少新手在这踩坑。

权限控制上用访问令牌的方式实现。在需要权限的接口上标注[Authorize],在需要特定角色的接口上标注[Authorize(Roles = "Admin")]。但我的实际经验是不要过度依赖角色硬编码,因为业务里经常出现"角色一样但能看的菜单不同"的需求,所以我把前端菜单权限和后端接口权限分开控制:后端管接口访问权,前端管菜单显示权,两者通过角色下的权限列表关联。

2.4 核心业务接口实现:入库、出库、库存

入库接口是整个系统的典型业务,涉及主表、明细表、库存表、流水表四张表的操作,必须放在一个数据库事务里。我用的是EF Core的IDbContextTransaction,任何一步失败就整体回滚。

public async Task<ResultDto> CreateInboundOrder(InboundOrderCreateDto dto) { using var transaction = await _context.Database.BeginTransactionAsync(); try { var order = new InboundOrder { OrderNo = GenerateOrderNo("RK"), SupplierId = dto.SupplierId, Status = "待审核", CreatedAt = DateTime.Now }; await _context.InboundOrders.AddAsync(order); await _context.SaveChangesAsync(); foreach (var item in dto.Items) { // 写入明细 var orderItem = new InboundItem { OrderId = order.Id, ... }; await _context.InboundItems.AddAsync(orderItem); // 更新库存(存在则累加,不存在则新增) var stock = await _context.Stocks .FirstOrDefaultAsync(s => s.MaterialId == item.MaterialId && s.WarehouseId == dto.WarehouseId); if (stock != null) { stock.Quantity += item.Quantity; } else { await _context.Stocks.AddAsync(new Stock { ... }); } // 写流水 await _context.StockFlows.AddAsync(new StockFlow { MaterialId = item.MaterialId, ChangeType = "入库", ChangeQuantity = item.Quantity, BeforeQuantity = stock?.Quantity ?? 0, AfterQuantity = (stock?.Quantity ?? 0) + item.Quantity }); } await _context.SaveChangesAsync(); await transaction.CommitAsync(); return ResultDto.Success(order.Id); } catch { await transaction.RollbackAsync(); throw; } }

注意流水表里我记录了BeforeQuantity和AfterQuantity,这不仅是可追溯的需要,后期做数据对账时能直接对比每笔单据对库存的影响,排查问题效率翻倍。

出库接口逻辑类似,但有个额外动作:检查库存是否充足,而且要用"锁定库存"的概念来防止超卖。我的做法是先在库存表里扣减LockedQuantity(锁定数量),实际出库确认时再扣减Quantity并释放锁定。这样可以防止订单提交后、出库确认前,别的订单把货抢走了。

3. 前端:Vue 3 + Element Plus实现

3.1 前端工程化架构

前端我选的是Vue 3 + Vite + Element Plus + Pinia + Vue Router的组合。Vite取代Webpack后,开发环境的冷启动速度提升非常明显,不再有漫长等待的痛苦。用npm create vite@latest创建项目后,再安装对应依赖模块。

目录结构我延续了实际项目规范化的做法:

src/ ├── api/ # 接口统一管理 │ ├── auth.ts │ ├── inventory.ts │ └── stock.ts ├── assets/ # 静态资源 ├── components/ # 公共组件 ├── layout/ # 布局组件 ├── router/ # 路由配置 ├── stores/ # Pinia状态管理 ├── utils/ # 工具函数(axios封装、格式化等) └── views/ # 页面视图

每个模块的接口调用统一放在api目录下,页面组件不直接写axios地址。这样后端接口地址变动时只需要改一个文件,不用全局搜索替换。路径别名用@指向src目录,避免写一长串相对路径。

3.2 登录状态管理与动态路由

前端登录流程是整套系统的入口环节,处理不好后续全乱。用户在登录页输入账号密码,调用后端登录接口获取Token和用户信息,然后把Token存储到localStorage,用户信息存到Pinia中。路由守卫每次跳转前检查是否存在Token,没有Token就直接重定向到登录页。

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (to.path !== '/login' && !token) { next('/login'); } else if (to.path === '/login' && token) { next('/'); } else { next(); } });

动态路由是权限控制的关键实现。用户登录后,后端返回该用户拥有权限的菜单和按钮编码列表,前端遍历这个列表动态生成路由表,用router.addRoute注册到路由器中。这样用户没权限的模块,连路由都不存在,直接在入口拦截掉。

一个重要的细节:动态路由需要持久化。页面刷新后Vue实例重建,需要重新从后端拉取路由配置。所以我把用户权限信息在Pinia中做了缓存,刷新时先检查缓存,没有就从后端拉取,再走动态路由注册流程。

3.3 Axios封装与请求拦截器

Axios封装是前端项目的核心基建。我封装的主要目的有三个:统一附加Token、统一处理错误码、统一格式化响应数据。这里展示封装的核心逻辑:

const service = axios.create({ baseURL: '/api', timeout: 10000 }); service.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); service.interceptors.response.use( response => { const res = response.data; if (res.code === 200) { return res; } if (res.code === 401) { localStorage.removeItem('token'); router.push('/login'); return Promise.reject(new Error('登录已过期')); } ElMessage.error(res.message || '请求失败'); return Promise.reject(new Error(res.message)); }, error => { ElMessage.error('网络异常,请稍后重试'); return Promise.reject(error); } );

超时时间设置10秒,仓库系统在弱网环境下的操作频率不高,这个时间足够。响应拦截器中,碰到401就直接清Token跳登录页,这是很多项目都会忽略但极其重要的设计。如果没有这一步,用户Token过期后,每个接口都会报错,页面一堆红条提示,体验很差。

3.4 仓库作业页面的关键交互实现

仓库作业页面最核心的交互是单据录入。以入库单为例,用户需要先选择供应商和仓库,然后再明细区域动态增加物料行,填写数量、单价,还有弹窗搜索物料的功能。

物料选择弹窗是这类页面的重灾区,数据量大了以后,一次性拉全部物料会导致首屏卡死。我采用的是远程搜索方案:输入关键字后调用后端接口模糊查询,接口限制条数最多返回20条。这样既保证了用户体验,又不给服务器造成压力。

<el-select v-model="materialId" filterable remote :remote-method="searchMaterial" placeholder="请输入物料编码或名称"> <el-option v-for="item in materialOptions" :key="item.id" :label="item.materialCode + ' - ' + item.materialName" :value="item.id" /> </el-select>

库存盘点页面用到了高亮和编辑状态切换,用户在表格中直接输入实盘数量,系统自动对比账面数量和实盘数量,差异部分高亮显示。这一步前端用computed属性做实时计算就够,不用每个格子都请求后端,保存时再一次性提交差异数据,性能体验都好很多。

4. 前后端联调与生产环境部署

4.1 本地开发跨域与代理配置

前后端分离开发阶段,最大的拦路虎就是跨域。前端开发服务器跑在5173端口,后端API跑在5000端口,浏览器直接请求会触发CORS策略。我用的方案是前端代理,不是后端开启CORS。虽然开发时后端开启CORS也能解决,但生产环境部署后反而容易埋下安全隐患。

Vite配置代理非常方便,修改vite.config.ts:

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

这样前端代码里请求地址直接写/api/inbound/create,开发环境下Vite自动代理到http://localhost:5000/inbound/create。前端代码不用写完整域名,生产环境环境通过Nginx做类似代理,前端代码一行都不用改。

代码里有个容易踩的坑:changeOrigin必须设为true。不设置时后端接受到的请求头Host还是localhost:5173,某些后端框架对域名校验严格的话就会拒请求。我第一次联调时这就卡了半小时。

4.2 生产环境部署:不同方案的优缺点

生产部署是我这次最想重点分享的部分。前后端分离的部署,本质上是"静态资源交给Web服务器,API请求转发给后端进程"。我先后尝试了两种方案,最后混合使用。

第一种方案是前端构建后交给IIS托管,同时用URL Rewrite模块做反向代理。核心是web.config里的配置:

<configuration> <system.webServer> <rewrite> <rules> <rule name="vue-history-route" stopProcessing="true"> <match url=".*" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> </conditions> <action type="Rewrite" url="/index.html" /> </rule> <rule name="api-proxy" stopProcessing="true"> <match url="^api/(.*)" /> <action type="Rewrite" url="http://localhost:5000/{R:1}" /> </rule> </rules> </rewrite> </system.webServer> </configuration>

这个配置解决两个问题:Vue Router的history模式刷新页面时会404,所以把所有非文件请求一律重写到index.html;和后端接口通信,需要把/api/开头的请求转发到后端。

第二种方案是纯Nginx部署,前端构建后放在/usr/share/nginx/html,Nginx配置中做同样的history回退和API代理。Nginx的部署方式更轻量,对服务器资源占用小,适合Linux服务器上部署的场景。

建议网络条件复杂的公司内网,优先用IIS方案,和Windows域环境集成比较方便;独立部署在公网服务器就用Nginx,性能和灵活性更优。

5. 常见问题与部署避坑记录

5.1 前端Token过期后的体验优化

这个坑我印象特别深。最初Token过期后,用户正在填表,突然点击保存时弹出"登录已过期",然后被强制踢到登录页,填了一半的数据全丢了。这在仓库现场使用中几乎是灾难级的体验。

我后来的优化思路是:Token设置8小时过期,在有效期内用户在页面操作用不到重新登录,同时记录Token获取的时间戳。在路由守卫中,每隔一段时间检查Token剩余有效期,如果低于30分钟,就静默调用刷新Token接口,替换本地存储的旧Token。用户全程无感知,不用中断操作。

如果刷新Token接口也失败,说明用户确实登录太久了。此时不直接跳转,而是弹出一个可以"保存草稿并重新登录"的提示框,用户确认后把当前表单数据存到localStorage再跳登录页。这个改动后来在客户现场反馈特别好。

5.2 数据库并发导致库存异常的解决办法

多用户同时操作时,库存扣减经常发生超卖问题。我遇到的具体场景是:用户A和用户B同时看到库存剩10件,A提交出库8件,B也提交出库8件,两个请求同时通过了"库存充足"的判断,结果库存被扣成了负数。

这个问题光靠事务的默认隔离级别解决不了,我用的是乐观锁方案:库存表中加一个Version字段,更新时不仅检查库存数量,还要检查版本号是否和查询时一致。

var stock = await _context.Stocks .FirstOrDefaultAsync(s => s.MaterialId == materialId && s.WarehouseId == warehouseId); // 检查库存是否足够 if (stock.Quantity < quantity) return ResultDto.Fail("库存不足"); var rows = await _context.Database.ExecuteSqlRawAsync( "UPDATE Stock SET Quantity = Quantity - {0}, Version = Version + 1 " + "WHERE Id = {1} AND Version = {2}", quantity, stock.Id, stock.Version); if (rows == 0) return ResultDto.Fail("库存已被其他操作修改,请刷新后重试");

利用EF Core的ExecuteSqlRaw执行原生更新,用版本号做冲突检测,影响行数为0说明数据被其他操作修改过。这条语句能保证原子性,同时利用数据库行锁防止并发问题。类似的方案后来也应用在锁定库存和释放库存上。

5.3 EF Core性能优化:避免N+1查询

后端开发里最容易被忽视的性能问题是EF Core的N+1查询。典型场景是查询入库单列表时,主表一条记录,代码里循环读取明细和物料名称,数据量到几千条时接口直接卡死。

我后来统一用Include或ProjectTo来优化:

var orders = await _context.InboundOrders .Include(o => o.Items) .ThenInclude(i => i.Material) .Where(o => o.CreatedAt >= startDate && o.CreatedAt <= endDate) .OrderByDescending(o => o.CreatedAt) .Select(o => new InboundOrderListDto { Id = o.Id, OrderNo = o.OrderNo, SupplierName = o.Supplier.SupplierName, ItemCount = o.Items.Count, TotalAmount = o.Items.Sum(i => i.Quantity * i.Price) }) .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .ToListAsync();

EF Core的延迟加载是个陷阱,框架看起来"好用",但业务复杂后每条子查询都拖垮性能。我建议对列表查询一律用即时加载或投影,确保生成一条SQL语句。也可以用SQL日志记录功能,把EF生成的SQL打印出来,逐条分析是否有多余查询。

5.4 前端构建部署后的静态资源缓存问题

部署Vue项目后出现过一个状况:界面更新了,但用户浏览器还是显示旧版本。这是因为服务器对静态文件设置了强缓存,浏览器直接用了本地缓存,完全没发请求。

解决方案有两步:第一步,Vite构建时开启文件指纹,默认已经开了,文件名带哈希值;第二步,服务器设置缓存策略时,对带哈希的文件设置缓存永久有效,对index.html设置no-cache。

Nginx下的配置写法:

location /assets/ { expires 1y; add_header Cache-Control "public, immutable"; } location / { add_header Cache-Control "no-cache"; try_files $uri $uri/ /index.html; }

这样带哈希的文件每次更新后文件名变更,浏览器自然请求新文件;index.html每次都重新验证,保证能拿到最新的入口文件。实际部署时还有个小技巧:发布前清一下Nginx缓存目录,避免旧文件残留。

5.5 部署常见错误速查表

结合自己经历和帮朋友排查的case,汇总几条高频错误:

错误现象原因解决方法
前端页面刷新后404Vue Router history模式未配置回退配置IIS URL Rewrite或Nginx try_files
接口返回404前端代理路径与后端路由不一致检查代理rewrite规则是否正确
Token验证一直失败签发和验证的密钥不一致检查配置文件Jwt:Key是否一致,密钥长度需≥16字符
跨域报CORS错误前端代理未生效或生产环境少了代理层确认代理配置,生产环境用Nginx反代
数据库字段精度丢失EF Core默认decimal精度18,2Framework配置HasPrecision指定精度

这些问题的共同点是:解决方案不复杂,但排查看不到底层逻辑就会浪费很长时间。我通常建议部署前后先画一张部署架构图,明确每个请求的完整路径,再逐段排查,效率会高很多。

6. 项目扩展与二次开发建议

6.1 系统功能的横向扩展方向

这套仓库管理系统的核心功能已经跑通,但实际落地到企业环境时,通常还需要做一些扩展。我建议优先考虑这几个方向:多仓库多货位的精细化管理,在现有Warehouse表基础上增加货位表,库存记录精确到货位级别,这对仓库拣货和盘点很有价值;条码扫描支持,配合PDA或者手机摄像头,入出库时直接扫条码录入,效率提升非常明显;对接上游ERP系统,通过Web API或者消息队列同步物料档案和单据数据。

扩展时要注意保持现有的单据驱动、流水可追溯的设计理念。很多系统改着改着就开始直接改库存、跳过单据,短期看省事,长期看后患无穷。我做过的一次二次开发就是客户要求"做一个库存调整功能",我坚持把这个功能也做成标准单据流程,后续对账查数据时省了不少力气。

6.2 性能优化和监控配置

仓库系统在数据量增长后,性能和监控是关键。我在这里做了三件事:第一,给核心查询表加上复合索引,比如库存流水表上的(MaterialId, CreatedAt)索引,列表中高频筛选字段都建了索引;第二,后端启用响应压缩中间件,JSON响应体量直接减少60%以上,对弱网环境尤其友好;第三,接入Sentry做前端错误监控,以及Serilog做后端日志记录,出了问题能快速定位到具体请求和堆栈。

监控这块的建议是:物料和库存相关接口的日志必须包括操作人、操作时间和变更前后数据,因为一旦业务数据出问题,要能倒查是谁什么时候改的。这一步在仓库系统里不只是技术支持,还是财务审计层面的要求。

6.3 移动端适配与PDA手持终端

仓库作业中,操作员经常要推着车、搬着货,在电脑上操作很不方便。移动端适配是仓库管理系统落地时的加分项。我的做法是把核心作业页面(入库、出库、盘点)做成响应式布局,用Vant组件库做移动端适配。由于前后端分离,接口层完全不用改,只是前端多了一套移动端页面入口。

如果现场有条件用手持PDA,可以用WebView套壳方案,把移动端页面包装成PDA应用。条码枪这类设备一般都支持扫描后模拟键盘输入,聚焦到输入框后扫码,内容自动输入,然后用回车或Tab键触发查询,这套交互在Vue里实现成本很低,但现场体验提升非常大。

7. 写在最后的实操心得

做这套仓库管理系统前后花了将近两个月,最大的体会是:前后端分离架构的价值不在于技术新,而在于让团队协作和系统扩展变得干净。后端只操心业务逻辑和数据处理,前端只关心交互和展示,接口对清楚了,两边基本可以并行开发不互相卡脖子。

我在实际开发中踩到最深的坑,一个是JWT密钥长度导致部署后认证失败,另一个是EF Core延迟加载在数据量上来后的性能灾难。这两个问题如果能在设计阶段就规避,后面能省下大量排查时间。

最后再分享一个小技巧:前后端联调时,强烈建议把Postman或者Apifox的接口环境配置好,把所有接口按模块分好目录。我用的流程是后端写完一个控制器就更新接口文档和测试用例,前端联调时直接导入测试集合,效率比一边开发一边联调高很多。这套项目本身就是一个完整的起点,里面的设计模式、目录结构、错误处理方式都可以直接复用,希望你能在这个基础上做出更适合自己业务的版本。

本文还有配套的精品资源,点击获取

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

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

立即咨询