feat:出库单一键设置功能及移库单CRUD
This commit is contained in:
@@ -240,6 +240,13 @@ export function manualAllocate(data: {
|
||||
return requestClient.post('/wms/iostor-inv/manual-allocate', data);
|
||||
}
|
||||
|
||||
export function oneClickSet(iostorinvId: string, iostorinvdtlId: string) {
|
||||
return requestClient.post('/wms/iostor-inv/one-click-set', {
|
||||
iostorinvId,
|
||||
iostorinvdtlId,
|
||||
});
|
||||
}
|
||||
|
||||
/** 新增出入库单主表 */
|
||||
export function createIostorInv(data: WmsIostorInvApi.IostorInv) {
|
||||
return requestClient.post('/wms/iostor-inv/create', data);
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace WmsMoveInvApi {
|
||||
export interface Detail {
|
||||
moveinvdtlId?: string;
|
||||
storagevehicleCode: string;
|
||||
turnoutSectCode?: string;
|
||||
turnoutStructCode: string;
|
||||
turninSectCode?: string;
|
||||
turninStructCode: string;
|
||||
materialCode?: string;
|
||||
pcsn?: string;
|
||||
qtyUnitId?: string;
|
||||
qtyUnitName?: string;
|
||||
qty: number;
|
||||
sourceBillCode?: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface MoveInv {
|
||||
moveinvId?: string;
|
||||
billCode?: string;
|
||||
billType: string;
|
||||
bizDate: string;
|
||||
storId: string;
|
||||
storName?: string;
|
||||
totalQty?: number;
|
||||
detailCount?: number;
|
||||
billStatus?: string;
|
||||
remark?: string;
|
||||
creatorName?: string;
|
||||
createTime?: string;
|
||||
details: Detail[];
|
||||
}
|
||||
|
||||
export interface Inventory {
|
||||
structId: string;
|
||||
structCode: string;
|
||||
structName: string;
|
||||
sectId: string;
|
||||
sectCode: string;
|
||||
sectName: string;
|
||||
storId: string;
|
||||
storagevehicleCode: string;
|
||||
layerNum: number;
|
||||
materialCode?: string;
|
||||
pcsn?: string;
|
||||
qtyUnitId?: string;
|
||||
qtyUnitName?: string;
|
||||
qty: number;
|
||||
sourceBillCode?: string;
|
||||
}
|
||||
|
||||
export interface Struct {
|
||||
structId: string;
|
||||
structCode: string;
|
||||
structName: string;
|
||||
sectCode: string;
|
||||
sectName: string;
|
||||
layerNum: number;
|
||||
}
|
||||
}
|
||||
|
||||
export function getMoveInvPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<WmsMoveInvApi.MoveInv>>(
|
||||
'/wms/move-inv/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
export function getMoveInv(id: string) {
|
||||
return requestClient.get<WmsMoveInvApi.MoveInv>('/wms/move-inv/get', {
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
export function createMoveInv(data: WmsMoveInvApi.MoveInv) {
|
||||
return requestClient.post('/wms/move-inv/create', data);
|
||||
}
|
||||
|
||||
export function updateMoveInv(data: WmsMoveInvApi.MoveInv) {
|
||||
return requestClient.put('/wms/move-inv/update', data);
|
||||
}
|
||||
|
||||
export function deleteMoveInv(id: string) {
|
||||
return requestClient.delete('/wms/move-inv/delete', { params: { id } });
|
||||
}
|
||||
|
||||
export function getMoveAvailableInventory(storId: string, sectId?: string) {
|
||||
return requestClient.get<WmsMoveInvApi.Inventory[]>(
|
||||
'/wms/move-inv/availableInventory',
|
||||
{ params: { storId, sectId } },
|
||||
);
|
||||
}
|
||||
|
||||
export function getMoveTargetStructs(sourceStructCode: string) {
|
||||
return requestClient.get<WmsMoveInvApi.Struct[]>(
|
||||
'/wms/move-inv/availableTargetStructs',
|
||||
{ params: { sourceStructCode } },
|
||||
);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
cancelAllAllocations,
|
||||
getIostorInvDetails,
|
||||
getOutboundAllocations,
|
||||
oneClickSet,
|
||||
} from '#/api/wms/iostorinv';
|
||||
import { getSectAttrSimpleList } from '#/api/wms/sectattr';
|
||||
|
||||
@@ -204,8 +205,18 @@ async function handleManualSuccess() {
|
||||
emit('success');
|
||||
}
|
||||
|
||||
function todo(name: string) {
|
||||
message.info(`${name}功能待开发`);
|
||||
async function handleOneClickSet() {
|
||||
if (!header.value || !requireSelectedDetail()) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
await oneClickSet(header.value.iostorinvId, selectedDetailId.value!);
|
||||
await loadDetails();
|
||||
await loadSelectedAllocations();
|
||||
emit('success');
|
||||
message.success('一键设置并下发任务完成');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function statusLabel(value?: string) {
|
||||
@@ -258,7 +269,6 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<Button :loading="loading" @click="handleAutoAllocate">自动分配</Button>
|
||||
<Button :loading="loading" @click="handleAutoCancel">自动取消</Button>
|
||||
<Button @click="handleManualAllocate">手工分配</Button>
|
||||
<Button type="primary" ghost @click="todo('一键设置')">一键设置</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
@@ -280,7 +290,12 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
<div class="mb-2 mt-4 text-base font-medium">分配明细</div>
|
||||
<div class="mb-2 mt-4 flex items-center justify-between">
|
||||
<span class="text-base font-medium">分配明细</span>
|
||||
<Button type="primary" ghost :loading="loading" @click="handleOneClickSet">
|
||||
一键设置
|
||||
</Button>
|
||||
</div>
|
||||
<Table
|
||||
bordered
|
||||
:columns="allocationColumns"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { getBsrealStorAttrSimpleList } from '#/api/wms/bsrealstorattr';
|
||||
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: { allowClear: true, placeholder: '请输入单据编码' },
|
||||
fieldName: 'billCode',
|
||||
label: '单据编码',
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
api: getBsrealStorAttrSimpleList,
|
||||
labelField: 'storName',
|
||||
valueField: 'storId',
|
||||
},
|
||||
fieldName: 'storId',
|
||||
label: '仓库',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.WMS_IO_BILL_STATUS),
|
||||
},
|
||||
fieldName: 'billStatus',
|
||||
label: '单据状态',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{ type: 'checkbox', width: 50 },
|
||||
{ field: 'billCode', slots: { default: 'billCode' }, title: '单据编码', width: 160 },
|
||||
{
|
||||
field: 'billType',
|
||||
title: '单据类型',
|
||||
cellRender: { name: 'CellDict', props: { type: DICT_TYPE.WMS_ORDER_TYPE } },
|
||||
width: 110,
|
||||
},
|
||||
{ field: 'bizDate', title: '业务日期', width: 120 },
|
||||
{ field: 'storName', title: '仓库', width: 140 },
|
||||
{ field: 'totalQty', title: '总数量', width: 110 },
|
||||
{ field: 'detailCount', title: '明细数', width: 90 },
|
||||
{
|
||||
field: 'billStatus',
|
||||
title: '单据状态',
|
||||
cellRender: { name: 'CellDict', props: { type: DICT_TYPE.WMS_IO_BILL_STATUS } },
|
||||
width: 100,
|
||||
},
|
||||
{ field: 'creatorName', title: '创建人', width: 100 },
|
||||
{ field: 'createTime', formatter: 'formatDateTime', title: '创建时间', width: 170 },
|
||||
{ field: 'remark', title: '备注', minWidth: 160 },
|
||||
{ field: 'actions', fixed: 'right', slots: { default: 'actions' }, title: '操作', width: 170 },
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { WmsMoveInvApi } from '#/api/wms/moveinv';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, message } from 'antdv-next';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteMoveInv, getMoveInvPage } from '#/api/wms/moveinv';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Detail from './modules/detail.vue';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
const [DetailModal, detailModalApi] = useVbenModal({
|
||||
connectedComponent: Detail,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
function refresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
function create() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
function edit(row: WmsMoveInvApi.MoveInv) {
|
||||
if (row.billStatus !== '10') {
|
||||
message.warning('只有生成状态的移库单允许修改');
|
||||
return;
|
||||
}
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
function view(row: WmsMoveInvApi.MoveInv) {
|
||||
detailModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
async function remove(row: WmsMoveInvApi.MoveInv) {
|
||||
if (row.billStatus !== '10') {
|
||||
message.warning('只有生成状态的移库单允许删除');
|
||||
return;
|
||||
}
|
||||
await deleteMoveInv(row.moveinvId!);
|
||||
message.success('删除成功');
|
||||
refresh();
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: { schema: useGridFormSchema() },
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, values) =>
|
||||
await getMoveInvPage({
|
||||
...values,
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: { isHover: true, keyField: 'moveinvId' },
|
||||
toolbarConfig: { refresh: true, search: true },
|
||||
} as VxeTableGridOptions<WmsMoveInvApi.MoveInv>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="refresh" />
|
||||
<DetailModal />
|
||||
<Grid>
|
||||
<template #billCode="{ row }">
|
||||
<Button class="p-0" type="link" @click="view(row)">
|
||||
{{ row.billCode }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增移库单',
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['wms:movement-order:create'],
|
||||
onClick: create,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '修改',
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
disabled: row.billStatus !== '10',
|
||||
auth: ['wms:movement-order:update'],
|
||||
onClick: edit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
disabled: row.billStatus !== '10',
|
||||
auth: ['wms:movement-order:delete'],
|
||||
popConfirm: {
|
||||
title: `确认删除移库单 ${row.billCode}?`,
|
||||
confirm: remove.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -0,0 +1,57 @@
|
||||
<script lang="ts" setup>
|
||||
import type { WmsMoveInvApi } from '#/api/wms/moveinv';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Descriptions, DescriptionsItem, Table } from 'antdv-next';
|
||||
|
||||
import { getMoveInv } from '#/api/wms/moveinv';
|
||||
|
||||
const data = ref<WmsMoveInvApi.MoveInv>();
|
||||
const columns = [
|
||||
{ dataIndex: 'storagevehicleCode', title: '载具编码' },
|
||||
{ dataIndex: 'turnoutSectCode', title: '来源库区' },
|
||||
{ dataIndex: 'turnoutStructCode', title: '来源仓位' },
|
||||
{ dataIndex: 'turninSectCode', title: '目标库区' },
|
||||
{ dataIndex: 'turninStructCode', title: '目标仓位' },
|
||||
{ dataIndex: 'materialCode', title: '物料编码' },
|
||||
{ dataIndex: 'pcsn', title: '批次' },
|
||||
{ dataIndex: 'qty', title: '数量' },
|
||||
];
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreen: true,
|
||||
footer: false,
|
||||
async onOpenChange(open) {
|
||||
if (!open) return;
|
||||
const row = modalApi.getData<WmsMoveInvApi.MoveInv>();
|
||||
if (row?.moveinvId) data.value = await getMoveInv(row.moveinvId);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="移库单详情">
|
||||
<div class="p-4">
|
||||
<Descriptions bordered :column="3">
|
||||
<DescriptionsItem label="单据编码">{{ data?.billCode }}</DescriptionsItem>
|
||||
<DescriptionsItem label="业务日期">{{ data?.bizDate }}</DescriptionsItem>
|
||||
<DescriptionsItem label="仓库">{{ data?.storName || data?.storId }}</DescriptionsItem>
|
||||
<DescriptionsItem label="总数量">{{ data?.totalQty }}</DescriptionsItem>
|
||||
<DescriptionsItem label="明细数">{{ data?.detailCount }}</DescriptionsItem>
|
||||
<DescriptionsItem label="创建人">{{ data?.creatorName }}</DescriptionsItem>
|
||||
<DescriptionsItem label="备注" :span="3">{{ data?.remark }}</DescriptionsItem>
|
||||
</Descriptions>
|
||||
<Table
|
||||
class="mt-4"
|
||||
:columns="columns"
|
||||
:data-source="data?.details || []"
|
||||
:pagination="false"
|
||||
row-key="moveinvdtlId"
|
||||
:scroll="{ x: 1100 }"
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,296 @@
|
||||
<script lang="ts" setup>
|
||||
import type { WmsMoveInvApi } from '#/api/wms/moveinv';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { Button, message, Modal, Select, Table } from 'antdv-next';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { getBsrealStorAttrSimpleList } from '#/api/wms/bsrealstorattr';
|
||||
import {
|
||||
createMoveInv,
|
||||
getMoveAvailableInventory,
|
||||
getMoveInv,
|
||||
getMoveTargetStructs,
|
||||
updateMoveInv,
|
||||
} from '#/api/wms/moveinv';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const editData = ref<WmsMoveInvApi.MoveInv>();
|
||||
const details = ref<WmsMoveInvApi.Detail[]>([]);
|
||||
const inventoryRows = ref<WmsMoveInvApi.Inventory[]>([]);
|
||||
const selectedInventoryKeys = ref<string[]>([]);
|
||||
const inventoryOpen = ref(false);
|
||||
const targetOptions = ref<Record<string, WmsMoveInvApi.Struct[]>>({});
|
||||
|
||||
const isEdit = computed(() => Boolean(editData.value?.moveinvId));
|
||||
const title = computed(() => (isEdit.value ? '修改移库单' : '新增移库单'));
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: { class: 'w-full' },
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 88,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: { disabled: true },
|
||||
defaultValue: '保存时自动生成',
|
||||
fieldName: 'billCode',
|
||||
label: '单据编码',
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getBsrealStorAttrSimpleList,
|
||||
labelField: 'storName',
|
||||
placeholder: '请选择仓库',
|
||||
valueField: 'storId',
|
||||
},
|
||||
fieldName: 'storId',
|
||||
label: '仓库',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
options: getDictOptions(DICT_TYPE.WMS_ORDER_TYPE),
|
||||
},
|
||||
defaultValue: '3',
|
||||
fieldName: 'billType',
|
||||
label: '单据类型',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
options: getDictOptions(DICT_TYPE.WMS_IO_BILL_STATUS),
|
||||
},
|
||||
defaultValue: '10',
|
||||
fieldName: 'billStatus',
|
||||
label: '单据状态',
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
componentProps: { format: 'YYYY-MM-DD', valueFormat: 'YYYY-MM-DD' },
|
||||
defaultValue: dayjs().format('YYYY-MM-DD'),
|
||||
fieldName: 'bizDate',
|
||||
label: '业务日期',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Textarea',
|
||||
componentProps: { rows: 2 },
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
wrapperClass: 'grid grid-cols-1 gap-x-4 md:grid-cols-2 xl:grid-cols-3',
|
||||
});
|
||||
|
||||
const detailColumns = [
|
||||
{ key: 'index', title: '序号', width: 60 },
|
||||
{ dataIndex: 'storagevehicleCode', title: '载具编码', width: 150 },
|
||||
{ dataIndex: 'turnoutSectCode', title: '来源库区', width: 120 },
|
||||
{ dataIndex: 'turnoutStructCode', title: '来源仓位', width: 130 },
|
||||
{ dataIndex: 'materialCode', title: '物料编码', width: 130 },
|
||||
{ dataIndex: 'pcsn', title: '批次', width: 150 },
|
||||
{ dataIndex: 'qty', title: '数量', width: 100 },
|
||||
{ dataIndex: 'turninStructCode', key: 'target', title: '目标仓位', width: 220 },
|
||||
{ key: 'action', title: '操作', width: 80 },
|
||||
];
|
||||
|
||||
const inventoryColumns = [
|
||||
{ dataIndex: 'storagevehicleCode', title: '载具编码', width: 150 },
|
||||
{ dataIndex: 'sectName', title: '库区', width: 130 },
|
||||
{ dataIndex: 'structCode', title: '仓位', width: 130 },
|
||||
{ dataIndex: 'layerNum', title: '层', width: 70 },
|
||||
{ dataIndex: 'materialCode', title: '物料编码', width: 130 },
|
||||
{ dataIndex: 'pcsn', title: '批次', width: 150 },
|
||||
{ dataIndex: 'qty', title: '数量', width: 100 },
|
||||
];
|
||||
|
||||
async function openInventory() {
|
||||
const values = await formApi.getValues();
|
||||
if (!values.storId) {
|
||||
message.warning('请先选择仓库');
|
||||
return;
|
||||
}
|
||||
inventoryRows.value = await getMoveAvailableInventory(values.storId);
|
||||
selectedInventoryKeys.value = [];
|
||||
inventoryOpen.value = true;
|
||||
}
|
||||
|
||||
async function confirmInventory() {
|
||||
const selected = inventoryRows.value.filter((item) =>
|
||||
selectedInventoryKeys.value.includes(item.storagevehicleCode),
|
||||
);
|
||||
const existing = new Set(details.value.map((item) => item.storagevehicleCode));
|
||||
for (const item of selected) {
|
||||
if (existing.has(item.storagevehicleCode)) continue;
|
||||
details.value.push({
|
||||
storagevehicleCode: item.storagevehicleCode,
|
||||
turnoutSectCode: item.sectCode,
|
||||
turnoutStructCode: item.structCode,
|
||||
turninStructCode: '',
|
||||
materialCode: item.materialCode,
|
||||
pcsn: item.pcsn,
|
||||
qtyUnitId: item.qtyUnitId,
|
||||
qtyUnitName: item.qtyUnitName,
|
||||
qty: item.qty,
|
||||
sourceBillCode: item.sourceBillCode,
|
||||
});
|
||||
targetOptions.value[item.storagevehicleCode] =
|
||||
await getMoveTargetStructs(item.structCode);
|
||||
}
|
||||
inventoryOpen.value = false;
|
||||
}
|
||||
|
||||
function usedTargetCodes(currentVehicle: string) {
|
||||
return new Set(
|
||||
details.value
|
||||
.filter((item) => item.storagevehicleCode !== currentVehicle)
|
||||
.map((item) => item.turninStructCode)
|
||||
.filter(Boolean),
|
||||
);
|
||||
}
|
||||
|
||||
function targetSelectOptions(row: WmsMoveInvApi.Detail) {
|
||||
const used = usedTargetCodes(row.storagevehicleCode);
|
||||
return (targetOptions.value[row.storagevehicleCode] || [])
|
||||
.filter((item) => !used.has(item.structCode))
|
||||
.map((item) => ({
|
||||
label: `${item.structCode}(${item.structName || item.sectName})`,
|
||||
value: item.structCode,
|
||||
}));
|
||||
}
|
||||
|
||||
function removeDetail(index: number) {
|
||||
details.value.splice(index, 1);
|
||||
}
|
||||
|
||||
function handleInventorySelection(keys: (number | string)[]) {
|
||||
selectedInventoryKeys.value = keys.map(String);
|
||||
}
|
||||
|
||||
const [ModalComponent, modalApi] = useVbenModal({
|
||||
fullscreen: true,
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) return;
|
||||
if (details.value.length === 0) {
|
||||
message.warning('请至少选择一个来源载具');
|
||||
return;
|
||||
}
|
||||
if (details.value.some((item) => !item.turninStructCode)) {
|
||||
message.warning('请为每个来源载具选择目标仓位');
|
||||
return;
|
||||
}
|
||||
const values = await formApi.getValues();
|
||||
modalApi.lock();
|
||||
try {
|
||||
const payload = {
|
||||
...values,
|
||||
details: details.value,
|
||||
moveinvId: editData.value?.moveinvId,
|
||||
} as WmsMoveInvApi.MoveInv;
|
||||
if (isEdit.value) await updateMoveInv(payload);
|
||||
else await createMoveInv(payload);
|
||||
message.success('保存成功');
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(open) {
|
||||
if (!open) return;
|
||||
editData.value = undefined;
|
||||
details.value = [];
|
||||
targetOptions.value = {};
|
||||
await formApi.resetForm();
|
||||
const data = modalApi.getData<WmsMoveInvApi.MoveInv>();
|
||||
if (!data?.moveinvId) {
|
||||
await formApi.setValues({
|
||||
billCode: '保存时自动生成',
|
||||
billStatus: '10',
|
||||
billType: '3',
|
||||
bizDate: dayjs().format('YYYY-MM-DD'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const result = await getMoveInv(data.moveinvId);
|
||||
editData.value = result;
|
||||
details.value = result.details || [];
|
||||
await formApi.setValues(result);
|
||||
await Promise.all(
|
||||
details.value.map(async (item) => {
|
||||
targetOptions.value[item.storagevehicleCode] =
|
||||
await getMoveTargetStructs(item.turnoutStructCode);
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ModalComponent :title="title">
|
||||
<div class="p-4">
|
||||
<Form />
|
||||
<div class="mb-3 mt-4">
|
||||
<Button type="primary" @click="openInventory">选择可用库存</Button>
|
||||
</div>
|
||||
<Table
|
||||
:columns="detailColumns"
|
||||
:data-source="details"
|
||||
:pagination="false"
|
||||
row-key="storagevehicleCode"
|
||||
:scroll="{ x: 1200 }"
|
||||
>
|
||||
<template #bodyCell="{ column, index, record }">
|
||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||
<template v-else-if="column.key === 'target'">
|
||||
<Select
|
||||
v-model:value="record.turninStructCode"
|
||||
class="w-full"
|
||||
:options="targetSelectOptions(record)"
|
||||
placeholder="请选择同库区同层仓位"
|
||||
show-search
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<Button danger type="link" @click="removeDetail(index)">删除</Button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</div>
|
||||
<Modal
|
||||
v-model:open="inventoryOpen"
|
||||
title="选择移库库存"
|
||||
width="1100px"
|
||||
@ok="confirmInventory"
|
||||
>
|
||||
<Table
|
||||
:columns="inventoryColumns"
|
||||
:data-source="inventoryRows"
|
||||
:pagination="false"
|
||||
row-key="storagevehicleCode"
|
||||
:row-selection="{
|
||||
selectedRowKeys: selectedInventoryKeys,
|
||||
onChange: handleInventorySelection,
|
||||
}"
|
||||
:scroll="{ x: 950, y: 500 }"
|
||||
/>
|
||||
</Modal>
|
||||
</ModalComponent>
|
||||
</template>
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import MoveInv from '../../moveinv/index.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MoveInv />
|
||||
</template>
|
||||
Reference in New Issue
Block a user