编辑 | blame | 历史 | 原始文档

仓储物流模块前端文档

一、功能概述

将MES模块的仓库管理提取为独立的一级菜单"仓储物流",并实现:
- ERP采购入库审核 → MES库存同步增加
- ERP销售出库审核 → MES库存同步减少
- 仓库级别审批流程配置(每个仓库可独立启用/禁用审批)


二、SQL脚本执行顺序

# 1. 菜单调整(将仓库管理提取为一级菜单)
sql/mysql/wls_menu_init.sql

# 2. 数据库初始化(审批配置表 + 单据BPM字段)
sql/mysql/wls_database_init.sql

# 3. ERP仓库与MES仓库映射表
sql/mysql/erp_mes_warehouse_mapping.sql

三、菜单结构

3.1 新菜单结构

仓储物流(一级菜单,id=5780)
├── 仓库设置
├── 库存现有量
├── 采购入库
├── 到货通知
├── 采购退货
├── 生产领料
├── 生产退料
├── 产品入库
├── 发货通知
├── 销售出库
├── 销售退货
├── 转移调拨
├── 库存盘点
├── 装箱管理
├── 条码配置
├── 赋能管理
├── SN码
├── 外协发料
├── 外协入库
├── 杂项入库
├── 杂项出库
├── 审批配置(新增)
└── 仓库映射(新增,在ERP菜单下)

3.2 前端路由配置

方案一:路径别名(推荐)

// vite.config.js
export default defineConfig({
  resolve: {
    alias: {
      '@/views/wls': path.resolve(__dirname, 'src/views/mes/wm')
    }
  }
})

方案二:复制组件

src/views/mes/wm 目录复制为 src/views/wls


四、新增页面

4.1 审批配置页面

路径views/wls/approval/config/index.vue

功能
1. 仓库列表展示
2. 每个仓库可配置入库/出库/调拨/盘点是否启用审批
3. 选择审批流程(从BPM流程列表)

<template>
  <div class="app-container">
    <!-- 搜索栏 -->
    <el-form :inline="true">
      <el-form-item label="仓库">
        <el-select v-model="queryParams.warehouseId" placeholder="请选择仓库" clearable>
          <el-option v-for="warehouse in warehouseList" :key="warehouse.id"
            :label="warehouse.warehouseName" :value="warehouse.id" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleQuery">搜索</el-button>
      </el-form-item>
    </el-form>

    <!-- 配置表格 -->
    <el-table :data="configList" v-loading="loading">
      <el-table-column prop="warehouseName" label="仓库名称" width="150" />
      <el-table-column label="入库审批" width="120">
        <template #default="{ row }">
          <el-switch v-model="row.inboundEnabled"
            @change="handleApprovalChange(row, 'INBOUND', $event)" />
        </template>
      </el-table-column>
      <el-table-column label="出库审批" width="120">
        <template #default="{ row }">
          <el-switch v-model="row.outboundEnabled"
            @change="handleApprovalChange(row, 'OUTBOUND', $event)" />
        </template>
      </el-table-column>
      <el-table-column label="调拨审批" width="120">
        <template #default="{ row }">
          <el-switch v-model="row.transferEnabled"
            @change="handleApprovalChange(row, 'TRANSFER', $event)" />
        </template>
      </el-table-column>
      <el-table-column label="盘点审批" width="120">
        <template #default="{ row }">
          <el-switch v-model="row.checkEnabled"
            @change="handleApprovalChange(row, 'CHECK', $event)" />
        </template>
      </el-table-column>
      <el-table-column label="操作" width="100">
        <template #default="{ row }">
          <el-button link type="primary" @click="handleConfigProcess(row)">配置流程</el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 流程配置弹窗 -->
    <el-dialog v-model="processDialogVisible" title="配置审批流程" width="500px">
      <el-form label-width="100px">
        <el-form-item label="业务类型">
          <el-select v-model="currentConfig.bizType">
            <el-option label="入库" value="INBOUND" />
            <el-option label="出库" value="OUTBOUND" />
            <el-option label="调拨" value="TRANSFER" />
            <el-option label="盘点" value="CHECK" />
          </el-select>
        </el-form-item>
        <el-form-item label="审批流程">
          <el-select v-model="currentConfig.processDefinitionKey" placeholder="请选择流程">
            <el-option v-for="process in processList" :key="process.key"
              :label="process.name" :value="process.key" />
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="processDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleSaveProcessConfig">保存</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import * as ApprovalApi from '@/api/wms/approval'

const warehouseList = ref([])
const configList = ref([])
const processList = ref([])
const loading = ref(false)
const queryParams = ref({ warehouseId: null })

const processDialogVisible = ref(false)
const currentConfig = ref({ warehouseId: null, bizType: null, processDefinitionKey: null })

// 加载仓库列表(从仓库设置模块获取)
const loadWarehouseList = async () => {
  // 调用仓库设置接口获取列表
}

// 加载审批配置
const loadConfigList = async (warehouseId) => {
  if (!warehouseId) return
  loading.value = true
  try {
    configList.value = await ApprovalApi.getApprovalConfigList(warehouseId)
  } finally {
    loading.value = false
  }
}

// 切换审批开关
const handleApprovalChange = async (row, bizType, enabled) => {
  await ApprovalApi.saveApprovalConfig({
    warehouseId: row.warehouseId,
    bizType: bizType,
    approvalEnabled: enabled
  })
}

// 配置流程
const handleConfigProcess = async (row) => {
  currentConfig.value = { warehouseId: row.warehouseId, bizType: null, processDefinitionKey: null }
  // 加载可用流程列表
  processList.value = await ApprovalApi.getAvailableProcessList(row.warehouseId, 'INBOUND')
  processDialogVisible.value = true
}

// 保存流程配置
const handleSaveProcessConfig = async () => {
  await ApprovalApi.saveApprovalConfig(currentConfig.value)
  processDialogVisible.value = false
  loadConfigList(queryParams.value.warehouseId)
}

onMounted(() => {
  loadWarehouseList()
})
</script>

4.2 仓库映射配置页面

路径views/erp/warehouse/mapping/index.vue(放在ERP仓库管理下)

功能
1. 配置ERP仓库与MES仓库的映射关系
2. 支持新增、修改、删除映射

<template>
  <div class="app-container">
    <!-- 搜索栏 -->
    <el-form :inline="true">
      <el-form-item label="ERP仓库">
        <el-select v-model="queryParams.erpWarehouseId" placeholder="请选择ERP仓库" clearable>
          <el-option v-for="warehouse in erpWarehouseList" :key="warehouse.id"
            :label="warehouse.name" :value="warehouse.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="MES仓库">
        <el-select v-model="queryParams.mesWarehouseId" placeholder="请选择MES仓库" clearable>
          <el-option v-for="warehouse in mesWarehouseList" :key="warehouse.id"
            :label="warehouse.warehouseName" :value="warehouse.id" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleQuery">搜索</el-button>
        <el-button type="primary" @click="handleAdd">新增</el-button>
      </el-form-item>
    </el-form>

    <!-- 映射表格 -->
    <el-table :data="mappingList" v-loading="loading">
      <el-table-column prop="erpWarehouseName" label="ERP仓库" />
      <el-table-column prop="mesWarehouseName" label="MES仓库" />
      <el-table-column prop="status" label="状态">
        <template #default="{ row }">
          <el-tag :type="row.status === 1 ? 'success' : 'danger'">
            {{ row.status === 1 ? '启用' : '禁用' }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column label="操作" width="150">
        <template #default="{ row }">
          <el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
          <el-button link type="danger" @click="handleDelete(row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 分页 -->
    <el-pagination v-model:current-page="queryParams.pageNo" v-model:page-size="queryParams.pageSize"
      :total="total" @change="handleQuery" />

    <!-- 新增/编辑弹窗 -->
    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px">
      <el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
        <el-form-item label="ERP仓库" prop="erpWarehouseId">
          <el-select v-model="formData.erpWarehouseId" placeholder="请选择ERP仓库">
            <el-option v-for="warehouse in erpWarehouseList" :key="warehouse.id"
              :label="warehouse.name" :value="warehouse.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="MES仓库" prop="mesWarehouseId">
          <el-select v-model="formData.mesWarehouseId" placeholder="请选择MES仓库">
            <el-option v-for="warehouse in mesWarehouseList" :key="warehouse.id"
              :label="warehouse.warehouseName" :value="warehouse.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="状态">
          <el-radio-group v-model="formData.status">
            <el-radio :value="1">启用</el-radio>
            <el-radio :value="0">禁用</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleSubmit">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import * as MappingApi from '@/api/erp/warehouseMapping'
import { ElMessage, ElMessageBox } from 'element-plus'

const loading = ref(false)
const total = ref(0)
const mappingList = ref([])
const queryParams = reactive({
  pageNo: 1,
  pageSize: 10,
  erpWarehouseId: null,
  mesWarehouseId: null
})

const dialogVisible = ref(false)
const dialogTitle = ref('')
const formRef = ref()
const formData = reactive({
  id: null,
  erpWarehouseId: null,
  mesWarehouseId: null,
  status: 1
})
const formRules = {
  erpWarehouseId: [{ required: true, message: '请选择ERP仓库', trigger: 'change' }],
  mesWarehouseId: [{ required: true, message: '请选择MES仓库', trigger: 'change' }]
}

// ERP仓库列表(从ERP仓库接口获取)
const erpWarehouseList = ref([])
// MES仓库列表(从MES仓库接口获取)
const mesWarehouseList = ref([])

// 加载映射列表
const loadMappingList = async () => {
  loading.value = true
  try {
    const res = await MappingApi.getMappingPage(queryParams)
    mappingList.value = res.list
    total.value = res.total
  } finally {
    loading.value = false
  }
}

// 新增
const handleAdd = () => {
  dialogTitle.value = '新增仓库映射'
  Object.assign(formData, { id: null, erpWarehouseId: null, mesWarehouseId: null, status: 1 })
  dialogVisible.value = true
}

// 编辑
const handleEdit = (row) => {
  dialogTitle.value = '编辑仓库映射'
  Object.assign(formData, row)
  dialogVisible.value = true
}

// 提交
const handleSubmit = async () => {
  await formRef.value.validate()
  if (formData.id) {
    await MappingApi.updateMapping(formData)
    ElMessage.success('更新成功')
  } else {
    await MappingApi.createMapping(formData)
    ElMessage.success('创建成功')
  }
  dialogVisible.value = false
  loadMappingList()
}

// 删除
const handleDelete = async (id) => {
  await ElMessageBox.confirm('确定删除该映射?', '提示', { type: 'warning' })
  await MappingApi.deleteMapping(id)
  ElMessage.success('删除成功')
  loadMappingList()
}

onMounted(() => {
  loadMappingList()
  // 加载仓库列表
})
</script>

五、API接口

5.1 审批配置接口

方法 路径 说明
GET /mes/wms/approval/config/get 获取单个审批配置
GET /mes/wms/approval/config/list 获取仓库所有审批配置
POST /mes/wms/approval/config/save 保存审批配置
GET /mes/wms/approval/process-list 获取可用审批流程

请求参数详情:

GET /mes/wms/approval/config/get

参数 类型 必填 说明
warehouseId Long 仓库ID
bizType String 业务类型(INBOUND/OUTBOUND/TRANSFER/CHECK)

响应:

{
  "code": 0,
  "data": {
    "id": 1,
    "warehouseId": 100,
    "bizType": "INBOUND",
    "approvalEnabled": true,
    "processDefinitionKey": "wms_inbound_approve",
    "categoryCode": "wms_approve"
  }
}

POST /mes/wms/approval/config/save

请求体:

{
  "warehouseId": 100,
  "bizType": "INBOUND",
  "approvalEnabled": true,
  "processDefinitionKey": "wms_inbound_approve"
}

GET /mes/wms/approval/process-list

参数 类型 必填 说明
warehouseId Long 仓库ID
bizType String 业务类型

响应:

{
  "code": 0,
  "data": [
    { "key": "wms_inbound_approve", "name": "入库审批流程" },
    { "key": "wms_common_approve", "name": "通用审批流程" }
  ]
}

5.2 仓库映射接口

方法 路径 说明
GET /erp/warehouse-mapping/page 分页查询映射列表
GET /erp/warehouse-mapping/get 获取单个映射
POST /erp/warehouse-mapping/create 新增映射
PUT /erp/warehouse-mapping/update 更新映射
DELETE /erp/warehouse-mapping/delete 删除映射

请求参数详情:

GET /erp/warehouse-mapping/page

参数 类型 必填 说明
pageNo Integer 页码
pageSize Integer 每页数量
erpWarehouseId Long ERP仓库ID
mesWarehouseId Long MES仓库ID
status Integer 状态

响应:

{
  "code": 0,
  "data": {
    "list": [
      {
        "id": 1,
        "erpWarehouseId": 1,
        "erpWarehouseName": "主仓库",
        "mesWarehouseId": 100,
        "mesWarehouseName": "总仓",
        "status": 1
      }
    ],
    "total": 1
  }
}

POST /erp/warehouse-mapping/create

请求体:

{
  "erpWarehouseId": 1,
  "mesWarehouseId": 100,
  "status": 1
}

六、API文件创建

6.1 审批配置API

创建文件:src/api/wms/approval/index.js

import request from '@/utils/request'

// 获取审批配置
export function getApprovalConfig(warehouseId, bizType) {
  return request({
    url: '/mes/wms/approval/config/get',
    method: 'get',
    params: { warehouseId, bizType }
  })
}

// 获取仓库所有审批配置
export function getApprovalConfigList(warehouseId) {
  return request({
    url: '/mes/wms/approval/config/list',
    method: 'get',
    params: { warehouseId }
  })
}

// 保存审批配置
export function saveApprovalConfig(data) {
  return request({
    url: '/mes/wms/approval/config/save',
    method: 'post',
    data: data
  })
}

// 获取可用审批流程列表
export function getAvailableProcessList(warehouseId, bizType) {
  return request({
    url: '/mes/wms/approval/process-list',
    method: 'get',
    params: { warehouseId, bizType }
  })
}

6.2 仓库映射API

创建文件:src/api/erp/warehouseMapping/index.js

import request from '@/utils/request'

// 分页查询映射列表
export function getMappingPage(params) {
  return request({
    url: '/erp/warehouse-mapping/page',
    method: 'get',
    params: params
  })
}

// 获取单个映射
export function getMapping(id) {
  return request({
    url: '/erp/warehouse-mapping/get',
    method: 'get',
    params: { id }
  })
}

// 新增映射
export function createMapping(data) {
  return request({
    url: '/erp/warehouse-mapping/create',
    method: 'post',
    data: data
  })
}

// 更新映射
export function updateMapping(data) {
  return request({
    url: '/erp/warehouse-mapping/update',
    method: 'put',
    data: data
  })
}

// 删除映射
export function deleteMapping(id) {
  return request({
    url: '/erp/warehouse-mapping/delete',
    method: 'delete',
    params: { id }
  })
}

七、数据映射配置

7.1 ERP产品与MDM物料

通过条码(barCode)关联:
- ERP产品的barCode字段值 = MDM物料的barCode字段值
- 系统自动通过barCode匹配,无需额外配置表

7.2 ERP仓库与MES仓库

需要在仓库映射页面配置映射关系:

ERP仓库 MES仓库 说明
主仓库(ID=1) 总仓(ID=100) 默认映射

注意:ERP仓库必须配置映射后,ERP采购入库/销售出库审核才能同步MES库存。


八、业务流程

8.1 采购入库流程(ERP → MES)

ERP采购入库单创建
  ↓
ERP采购入库单审核
  ↓
调用 WmsStockApi.purchaseInbound()
  ↓
查找ERP仓库 → MES仓库映射
  ↓
查找ERP产品(barCode) → MDM物料(barCode)
  ↓
创建MES库存事务记录
  ↓
MES库存增加

8.2 销售出库流程(ERP → MES)

ERP销售出库单创建
  ↓
ERP销售出库单审核
  ↓
调用 WmsStockApi.salesOutbound()
  ↓
查找仓库映射 + 产品映射
  ↓
创建MES库存事务记录(数量为负)
  ↓
MES库存减少

8.3 审批流程(可选)

MES入库单创建(草稿)
  ↓
检查仓库审批配置 → 是否需要审批?
  ↓ (需要审批)
提交审批 → BPM流程启动
  ↓
审批通过 → 单据状态更新 → 执行入库
  ↓ (不需要审批)
直接执行入库

九、注意事项

  1. 执行顺序:先执行SQL脚本,再部署后端,最后调整前端
  2. 映射配置:ERP仓库必须先配置映射才能同步MES库存
  3. 权限前缀
  • 审批配置:mes:wm-warehouse:querymes:wm-warehouse:update
  • 仓库映射:erp:warehouse:queryerp:warehouse:updateerp:warehouse:delete
  1. 审批流程:需在BPM管理后台创建"仓库审核(wms_approve)"分类下的流程
  2. 物料条码:MDM物料必须填写barCode,且与ERP产品barCode一致

十、测试验证

10.1 功能测试

  1. 菜单验证:登录系统,确认"仓储物流"作为一级菜单显示
  2. 映射配置:在ERP仓库管理下,配置ERP仓库与MES仓库映射
  3. 采购入库
  • ERP创建采购入库单 → 审核
  • 查询MES库存现有量 → 确认库存增加
  1. 销售出库
  • ERP创建销售出库单 → 审核
  • 查询MES库存现有量 → 确认库存减少
  1. 审批流程
  • 配置仓库入库审批 → 启用
  • 创建MES入库单 → 提审批 → 审批通过 → 状态变更

10.2 异常场景

  1. 未配置仓库映射 → ERP审核时报错提示"仓库映射不存在"
  2. 物料条码不匹配 → ERP审核时报错提示"物料不存在"
  3. 库存不足 → 销售出库时报错提示"库存不足"