- 仓库管理(mom-frontend-vue2): 新增简易标签打印,不依赖MinIO,使用前端qrcode+window.print - 工作站(erp-frontend-vue): 新增WorkstationLabelPrint组件,支持批量打印工作站标签 - 生产订单: handlePrint改用getProcessTasksByWorkorder,从工艺路线获取工序数据,解决无pro_task时打印无数据问题 Made-with: Cursor
铭奕 ERP 前端(erp-frontend-vue)
基于 Vue 3 + TypeScript + Vite + Element Plus 实现的 ERP Web 前端,覆盖主数据、销售、采购、生产计划、仓储、系统管理等业务模块。
本文档用于说明项目整体架构和约定,方便后续功能迭代、Bug 修复以及测试用例编写。
1. 技术栈与关键依赖
- 框架:Vue 3(
<script setup>语法) - 语言:TypeScript(严格类型约束,配合
vue-tsc做构建时类型检查) - 构建工具:Vite
- UI 组件库:Element Plus(含图标集
@element-plus/icons-vue) - 路由:Vue Router 4
- HTTP 客户端:Axios(统一封装在
src/api/request.ts) - 测试:Vitest + @vue/test-utils + jsdom
主要脚本(见 package.json):
npm run dev:启动开发服务器npm run build:先运行vue-tsc -b做类型检查,再用 Vite 打生产包npm run preview:预览生产构建npm run test:一次性运行所有 Vitest 用例npm run test:watch:watch 模式运行 Vitest
2. 目录结构概览
仅列出与前端业务开发关系最密切的部分:
src/main.ts:应用入口,创建 Vue 应用,注册 Element Plus 图标,挂载路由与权限守卫src/App.vue:根组件src/router/index.ts:路由配置(按业务域划分路由树)src/permission.ts:路由前置守卫,处理登录校验、用户信息加载、白名单路由等src/layout/index.vue:主布局(侧边菜单 + 顶部导航 + 面包屑 + 内容区域)
src/stores/user.ts:用户状态与权限校验(登录、退出、获取用户信息等)
src/api/request.ts:Axios 实例及请求/响应拦截器- 业务 API 模块(每个文件对应一个业务域,内部定义 TypeScript 接口与请求方法),例如:
auth.ts:认证登录/获取用户信息productionPlan.ts:生产计划单mbom.ts:物料清单 / MRPpurchasePlan.ts:采购计划purchaseOrder.ts:采购订单salesOrder.ts:销售订单deliver.ts/invoice.ts/saleback.ts:销售相关单据masterdata/*:物料、计量单位、车间、工作站等主数据system/*:组织架构(部门、岗位、角色、用户)warehouse/*:生产领料相关接口rd/ebom.ts:研发 BOM
src/views/:按业务域划分的页面组件MasterData/:主数据Sales/:销售管理与销售报表Purchasing/:采购管理与采购报表Production/:生产计划、MBOM/MRP、采购计划、生产报表Warehouse/Issue/:生产领料单RD/Ebom/:研发 EBOMSystem/:用户、角色、部门、岗位- 每个业务通常使用:
index.vue:列表/查询页form.vue:单据编辑/查看页
src/utils/:通用工具(如 token 存取等)
3. 运行与构建
# 安装依赖
npm install
# 开发调试
npm run dev
# 类型检查 + 构建生产包
npm run build
# 本地预览生产包
npm run preview
# 运行单元测试
npm run test
建议在提交代码前至少保证
npm run build通过,以防止 TypeScript 类型错误和明显的构建问题进入主分支。
4. 前端架构设计
4.1 路由与布局
- 路由配置集中在
src/router/index.ts,采用按业务域分组的嵌套路由结构:/sales/*:销售管理模块/purchasing/*:采购管理模块/production/*:生产计划模块/masterdata/*:主数据维护/system/*:系统管理/warehouse/*:仓储/生产领料/rd/*:研发
- 所有业务路由均以
Layout作为父路由,实现统一的侧边菜单 + 顶部导航 + 面包屑结构。 - 每个路由都带有
meta.title,用于:- 页面标题(
permission.ts中设置document.title) - 面包屑显示
- 侧边菜单高亮匹配
- 页面标题(
4.2 权限与认证流程
相关文件:
src/stores/user.ts:维护用户 token、基本信息、角色、权限等src/utils/auth.ts:token 的本地存储读写src/api/auth.ts:登录、获取用户信息、注销src/permission.ts:全局路由守卫
核心逻辑:
- 路由进入前,
permission.ts会检查:- 当前是否在白名单(如
/login等) - 本地是否存在 token
- 当前是否在白名单(如
- 若有 token 但
userStore.state.userInfo.roles为空,则会调用userStore.getUserInfo()拉取用户信息。 - 若认证失败或接口错误,会调用
fedLogout清理本地状态,并跳转登录页。 - 在开发模式下支持
DEV_SKIP_AUTH(通过import.meta.env控制),方便后端未就绪时调试页面。
4.3 API 封装与数据访问
统一入口:src/api/request.ts
- 使用 Axios 创建单例
request,通过请求拦截器自动处理:- 按 URL 前缀自动拼接
/erp、/mes、/system等前缀 - 附加
Authorization: Bearer <token>头
- 按 URL 前缀自动拼接
- 响应拦截器统一处理:
code === 401:弹出重新登录提示,清理 token 并跳转登录- 其他非 200 code:使用
ElMessage.error提示 - 成功时统一返回包裹后的
res对象(通常包含data、rows、total等)
业务 API 模块模式(以 src/api/productionPlan.ts 为例):
- 在文件顶部定义接口类型,如
ProductionPlan、PlanLine等。 - 导出对应的请求方法:
getProductionPlanList(params: PlanQuery)createProductionPlan(data: Partial<ProductionPlan>)approveProductionPlan(planId: number)等。
- 列表接口通常约定返回
{ rows: T[]; total: number }结构,方便列表分页展示。
建议:新增接口时先定义好 TypeScript 接口类型,再在页面组件中通过
type Xxx引用,保持前后端字段的一致性与可维护性。
4.4 状态管理
当前项目使用 轻量级的自实现 store(并未引入 Pinia/Vuex),例如:
src/stores/user.ts使用reactive维护用户状态,并暴露:loginAction/logoutAction/getUserInfohasRole、hasPermission做按钮级权限控制
在组件中通过:
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
来访问和修改全局用户状态。
4.5 业务模块划分
按典型业务流程进行模块化:
- 主数据(MasterData):物料、分类、计量单位、车间/工作站等基础资料。
- 销售(Sales):客户、销售订单、发货通知单、开票结算单、退货通知单以及相关报表。
- 采购(Purchasing):供应商、采购订单、到货单、发票、退货及采购执行报表。
- 生产(Production):生产计划单、MBOM/MRP 运算、采购计划及生产执行报表。
- 仓储/生产领料(Warehouse/Issue):工单领料单及出库明细。
- 研发(RD/Ebom):工程 BOM(EBOM)维护。
- 系统管理(System):用户、角色、部门、岗位等组织架构。
各模块页面组件基本遵循:
index.vue:列表、搜索、分页、批量操作form.vue:单据新增/编辑/查看(通常由路由new/edit/:id/view/:id复用)
5. 开发约定与最佳实践
-
类型优先
- 所有接口数据结构优先在
src/api/*中通过interface/type定义。 - 组件中通过
import type Xxx from '@/api/xxx'复用类型,避免魔法字符串和隐式any。
- 所有接口数据结构优先在
-
单据模式一致
- 列表页负责查询、分页、批量操作。
- 单据表单页负责单据的增删改查、审批、导出、引入等。
- 路由命名尽量统一(如
XxxList、XxxNew、XxxEdit、XxxView)。
-
API 调用约定
- 列表类:
getXxxList(params)/getXxxDocList(params)/getXxxSummary(params)。 - 单据类:
getXxx(id)/createXxx(data)/updateXxx(data)/deleteXxx(id)/approveXxx(id)。 - 导出类:
exportXxx(params)返回Blob,前端统一通过URL.createObjectURL下载。
- 列表类:
-
错误处理
- 全局拦截器负责大部分 HTTP 错误提示。
- 业务逻辑错误(如必填字段缺失、状态不允许操作)应在组件内通过
ElMessage或ElMessageBox友好提示。
-
国际化与文案
- 当前以中文为主,业务文案尽量统一、简洁。
- 后续如需多语言,可在布局和组件中收口文案位置,方便替换。
6. 新功能迭代指南
以新增一个简单业务模块为例,推荐步骤如下:
-
定义后端接口与数据结构
- 与后端约定好 URL、请求方式以及返回结构。
- 在
src/api/<module>.ts中新增对应的接口类型定义与请求函数。
-
新增页面组件
- 在
src/views/<Module>/<Feature>/下创建:index.vue:列表页form.vue:单据表单页(可根据是否需要拆分为 new/edit/view 来复用)
- 使用
<script setup lang="ts">,并引入上一步定义的类型。
- 在
-
注册路由
- 在
src/router/index.ts中对应业务分组下新增子路由:- 列表:
/module/feature - 新增:
/module/feature/new - 编辑:
/module/feature/edit/:id - 查看:
/module/feature/view/:id
- 列表:
- 配置好
name和meta.title,以便面包屑与标题显示。
- 在
-
加入侧边菜单
- 在
src/layout/index.vue左侧菜单中增加对应菜单项,index 与路由 path 对齐。
- 在
-
权限控制(如需要)
- 后端返回对应的角色/权限字符串。
- 在前端根据
userStore.hasRole/hasPermission决定是否展示按钮或入口。
-
编写/更新测试
- 对复杂的计算逻辑或数据加工函数,建议抽到独立模块并使用 Vitest 编写单元测试。
- 对关键表单组件或流程,使用
@vue/test-utils+ Vitest 编写基础渲染和交互用例。
-
自测与提交
- 运行
npm run dev手动验证主要流程。 - 运行
npm run test和npm run build确认无测试失败与类型错误。
- 运行
7. Bug 修复与调试建议
-
快速定位问题
- TS 编译错误:优先通过
npm run build或vue-tsc -b定位。 - 运行时错误:查看浏览器控制台和网络请求(Network)日志。
- TS 编译错误:优先通过
-
保持类型一致
- 修复字段名或结构变更时,同步更新:
- 对应的 API 接口类型定义(
src/api/*) - 所有使用该类型的组件
- 对应的 API 接口类型定义(
- 修复字段名或结构变更时,同步更新:
-
避免静默失败
- catch 到错误时,优先使用
ElMessage.error告知用户(同时console.error方便开发排查)。
- catch 到错误时,优先使用
-
回归检查
- 对涉及核心单据(生产计划、采购计划、销售订单、领料单等)的改动,建议:
- 至少完成一次完整的“从主数据 → 业务单据 → 报表/导出”的端到端手工验证。
- 补充或更新相应的单元测试。
- 对涉及核心单据(生产计划、采购计划、销售订单、领料单等)的改动,建议:
8. 测试用例编写建议
项目已集成 Vitest 与 Vue Test Utils,可按以下思路补充测试:
-
纯逻辑/工具函数
- 放在独立的
utils或helpers文件中。 - 使用 Vitest 编写函数级单元测试,覆盖数字计算、状态映射等逻辑。
- 放在独立的
-
API 封装
- 使用 Vitest 的 mocking 能力(如
vi.mock('axios'))对 API 模块进行单元测试,确保在不同返回结构下都有正确行为。
- 使用 Vitest 的 mocking 能力(如
-
组件/页面
- 使用
@vue/test-utils挂载组件,测试:- 基本渲染是否正常(必需字段/按钮是否存在)
- 关键交互(点击按钮后是否发出预期事件或调用 API)
- 表单校验逻辑(缺失必填项时是否提示)
- 使用
-
回归测试
- 对已修复的 Bug,尽量补一条对应的测试用例,避免同类问题再次出现。
如需在后续迭代中扩展架构(例如引入 Pinia、国际化、多布局支持等),可以在本 README 继续追加对应的小节,保持文档与实现同步更新。