feat(mes): 迁移 batchtrace

pull/350/head
YunaiV 2026-05-29 19:31:13 +08:00
parent 8192bb4777
commit 8028d8b6ef
5 changed files with 472 additions and 0 deletions

View File

@ -0,0 +1,67 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmBatchApi {
/** MES 批次 */
export interface Batch {
id?: number; // 批次 ID
code?: string; // 批次编码
itemId?: number; // 物料 ID
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
itemSpecification?: string; // 规格型号
unitName?: string; // 单位名称
produceDate?: Date; // 生产日期
expireDate?: Date; // 有效期
receiptDate?: Date; // 入库日期
vendorId?: number; // 供应商 ID
vendorCode?: string; // 供应商编码
vendorName?: string; // 供应商名称
clientId?: number; // 客户 ID
clientCode?: string; // 客户编码
clientName?: string; // 客户名称
purchaseOrderCode?: string; // 采购订单编号
salesOrderCode?: string; // 销售订单编号
workOrderId?: number; // 生产工单 ID
workOrderCode?: string; // 生产工单编号
taskId?: number; // 生产任务 ID
taskCode?: string; // 生产任务编号
workstationId?: number; // 工作站 ID
workstationCode?: string; // 工作站编码
toolId?: number; // 工具 ID
toolCode?: string; // 工具编号
moldId?: number; // 模具 ID
lotNumber?: string; // 生产批号
qualityStatus?: number; // 质量状态
remark?: string; // 备注
}
}
/** 查询批次详情 */
export function getBatch(id: number) {
return requestClient.get<MesWmBatchApi.Batch>(`/mes/wm/batch/get?id=${id}`);
}
/** 查询批次分页 */
export function getBatchPage(params: PageParam) {
return requestClient.get<PageResult<MesWmBatchApi.Batch>>(
'/mes/wm/batch/page',
{ params },
);
}
/** 批次向前追溯 */
export function getForwardBatchList(code: string) {
return requestClient.get<MesWmBatchApi.Batch[]>('/mes/wm/batch/forward-list', {
params: { code },
});
}
/** 批次向后追溯 */
export function getBackwardBatchList(code: string) {
return requestClient.get<MesWmBatchApi.Batch[]>(
'/mes/wm/batch/backward-list',
{ params: { code } },
);
}

View File

@ -0,0 +1,169 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmBatchApi } from '#/api/mes/wm/batch';
import { markRaw } from 'vue';
import MdClientSelect from '#/views/mes/md/client/components/md-client-select.vue';
import MdItemSelect from '#/views/mes/md/item/components/md-item-select.vue';
import MdVendorSelect from '#/views/mes/md/vendor/components/md-vendor-select.vue';
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'code',
label: '批次号',
component: 'Input',
componentProps: {
allowClear: true,
placeholder: '请输入批次号',
},
},
{
fieldName: 'itemId',
label: '产品物料',
component: markRaw(MdItemSelect),
componentProps: {
placeholder: '请选择产品物料',
},
},
{
fieldName: 'vendorId',
label: '供应商',
component: markRaw(MdVendorSelect),
componentProps: {
placeholder: '请选择供应商',
},
},
{
fieldName: 'clientId',
label: '客户',
component: markRaw(MdClientSelect),
componentProps: {
placeholder: '请选择客户',
},
},
{
fieldName: 'salesOrderCode',
label: '销售订单编号',
component: 'Input',
componentProps: {
allowClear: true,
placeholder: '请输入销售订单编号',
},
},
{
fieldName: 'purchaseOrderCode',
label: '采购订单编号',
component: 'Input',
componentProps: {
allowClear: true,
placeholder: '请输入采购订单编号',
},
},
];
}
/** 列表的字段 */
export function useGridColumns(): VxeTableGridOptions<MesWmBatchApi.Batch>['columns'] {
return [
{
field: 'code',
title: '批次编号',
minWidth: 160,
},
{
field: 'itemCode',
title: '产品物料编码',
minWidth: 140,
},
{
field: 'itemName',
title: '产品物料名称',
minWidth: 160,
},
{
field: 'itemSpecification',
title: '规格型号',
minWidth: 140,
},
{
field: 'unitName',
title: '单位',
width: 80,
},
{
field: 'vendorCode',
title: '供应商编码',
minWidth: 140,
},
{
field: 'vendorName',
title: '供应商名称',
minWidth: 160,
},
{
field: 'clientCode',
title: '客户编码',
minWidth: 140,
},
{
field: 'clientName',
title: '客户名称',
minWidth: 160,
},
{
field: 'salesOrderCode',
title: '销售订单编号',
minWidth: 160,
},
{
field: 'purchaseOrderCode',
title: '采购订单编号',
minWidth: 160,
},
{
title: '操作',
width: 110,
fixed: 'right',
slots: { default: 'actions' },
},
];
}
/** 追溯明细列表的字段 */
export function useTraceGridColumns(): VxeTableGridOptions<MesWmBatchApi.Batch>['columns'] {
return [
{
field: 'workOrderCode',
title: '生产工单号',
width: 160,
},
{
field: 'code',
title: '批次编号',
minWidth: 160,
},
{
field: 'itemCode',
title: '产品物料编码',
minWidth: 140,
},
{
field: 'itemName',
title: '产品物料名称',
minWidth: 160,
},
{
field: 'itemSpecification',
title: '规格型号',
minWidth: 140,
},
{
field: 'unitName',
title: '单位',
width: 80,
},
];
}

View File

@ -0,0 +1,81 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmBatchApi } from '#/api/mes/wm/batch';
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getBatchPage } from '#/api/mes/wm/batch';
import { useGridColumns, useGridFormSchema } from './data';
import TraceDetail from './modules/trace-detail.vue';
const [DetailModal, detailModalApi] = useVbenModal({
connectedComponent: TraceDetail,
destroyOnClose: true,
});
/** 打开批次追溯详情 */
function handleTrace(row: MesWmBatchApi.Batch) {
detailModalApi.setData(row).open();
}
const [Grid] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getBatchPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<MesWmBatchApi.Batch>,
});
</script>
<template>
<Page auto-content-height>
<template #doc>
<DocAlert
title="【仓库】批次管理、库存现有量、库存事务"
url="https://doc.iocoder.cn/mes/wm/stock/"
/>
</template>
<DetailModal />
<Grid table-title="">
<template #actions="{ row }">
<TableAction
:actions="[
{
label: '批次追溯',
type: 'link',
icon: ACTION_ICON.VIEW,
auth: ['mes:wm-batch:query'],
onClick: handleTrace.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@ -0,0 +1,89 @@
<script lang="ts" setup>
// TODO @AI使 description
import type { MesWmBatchApi } from '#/api/mes/wm/batch';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Descriptions, Tabs } from 'ant-design-vue';
import TraceList from './trace-list.vue';
const detailData = ref<MesWmBatchApi.Batch>(); //
const subTabsName = ref<'backward' | 'forward'>('forward'); //
const [Modal, modalApi] = useVbenModal({
showCancelButton: false,
showConfirmButton: false,
onOpenChange(isOpen) {
if (!isOpen) {
detailData.value = undefined;
return;
}
detailData.value = modalApi.getData<MesWmBatchApi.Batch>();
subTabsName.value = 'forward';
},
});
</script>
<template>
<Modal title="批次追溯" class="w-4/5">
<div class="mx-4 mt-2">
<Descriptions :column="3" bordered size="small">
<Descriptions.Item label="批次编号">
{{ detailData?.code || '-' }}
</Descriptions.Item>
<Descriptions.Item label="物资编码">
{{ detailData?.itemCode || '-' }}
</Descriptions.Item>
<Descriptions.Item label="物资名称">
{{ detailData?.itemName || '-' }}
</Descriptions.Item>
<Descriptions.Item label="规格型号" :span="3">
{{ detailData?.itemSpecification || '-' }}
</Descriptions.Item>
<Descriptions.Item label="采购订单编号">
{{ detailData?.purchaseOrderCode || '-' }}
</Descriptions.Item>
<Descriptions.Item label="供应商编码">
{{ detailData?.vendorCode || '-' }}
</Descriptions.Item>
<Descriptions.Item label="供应商名称">
{{ detailData?.vendorName || '-' }}
</Descriptions.Item>
<Descriptions.Item label="销售订单编号">
{{ detailData?.salesOrderCode || '-' }}
</Descriptions.Item>
<Descriptions.Item label="客户编码">
{{ detailData?.clientCode || '-' }}
</Descriptions.Item>
<Descriptions.Item label="客户名称">
{{ detailData?.clientName || '-' }}
</Descriptions.Item>
<Descriptions.Item label="生产批号">
{{ detailData?.lotNumber || '-' }}
</Descriptions.Item>
<Descriptions.Item label="生产工单">
{{ detailData?.workOrderCode || '-' }}
</Descriptions.Item>
<Descriptions.Item label="工作站编码">
{{ detailData?.workstationCode || '-' }}
</Descriptions.Item>
</Descriptions>
</div>
<Tabs
v-if="detailData?.code"
v-model:active-key="subTabsName"
class="mx-4 mt-4"
type="card"
>
<Tabs.TabPane key="forward" tab="向前追溯">
<TraceList :batch-code="detailData.code" direction="forward" />
</Tabs.TabPane>
<Tabs.TabPane key="backward" tab="向后追溯">
<TraceList :batch-code="detailData.code" direction="backward" />
</Tabs.TabPane>
</Tabs>
</Modal>
</template>

View File

@ -0,0 +1,66 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmBatchApi } from '#/api/mes/wm/batch';
import { ref, watch } from 'vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
getBackwardBatchList,
getForwardBatchList,
} from '#/api/mes/wm/batch';
import { useTraceGridColumns } from '../data';
const props = defineProps<{
batchCode?: string;
direction: 'backward' | 'forward'; // forward=backward=
}>();
const list = ref<MesWmBatchApi.Batch[]>([]); //
// TODO @AI
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: useTraceGridColumns(),
data: list.value,
minHeight: 240,
pagerConfig: { enabled: false },
rowConfig: { isHover: true, keyField: 'id' },
showOverflow: true,
toolbarConfig: { enabled: false },
} as VxeTableGridOptions<MesWmBatchApi.Batch>,
});
/** 加载追溯列表 */
// TODO @AI useVbenVxeGrid
async function getList() {
if (!props.batchCode) {
list.value = [];
gridApi.setGridOptions({ data: list.value });
return;
}
gridApi.setLoading(true);
try {
list.value =
props.direction === 'forward'
? await getForwardBatchList(props.batchCode)
: await getBackwardBatchList(props.batchCode);
gridApi.setGridOptions({ data: list.value });
} finally {
gridApi.setLoading(false);
}
}
watch(
() => props.batchCode,
() => {
getList();
},
{ immediate: true },
);
</script>
<template>
<Grid class="w-full" />
</template>