feat(mes): 提交 wm stocktaking 相关的迁移

pull/350/head
YunaiV 2026-05-30 09:15:06 +08:00
parent 990d0a78fb
commit b6d1154b8f
58 changed files with 7433 additions and 0 deletions

View File

@ -54,6 +54,7 @@
"camunda-bpmn-moddle": "catalog:",
"cropperjs": "catalog:",
"dayjs": "catalog:",
"dhtmlx-gantt": "catalog:",
"diagram-js": "catalog:",
"fast-xml-parser": "catalog:",
"highlight.js": "catalog:",

View File

@ -0,0 +1,2 @@
export { default as StockTakingPlanSelectDialog } from './stock-taking-plan-select-dialog.vue';
export { default as StockTakingPlanSelect } from './stock-taking-plan-select.vue';

View File

@ -0,0 +1,210 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import { nextTick, ref } from 'vue';
import { CommonStatusEnum } from '@vben/constants';
import { message, Modal } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getStockTakingPlanPage } from '#/api/mes/wm/stocktaking/plan';
import { useSelectGridColumns, useSelectGridFormSchema } from '../data';
const emit = defineEmits<{
selected: [rows: MesWmStockTakingPlanApi.StockTakingPlan[]];
}>();
const open = ref(false); //
const multiple = ref(true); //
const selectedRows = ref<MesWmStockTakingPlanApi.StockTakingPlan[]>([]); //
const preSelectedIds = ref<number[]>([]); //
/** 获取多选记录,包含 VXE reserve 跨页记录 */
function getMultipleSelectedRows() {
const selectedMap = new Map<
number,
MesWmStockTakingPlanApi.StockTakingPlan
>();
const records = [
...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
...(gridApi.grid.getCheckboxRecords?.() ?? []),
] as MesWmStockTakingPlanApi.StockTakingPlan[];
records.forEach((row) => {
if (row.id != null) {
selectedMap.set(row.id, row);
}
});
return [...selectedMap.values()];
}
/** 处理勾选变化 */
function handleCheckboxSelectChange() {
selectedRows.value = getMultipleSelectedRows();
}
/** 处理单选变化 */
function handleRadioChange(row: MesWmStockTakingPlanApi.StockTakingPlan) {
selectedRows.value = [row];
}
/** 多选模式下切换行勾选 */
async function toggleMultipleRow(
row: MesWmStockTakingPlanApi.StockTakingPlan,
) {
const selected = gridApi.grid.isCheckedByCheckboxRow(row);
await gridApi.grid.setCheckboxRow(row, !selected);
selectedRows.value = getMultipleSelectedRows();
}
/** 处理行双击:单选直接确认;多选切换勾选 */
async function handleCellDblclick({
row,
}: {
row: MesWmStockTakingPlanApi.StockTakingPlan;
}) {
if (multiple.value) {
await toggleMultipleRow(row);
return;
}
selectedRows.value = [row];
await gridApi.grid.setRadioRow(row);
handleConfirm();
}
/** 回显预选盘点方案 */
async function applyPreSelection() {
if (preSelectedIds.value.length === 0) {
return;
}
const rows = gridApi.grid.getData() as MesWmStockTakingPlanApi.StockTakingPlan[];
for (const row of rows) {
if (row.id == null || !preSelectedIds.value.includes(row.id)) {
continue;
}
if (multiple.value) {
await gridApi.grid.setCheckboxRow(row, true);
} else {
await gridApi.grid.setRadioRow(row);
selectedRows.value = [row];
return;
}
}
if (multiple.value) {
selectedRows.value = getMultipleSelectedRows();
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useSelectGridFormSchema(),
},
gridOptions: {
columns: useSelectGridColumns(true),
height: 480,
keepSource: true,
checkboxConfig: {
highlight: true,
range: true,
reserve: true,
},
radioConfig: {
highlight: true,
trigger: 'row',
},
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getStockTakingPlanPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
//
status: CommonStatusEnum.ENABLE,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<MesWmStockTakingPlanApi.StockTakingPlan>,
gridEvents: {
cellDblclick: handleCellDblclick,
checkboxAll: handleCheckboxSelectChange,
checkboxChange: handleCheckboxSelectChange,
radioChange: ({
row,
}: {
row: MesWmStockTakingPlanApi.StockTakingPlan;
}) => {
handleRadioChange(row);
},
},
});
/** 重置查询和选择状态 */
async function resetQueryState() {
selectedRows.value = [];
await gridApi.grid.clearCheckboxRow();
await gridApi.grid.clearCheckboxReserve();
await gridApi.grid.clearRadioRow();
await gridApi.formApi.resetForm();
}
/** 打开盘点方案选择弹窗 */
async function openModal(
selectedIds?: number[],
options?: { multiple?: boolean },
) {
open.value = true;
multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || [];
await nextTick();
gridApi.setGridOptions({
columns: useSelectGridColumns(multiple.value),
});
await resetQueryState();
await gridApi.query();
await nextTick();
await applyPreSelection();
}
/** 关闭弹窗 */
function closeModal() {
open.value = false;
}
/** 确认选择 */
function handleConfirm() {
const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
if (rows.length === 0) {
message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
return;
}
emit('selected', multiple.value ? rows : [rows[0]!]);
open.value = false;
}
defineExpose({ open: openModal });
</script>
<template>
<Modal
v-model:open="open"
:destroy-on-close="true"
title="盘点方案选择"
width="70%"
@cancel="closeModal"
@ok="handleConfirm"
>
<Grid table-title="" />
</Modal>
</template>

View File

@ -0,0 +1,137 @@
<script lang="ts" setup>
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import { computed, ref, useAttrs, watch } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { Input, Tooltip } from 'ant-design-vue';
import { getStockTakingPlan } from '#/api/mes/wm/stocktaking/plan';
import StockTakingPlanSelectDialog from './stock-taking-plan-select-dialog.vue';
defineOptions({ name: 'StockTakingPlanSelect', inheritAttrs: false });
const props = withDefaults(
defineProps<{
allowClear?: boolean;
disabled?: boolean;
modelValue?: number;
placeholder?: string;
}>(),
{
allowClear: true,
disabled: false,
modelValue: undefined,
placeholder: '请选择盘点方案',
},
);
const emit = defineEmits<{
change: [item: MesWmStockTakingPlanApi.StockTakingPlan | undefined];
'update:modelValue': [value: number | undefined];
}>();
const attrs = useAttrs();
const dialogRef = ref<InstanceType<typeof StockTakingPlanSelectDialog>>();
const hovering = ref(false);
const selectedItem = ref<MesWmStockTakingPlanApi.StockTakingPlan>();
const displayLabel = computed(() => selectedItem.value?.name ?? '');
const showClear = computed(
() =>
props.allowClear &&
!props.disabled &&
hovering.value &&
props.modelValue != null,
);
/** 根据编号单条查询盘点方案信息(用于编辑回显) */
async function resolveItemById(id: number | undefined) {
if (id == null) {
selectedItem.value = undefined;
return;
}
if (selectedItem.value?.id === id) {
return;
}
try {
selectedItem.value = await getStockTakingPlan(id);
} catch (error) {
console.error('[StockTakingPlanSelect] resolveItemById failed:', error);
}
}
watch(() => props.modelValue, resolveItemById, { immediate: true });
/** 清空已选盘点方案 */
function clearSelected() {
selectedItem.value = undefined;
emit('update:modelValue', undefined);
emit('change', undefined);
}
/** 打开盘点方案选择弹窗 */
function handleClick(event: MouseEvent) {
if (props.disabled) {
return;
}
const target = event.target as HTMLElement;
if (showClear.value && target.closest('.ant-input-suffix')) {
event.stopPropagation();
clearSelected();
return;
}
const selectedIds = props.modelValue == null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds, { multiple: false });
}
/** 弹窗选中回调 */
function handleSelected(rows: MesWmStockTakingPlanApi.StockTakingPlan[]) {
const item = rows[0];
if (!item) {
return;
}
selectedItem.value = item;
emit('update:modelValue', item.id);
emit('change', item);
}
</script>
<template>
<div
v-bind="attrs"
class="w-full"
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
@click="handleClick"
@mouseenter="hovering = true"
@mouseleave="hovering = false"
>
<Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
<template #title>
<div v-if="selectedItem" class="leading-6">
<div>编码{{ selectedItem.code || '-' }}</div>
<div>名称{{ selectedItem.name || '-' }}</div>
<div>是否盲盘{{ selectedItem.blindFlag ? '是' : '否' }}</div>
<div>是否冻结库存{{ selectedItem.frozen ? '是' : '否' }}</div>
</div>
</template>
<Input
:disabled="disabled"
:placeholder="placeholder"
:value="displayLabel"
readonly
>
<template #suffix>
<IconifyIcon
class="size-4"
:icon="showClear ? 'lucide:circle-x' : 'lucide:search'"
/>
</template>
</Input>
</Tooltip>
</div>
<StockTakingPlanSelectDialog ref="dialogRef" @selected="handleSelected" />
</template>

View File

@ -0,0 +1,387 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import type { MesWmStockTakingPlanParamApi } from '#/api/mes/wm/stocktaking/plan/param';
import { h } from 'vue';
import { CommonStatusEnum, DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { Button } from 'ant-design-vue';
import { z } from '#/adapter/form';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import {
MesAutoCodeRuleCode,
MesWmStockTakingTypeEnum,
} from '#/views/mes/utils/constants';
/** 表单类型 */
export type FormType = 'create' | 'detail' | 'update';
/** 新增/修改的表单 */
export function useFormSchema(
formType: FormType,
formApi?: VbenFormApi,
): VbenFormSchema[] {
return [
{
fieldName: 'id',
component: 'Input',
dependencies: {
triggerFields: [''],
show: () => false,
},
},
{
fieldName: 'code',
label: '方案编码',
component: 'Input',
componentProps: {
placeholder: '请输入方案编码',
},
rules: 'required',
suffix:
formType === 'detail'
? undefined
: () =>
h(
Button,
{
type: 'default',
onClick: async () => {
const code = await generateAutoCode(
MesAutoCodeRuleCode.WM_STOCK_TAKING_PLAN_CODE,
);
await formApi?.setFieldValue('code', code);
},
},
{ default: () => '生成' },
),
},
{
fieldName: 'name',
label: '方案名称',
component: 'Input',
componentProps: {
placeholder: '请输入方案名称',
},
rules: 'required',
},
{
fieldName: 'type',
label: '盘点类型',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, 'number'),
placeholder: '请选择盘点类型',
},
rules: 'selectRequired',
},
{
fieldName: 'startTime',
label: '开始时间',
component: 'DatePicker',
componentProps: {
placeholder: '请选择开始时间',
showTime: true,
valueFormat: 'x',
},
dependencies: {
triggerFields: ['type'],
show: (values) => values.type === MesWmStockTakingTypeEnum.DYNAMIC,
},
},
{
fieldName: 'endTime',
label: '结束时间',
component: 'DatePicker',
componentProps: {
placeholder: '请选择结束时间',
showTime: true,
valueFormat: 'x',
},
dependencies: {
triggerFields: ['type'],
show: (values) => values.type === MesWmStockTakingTypeEnum.DYNAMIC,
},
},
{
fieldName: 'blindFlag',
label: '是否盲盘',
component: 'Switch',
componentProps: {
checkedChildren: '是',
unCheckedChildren: '否',
},
rules: z.boolean().default(false),
},
{
fieldName: 'frozen',
label: '冻结库存',
component: 'Switch',
componentProps: {
checkedChildren: '是',
unCheckedChildren: '否',
},
rules: z.boolean().default(false),
},
{
fieldName: 'remark',
label: '备注',
component: 'Textarea',
formItemClass: 'col-span-3',
componentProps: {
placeholder: '请输入备注',
rows: 3,
},
},
];
}
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'code',
label: '方案编码',
component: 'Input',
componentProps: {
allowClear: true,
placeholder: '请输入方案编码',
},
},
{
fieldName: 'name',
label: '方案名称',
component: 'Input',
componentProps: {
allowClear: true,
placeholder: '请输入方案名称',
},
},
{
fieldName: 'type',
label: '盘点类型',
component: 'Select',
componentProps: {
allowClear: true,
options: getDictOptions(DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, 'number'),
placeholder: '请选择盘点类型',
},
},
];
}
/** 列表的字段 */
export function useGridColumns(
onStatusChange?: (
newStatus: number,
row: MesWmStockTakingPlanApi.StockTakingPlan,
) => Promise<boolean | undefined>,
): VxeTableGridOptions<MesWmStockTakingPlanApi.StockTakingPlan>['columns'] {
return [
{
field: 'code',
title: '方案编码',
minWidth: 160,
slots: { default: 'code' },
},
{
field: 'name',
title: '方案名称',
minWidth: 160,
},
{
field: 'type',
title: '盘点类型',
minWidth: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_TYPE },
},
},
{
field: 'startTime',
title: '开始时间',
width: 180,
formatter: 'formatDateTime',
},
{
field: 'endTime',
title: '结束时间',
width: 180,
formatter: 'formatDateTime',
},
{
field: 'blindFlag',
title: '是否盲盘',
width: 100,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
},
},
{
field: 'frozen',
title: '是否冻结库存',
width: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
},
},
{
field: 'status',
title: '状态',
width: 120,
cellRender: {
attrs: { beforeChange: onStatusChange },
name: 'CellSwitch',
props: {
checkedValue: CommonStatusEnum.ENABLE,
unCheckedValue: CommonStatusEnum.DISABLE,
},
},
},
{
title: '操作',
width: 160,
fixed: 'right',
slots: { default: 'actions' },
},
];
}
/** 盘点方案条件列表的字段 */
export function useParamGridColumns(
editable = true,
): VxeTableGridOptions<MesWmStockTakingPlanParamApi.StockTakingPlanParam>['columns'] {
return [
{
field: 'type',
title: '条件类型',
minWidth: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_PLAN_PARAM_TYPE },
},
},
{
field: 'valueCode',
title: '条件值编码',
minWidth: 140,
},
{
field: 'valueName',
title: '条件值名称',
minWidth: 160,
},
...(editable
? [
{
title: '操作',
width: 120,
fixed: 'right',
slots: { default: 'actions' },
} as const,
]
: []),
];
}
/** 选择弹窗的搜索表单 */
export function useSelectGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'code',
label: '方案编码',
component: 'Input',
componentProps: {
allowClear: true,
placeholder: '请输入方案编码',
},
},
{
fieldName: 'name',
label: '方案名称',
component: 'Input',
componentProps: {
allowClear: true,
placeholder: '请输入方案名称',
},
},
{
fieldName: 'type',
label: '盘点类型',
component: 'Select',
componentProps: {
allowClear: true,
options: getDictOptions(DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, 'number'),
placeholder: '请选择盘点类型',
},
},
];
}
/** 选择弹窗的字段 */
export function useSelectGridColumns(
multiple = true,
): VxeTableGridOptions<MesWmStockTakingPlanApi.StockTakingPlan>['columns'] {
return [
{
type: multiple ? 'checkbox' : 'radio',
width: 50,
},
{
field: 'code',
title: '方案编码',
width: 200,
},
{
field: 'name',
title: '方案名称',
minWidth: 150,
},
{
field: 'type',
title: '盘点类型',
width: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_TYPE },
},
},
{
field: 'startTime',
title: '开始时间',
width: 180,
formatter: 'formatDateTime',
},
{
field: 'endTime',
title: '结束时间',
width: 180,
formatter: 'formatDateTime',
},
{
field: 'blindFlag',
title: '是否盲盘',
width: 100,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
},
},
{
field: 'frozen',
title: '是否冻结库存',
width: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
},
},
];
}

View File

@ -0,0 +1,184 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { CommonStatusEnum } from '@vben/constants';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteStockTakingPlan,
exportStockTakingPlan,
getStockTakingPlanPage,
updateStockTakingPlanStatus,
} from '#/api/mes/wm/stocktaking/plan';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
destroyOnClose: true,
});
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 创建盘点方案 */
function handleCreate() {
formModalApi.setData({ formType: 'create' }).open();
}
/** 查看盘点方案 */
function handleDetail(row: MesWmStockTakingPlanApi.StockTakingPlan) {
formModalApi.setData({ formType: 'detail', id: row.id }).open();
}
/** 编辑盘点方案 */
function handleEdit(row: MesWmStockTakingPlanApi.StockTakingPlan) {
formModalApi.setData({ formType: 'update', id: row.id }).open();
}
/** 更新盘点方案状态 */
async function handleStatusChange(
newStatus: number,
row: MesWmStockTakingPlanApi.StockTakingPlan,
): Promise<boolean | undefined> {
try {
await confirm(
`确认要${newStatus === CommonStatusEnum.ENABLE ? '启用' : '停用'}"${row.name}"盘点方案吗?`,
);
} catch {
return false;
}
await updateStockTakingPlanStatus(row.id!, newStatus);
message.success($t('ui.actionMessage.operationSuccess'));
return true;
}
/** 删除盘点方案 */
async function handleDelete(row: MesWmStockTakingPlanApi.StockTakingPlan) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
});
try {
await deleteStockTakingPlan(row.id!);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
handleRefresh();
} finally {
hideLoading();
}
}
/** 导出表格 */
async function handleExport() {
const data = await exportStockTakingPlan(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '盘点方案.xls', source: data });
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(handleStatusChange),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getStockTakingPlanPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<MesWmStockTakingPlanApi.StockTakingPlan>,
});
const StatusEnum = CommonStatusEnum;
</script>
<template>
<Page auto-content-height>
<template #doc>
<DocAlert
title="【仓库】库存盘点"
url="https://doc.iocoder.cn/mes/wm/stocktaking/"
/>
</template>
<FormModal @success="handleRefresh" />
<Grid table-title="">
<template #toolbar-tools>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['盘点方案']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['mes:wm-stock-taking-plan:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['mes:wm-stock-taking-plan:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #code="{ row }">
<Button type="link" @click="handleDetail(row)">
{{ row.code }}
</Button>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['mes:wm-stock-taking-plan:update'],
disabled: row.status !== StatusEnum.DISABLE,
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['mes:wm-stock-taking-plan:delete'],
disabled: row.status !== StatusEnum.DISABLE,
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@ -0,0 +1,126 @@
<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Divider, message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import {
createStockTakingPlan,
getStockTakingPlan,
updateStockTakingPlan,
} from '#/api/mes/wm/stocktaking/plan';
import { $t } from '#/locales';
import { useFormSchema } from '../data';
import ParamList from './param-list.vue';
const emit = defineEmits(['success']);
const formType = ref<FormType>('create');
const formData = ref<MesWmStockTakingPlanApi.StockTakingPlan>();
const isDetail = computed(() => formType.value === 'detail');
const showParam = computed(
() =>
(formType.value === 'detail' || formType.value === 'update') &&
!!formData.value?.id,
);
const getTitle = computed(() => {
if (formType.value === 'detail') {
return $t('ui.actionTitle.view', ['盘点方案']);
}
return formType.value === 'update'
? $t('ui.actionTitle.edit', ['盘点方案'])
: $t('ui.actionTitle.create', ['盘点方案']);
});
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-1',
labelWidth: 110,
},
layout: 'horizontal',
schema: [],
showDefaultActions: false,
wrapperClass: 'grid-cols-3',
});
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
if (isDetail.value) {
await modalApi.close();
return;
}
const { valid } = await formApi.validate();
if (!valid) {
return;
}
modalApi.lock();
//
const data =
(await formApi.getValues()) as MesWmStockTakingPlanApi.StockTakingPlan;
try {
if (formData.value?.id) {
await updateStockTakingPlan({ ...data, id: formData.value.id });
//
await modalApi.close();
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
} else {
//
const id = await createStockTakingPlan(data);
formData.value = { ...data, id };
await formApi.setFieldValue('id', id);
formType.value = 'update';
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
}
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
return;
}
//
const data = modalApi.getData<{ formType: FormType; id?: number }>();
formType.value = data.formType;
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
formApi.setDisabled(formType.value === 'detail');
modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
if (!data?.id) {
return;
}
modalApi.lock();
try {
formData.value = await getStockTakingPlan(data.id);
// values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
},
});
</script>
<template>
<Modal :title="getTitle" class="w-3/5">
<Form class="mx-4" />
<!-- 编辑或详情时展示盘点参数 -->
<template v-if="showParam">
<Divider>盘点参数</Divider>
<div class="mx-4">
<ParamList :disabled="isDetail" :plan-id="formData!.id!" />
</div>
</template>
</Modal>
</template>

View File

@ -0,0 +1,267 @@
<script lang="ts" setup>
import type { MesWmStockTakingPlanParamApi } from '#/api/mes/wm/stocktaking/plan/param';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { Form, message, Select, Textarea } from 'ant-design-vue';
import {
createStockTakingPlanParam,
getStockTakingPlanParam,
updateStockTakingPlanParam,
} from '#/api/mes/wm/stocktaking/plan/param';
import { getWarehouseArea } from '#/api/mes/wm/warehouse/area';
import { getWarehouseLocation } from '#/api/mes/wm/warehouse/location';
import { $t } from '#/locales';
import MdItemSelect from '#/views/mes/md/item/components/md-item-select.vue';
import { MesWmStockTakingParamTypeEnum } from '#/views/mes/utils/constants';
import { WmBatchSelect } from '#/views/mes/wm/batch/components';
import {
WmWarehouseAreaSelect,
WmWarehouseLocationSelect,
WmWarehouseSelect,
} from '#/views/mes/wm/warehouse/components';
const emit = defineEmits(['success']);
const formData = ref<MesWmStockTakingPlanParamApi.StockTakingPlanParam>({});
const planId = ref<number>(); // TODO @AI
const locationWarehouseId = ref<number>(); //
const areaWarehouseId = ref<number>(); //
const areaLocationId = ref<number>(); //
// TODO @AI mapping
const paramTypeOptions = getDictOptions(
DICT_TYPE.MES_WM_STOCK_TAKING_PLAN_PARAM_TYPE,
'number',
).map(({ label, value }) => ({ label, value: Number(value) }));
const qualityStatusOptions = getDictOptions(
DICT_TYPE.MES_WM_QUALITY_STATUS,
'string',
).map(({ label, value }) => ({ label, value: String(value) }));
// TODO @AI title const
const getTitle = computed(() =>
formData.value?.id
? $t('ui.actionTitle.edit', ['盘点条件'])
: $t('ui.actionTitle.create', ['盘点条件']),
);
/** 条件类型变化:清空已选条件值和级联临时数据 */
function handleTypeChange() {
formData.value.valueId = undefined;
formData.value.valueCode = '';
formData.value.valueName = '';
locationWarehouseId.value = undefined;
areaWarehouseId.value = undefined;
areaLocationId.value = undefined;
}
/** 通用选择器变化:回填条件值编码、名称 */
function handleSelectorChange(item?: any) {
formData.value.valueId = item?.id;
formData.value.valueCode = item?.code || '';
formData.value.valueName = item?.name || item?.nickname || '';
}
/** 批次选择器变化 */
function handleBatchChange(batch?: any) {
formData.value.valueId = batch?.id;
formData.value.valueCode = batch?.code || '';
formData.value.valueName = batch?.code || '';
}
/** 质量状态选择器变化:无实体编号,仅记录字典编码和文案 */
function handleQualityStatusChange(value: any) {
const selected = qualityStatusOptions.find((item) => item.value === value);
formData.value.valueId = undefined;
formData.value.valueCode = value;
formData.value.valueName = selected?.label || '';
}
/** 库区仓库选择回调:清空库区 */
function handleLocationWarehouseChange() {
formData.value.valueId = undefined;
formData.value.valueCode = '';
formData.value.valueName = '';
}
/** 库位仓库选择回调:清空库区和库位 */
function handleAreaWarehouseChange() {
areaLocationId.value = undefined;
formData.value.valueId = undefined;
formData.value.valueCode = '';
formData.value.valueName = '';
}
/** 库位库区选择回调:清空库位 */
function handleAreaLocationChange() {
formData.value.valueId = undefined;
formData.value.valueCode = '';
formData.value.valueName = '';
}
/** 编辑时回填级联选择器的上级数据(库区所属仓库、库位所属仓库/库区) */
async function loadCascadeData() {
if (!formData.value.type || !formData.value.valueId) {
return;
}
const valueId = formData.value.valueId;
if (formData.value.type === MesWmStockTakingParamTypeEnum.LOCATION) {
const location = await getWarehouseLocation(valueId);
locationWarehouseId.value = location?.warehouseId;
} else if (formData.value.type === MesWmStockTakingParamTypeEnum.AREA) {
const area = await getWarehouseArea(valueId);
areaWarehouseId.value = area?.warehouseId;
areaLocationId.value = area?.locationId;
}
}
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
if (!formData.value.type) {
message.warning('请选择条件类型');
return;
}
// valueCode valueId
const valid =
formData.value.type === MesWmStockTakingParamTypeEnum.QUALITY_STATUS
? !!formData.value.valueCode
: formData.value.valueId != null;
if (!valid) {
message.warning('请选择条件值');
return;
}
modalApi.lock();
//
const data = {
...formData.value,
planId: planId.value,
} as MesWmStockTakingPlanParamApi.StockTakingPlanParam;
try {
await (formData.value.id
? updateStockTakingPlanParam(data)
: createStockTakingPlanParam(data));
//
await modalApi.close();
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = {};
locationWarehouseId.value = undefined;
areaWarehouseId.value = undefined;
areaLocationId.value = undefined;
return;
}
//
const data = modalApi.getData<{ id?: number; planId: number }>();
planId.value = data.planId;
if (!data.id) {
return;
}
modalApi.lock();
try {
formData.value = await getStockTakingPlanParam(data.id);
await loadCascadeData();
} finally {
modalApi.unlock();
}
},
});
const ParamTypeEnum = MesWmStockTakingParamTypeEnum;
</script>
<template>
<Modal :title="getTitle" class="w-3/5">
<Form class="mx-4" :label-col="{ span: 4 }">
<!-- TODO @AI可以更加 form schema 更好的 antd ele 的复用 -->
<Form.Item label="条件类型" required>
<Select
v-model:value="formData.type"
:options="paramTypeOptions"
placeholder="请选择条件类型"
@change="handleTypeChange"
/>
</Form.Item>
<Form.Item v-if="formData.type" label="条件值" required>
<WmWarehouseSelect
v-if="formData.type === ParamTypeEnum.WAREHOUSE"
v-model="formData.valueId"
@change="handleSelectorChange"
/>
<div
v-else-if="formData.type === ParamTypeEnum.LOCATION"
class="space-y-2"
>
<WmWarehouseSelect
v-model="locationWarehouseId"
placeholder="请选择仓库"
@change="handleLocationWarehouseChange"
/>
<WmWarehouseLocationSelect
v-if="locationWarehouseId"
v-model="formData.valueId"
placeholder="请选择库区"
:warehouse-id="locationWarehouseId"
@change="handleSelectorChange"
/>
</div>
<div v-else-if="formData.type === ParamTypeEnum.AREA" class="space-y-2">
<WmWarehouseSelect
v-model="areaWarehouseId"
placeholder="请选择仓库"
@change="handleAreaWarehouseChange"
/>
<WmWarehouseLocationSelect
v-if="areaWarehouseId"
v-model="areaLocationId"
placeholder="请选择库区"
:warehouse-id="areaWarehouseId"
@change="handleAreaLocationChange"
/>
<WmWarehouseAreaSelect
v-if="areaLocationId"
v-model="formData.valueId"
:location-id="areaLocationId"
placeholder="请选择库位"
@change="handleSelectorChange"
/>
</div>
<MdItemSelect
v-else-if="formData.type === ParamTypeEnum.ITEM"
v-model="formData.valueId"
@change="handleSelectorChange"
/>
<WmBatchSelect
v-else-if="formData.type === ParamTypeEnum.BATCH"
v-model="formData.valueId"
@change="handleBatchChange"
/>
<Select
v-else-if="formData.type === ParamTypeEnum.QUALITY_STATUS"
v-model:value="formData.valueCode"
:options="qualityStatusOptions"
placeholder="请选择质量状态"
@change="handleQualityStatusChange"
/>
</Form.Item>
<Form.Item label="备注">
<Textarea
v-model:value="formData.remark"
placeholder="请输入备注"
:rows="3"
/>
</Form.Item>
</Form>
</Modal>
</template>

View File

@ -0,0 +1,582 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import type { MesWmStockTakingTaskApi } from '#/api/mes/wm/stocktaking/task';
import type { MesWmStockTakingTaskLineApi } from '#/api/mes/wm/stocktaking/task/line';
import type { MesWmStockTakingResultApi } from '#/api/mes/wm/stocktaking/task/result';
import { h, markRaw } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { Button } from 'ant-design-vue';
import { z } from '#/adapter/form';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils';
import MdItemSelect from '#/views/mes/md/item/components/md-item-select.vue';
import {
MesAutoCodeRuleCode,
MesWmStockTakingTypeEnum,
} from '#/views/mes/utils/constants';
import { StockTakingPlanSelect } from '#/views/mes/wm/stocktaking/plan/components';
import {
WmWarehouseAreaSelect,
WmWarehouseLocationSelect,
WmWarehouseSelect,
} from '#/views/mes/wm/warehouse/components';
/** 表单类型 */
export type FormType = 'create' | 'detail' | 'execute' | 'submit' | 'update';
/** 表单头部是否只读(提交、执行盘点、详情态) */
function isHeaderReadonly(formType: FormType): boolean {
return (
formType === 'detail' ||
formType === 'execute' ||
formType === 'submit'
);
}
/** 新增/修改的表单 */
export function useFormSchema(
formType: FormType,
formApi?: VbenFormApi,
): VbenFormSchema[] {
const headerReadonly = isHeaderReadonly(formType);
return [
{
fieldName: 'id',
component: 'Input',
dependencies: {
triggerFields: [''],
show: () => false,
},
},
{
fieldName: 'status',
component: 'Input',
dependencies: {
triggerFields: [''],
show: () => false,
},
},
{
fieldName: 'code',
label: '任务编码',
component: 'Input',
componentProps: {
placeholder: '请输入任务编码',
},
rules: 'required',
suffix: headerReadonly
? undefined
: () =>
h(
Button,
{
type: 'default',
onClick: async () => {
const code = await generateAutoCode(
MesAutoCodeRuleCode.WM_STOCK_TAKING_CODE,
);
await formApi?.setFieldValue('code', code);
},
},
{ default: () => '生成' },
),
},
{
fieldName: 'name',
label: '任务名称',
component: 'Input',
componentProps: {
placeholder: '请输入任务名称',
},
rules: 'required',
},
{
fieldName: 'planId',
label: '盘点方案',
component: markRaw(StockTakingPlanSelect),
componentProps: {
// 选择盘点方案后,自动带出名称、类型、起止时间和盲盘/冻结配置
onChange: async (plan?: MesWmStockTakingPlanApi.StockTakingPlan) => {
if (!plan) {
return;
}
await formApi?.setValues({
blindFlag: !!plan.blindFlag,
endTime: plan.endTime,
frozen: !!plan.frozen,
name: plan.name,
startTime: plan.startTime,
type: plan.type,
});
},
},
rules: 'selectRequired',
},
{
fieldName: 'type',
label: '盘点类型',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, 'number'),
placeholder: '请选择盘点类型',
},
rules: 'selectRequired',
},
{
fieldName: 'startTime',
label: '开始时间',
component: 'DatePicker',
componentProps: {
placeholder: '请选择开始时间',
showTime: true,
valueFormat: 'x',
},
dependencies: {
triggerFields: ['type'],
show: (values) => values.type === MesWmStockTakingTypeEnum.DYNAMIC,
},
},
{
fieldName: 'endTime',
label: '结束时间',
component: 'DatePicker',
componentProps: {
placeholder: '请选择结束时间',
showTime: true,
valueFormat: 'x',
},
dependencies: {
triggerFields: ['type'],
show: (values) => values.type === MesWmStockTakingTypeEnum.DYNAMIC,
},
},
{
fieldName: 'takingDate',
label: '盘点日期',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD',
placeholder: '请选择盘点日期',
valueFormat: 'YYYY-MM-DD',
},
rules: 'required',
},
{
fieldName: 'blindFlag',
label: '是否盲盘',
component: 'Switch',
componentProps: {
checkedChildren: '是',
unCheckedChildren: '否',
},
rules: z.boolean().default(false),
},
{
fieldName: 'frozen',
label: '是否冻结库存',
component: 'Switch',
componentProps: {
checkedChildren: '是',
unCheckedChildren: '否',
},
rules: z.boolean().default(false),
},
{
fieldName: 'userId',
label: '盘点人',
component: 'ApiSelect',
componentProps: {
allowClear: true,
api: getSimpleUserList,
labelField: 'nickname',
placeholder: '请选择盘点人',
valueField: 'id',
},
rules: 'selectRequired',
},
{
fieldName: 'remark',
label: '备注',
component: 'Textarea',
formItemClass: 'col-span-3',
componentProps: {
placeholder: '请输入备注',
rows: 3,
},
},
];
}
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'type',
label: '盘点类型',
component: 'Select',
componentProps: {
allowClear: true,
options: getDictOptions(DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, 'number'),
placeholder: '请选择盘点类型',
},
},
{
fieldName: 'code',
label: '任务编码',
component: 'Input',
componentProps: {
allowClear: true,
placeholder: '请输入任务编码',
},
},
{
fieldName: 'name',
label: '任务名称',
component: 'Input',
componentProps: {
allowClear: true,
placeholder: '请输入任务名称',
},
},
{
fieldName: 'takingDate',
label: '盘点日期',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
{
fieldName: 'status',
label: '单据状态',
component: 'Select',
componentProps: {
allowClear: true,
options: getDictOptions(
DICT_TYPE.MES_WM_STOCK_TAKING_TASK_STATUS,
'number',
),
placeholder: '请选择单据状态',
},
},
];
}
/** 列表的字段 */
export function useGridColumns(): VxeTableGridOptions<MesWmStockTakingTaskApi.StockTakingTask>['columns'] {
return [
{
field: 'code',
title: '任务编码',
minWidth: 160,
slots: { default: 'code' },
},
{
field: 'name',
title: '任务名称',
minWidth: 160,
},
{
field: 'type',
title: '盘点类型',
minWidth: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_TYPE },
},
},
{
field: 'planName',
title: '盘点方案',
minWidth: 180,
},
{
field: 'takingDate',
title: '盘点日期',
minWidth: 180,
formatter: 'formatDate',
},
{
field: 'userNickname',
title: '盘点人',
minWidth: 120,
},
{
field: 'status',
title: '单据状态',
minWidth: 110,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_TASK_STATUS },
},
},
{
title: '操作',
width: 280,
fixed: 'right',
slots: { default: 'actions' },
},
];
}
/** 盘点任务行列表的字段 */
export function useLineGridColumns(
editable = true,
): VxeTableGridOptions<MesWmStockTakingTaskLineApi.StockTakingTaskLine>['columns'] {
return [
{
field: 'itemCode',
title: '物料编码',
minWidth: 140,
},
{
field: 'itemName',
title: '物料名称',
minWidth: 160,
},
{
field: 'specification',
title: '规格型号',
minWidth: 120,
},
{
field: 'unitMeasureName',
title: '单位',
width: 90,
},
{
field: 'batchCode',
title: '批次号',
minWidth: 120,
},
{
field: 'quantity',
title: '在库数量',
width: 120,
},
{
field: 'warehouseName',
title: '仓库',
minWidth: 120,
},
{
field: 'locationName',
title: '库区',
minWidth: 120,
},
{
field: 'areaName',
title: '库位',
minWidth: 120,
},
{
field: 'status',
title: '状态',
minWidth: 100,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_LINE_STATUS },
},
},
...(editable
? [
{
title: '操作',
width: 80,
fixed: 'right',
slots: { default: 'actions' },
} as const,
]
: []),
];
}
/** 盘点结果列表的字段 */
export function useResultGridColumns(
editable = true,
): VxeTableGridOptions<MesWmStockTakingResultApi.StockTakingResult>['columns'] {
return [
{
field: 'itemCode',
title: '产品物料编码',
minWidth: 140,
},
{
field: 'itemName',
title: '产品物料名称',
minWidth: 160,
},
{
field: 'specification',
title: '规格型号',
minWidth: 120,
},
{
field: 'unitMeasureName',
title: '单位名称',
width: 90,
},
{
field: 'warehouseName',
title: '仓库',
minWidth: 120,
},
{
field: 'locationName',
title: '库区',
minWidth: 120,
},
{
field: 'areaName',
title: '库位',
minWidth: 120,
},
{
field: 'quantity',
title: '数量',
minWidth: 120,
},
{
field: 'takingQuantity',
title: '盘点数量',
minWidth: 120,
},
...(editable
? [
{
title: '操作',
width: 160,
fixed: 'right',
slots: { default: 'actions' },
} as const,
]
: []),
];
}
/** 盘点结果新增/修改的表单 */
export function useResultFormSchema(
formApi?: VbenFormApi,
taskLines: MesWmStockTakingTaskLineApi.StockTakingTaskLine[] = [],
): VbenFormSchema[] {
return [
{
fieldName: 'lineId',
label: '盘点清单',
component: 'Select',
componentProps: {
allowClear: true,
// 选择盘点清单后,自动带出物料、批次和仓储位置信息
onChange: async (lineId?: number) => {
const line = taskLines.find((item) => item.id === lineId);
await formApi?.setValues({
areaId: line?.areaId,
batchCode: line?.batchCode,
itemId: line?.itemId,
locationId: line?.locationId,
materialStockId: line?.materialStockId,
warehouseId: line?.warehouseId,
});
},
options: taskLines.map((line) => ({
label: `${line.itemCode} - ${line.itemName} (${line.warehouseName}${
line.locationName ? ` / ${line.locationName}` : ''
}${line.areaName ? ` / ${line.areaName}` : ''})`,
value: line.id,
})),
placeholder: '请选择盘点清单(可选)',
},
formItemClass: 'col-span-3',
},
{
fieldName: 'itemId',
label: '物料',
component: markRaw(MdItemSelect),
componentProps: {
placeholder: '请选择物料',
},
rules: 'selectRequired',
},
{
fieldName: 'batchCode',
label: '批次编码',
component: 'Input',
componentProps: {
placeholder: '请输入批次编码',
},
},
{
fieldName: 'takingQuantity',
label: '盘点数量',
component: 'InputNumber',
componentProps: {
class: '!w-full',
placeholder: '请输入盘点数量',
precision: 2,
},
rules: 'required',
},
{
fieldName: 'warehouseId',
label: '仓库',
component: markRaw(WmWarehouseSelect),
componentProps: {
// 仓库变化时清空库区和库位
onChange: () =>
formApi?.setValues({
areaId: undefined,
locationId: undefined,
}),
placeholder: '请选择仓库',
},
rules: 'selectRequired',
},
{
fieldName: 'locationId',
label: '库区',
component: markRaw(WmWarehouseLocationSelect),
rules: 'selectRequired',
dependencies: {
triggerFields: ['warehouseId'],
show: (values) => !!values.warehouseId,
componentProps: (values) => ({
onChange: () => formApi?.setFieldValue('areaId', undefined),
placeholder: '请选择库区',
warehouseId: values.warehouseId,
}),
},
},
{
fieldName: 'areaId',
label: '库位',
component: markRaw(WmWarehouseAreaSelect),
rules: 'selectRequired',
dependencies: {
triggerFields: ['locationId'],
show: (values) => !!values.locationId,
componentProps: (values) => ({
locationId: values.locationId,
placeholder: '请选择库位',
}),
},
},
{
fieldName: 'remark',
label: '备注',
component: 'Textarea',
formItemClass: 'col-span-3',
componentProps: {
placeholder: '请输入备注',
rows: 3,
},
},
];
}

View File

@ -0,0 +1,209 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingTaskApi } from '#/api/mes/wm/stocktaking/task';
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
cancelStockTaking,
deleteStockTaking,
exportStockTaking,
getStockTakingPage,
} from '#/api/mes/wm/stocktaking/task';
import { $t } from '#/locales';
import { MesWmStockTakingTaskStatusEnum } from '#/views/mes/utils/constants';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
destroyOnClose: true,
});
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 创建盘点任务 */
function handleCreate() {
formModalApi.setData({ formType: 'create' }).open();
}
/** 查看盘点任务 */
function handleDetail(row: MesWmStockTakingTaskApi.StockTakingTask) {
formModalApi.setData({ formType: 'detail', id: row.id }).open();
}
/** 编辑盘点任务 */
function handleEdit(row: MesWmStockTakingTaskApi.StockTakingTask) {
formModalApi.setData({ formType: 'update', id: row.id }).open();
}
/** 提交盘点任务 */
function handleSubmit(row: MesWmStockTakingTaskApi.StockTakingTask) {
formModalApi.setData({ formType: 'submit', id: row.id }).open();
}
/** 执行盘点 */
function handleExecute(row: MesWmStockTakingTaskApi.StockTakingTask) {
formModalApi.setData({ formType: 'execute', id: row.id }).open();
}
/** 取消盘点任务 */
async function handleCancel(row: MesWmStockTakingTaskApi.StockTakingTask) {
await cancelStockTaking(row.id!);
message.success('取消成功');
handleRefresh();
}
/** 删除盘点任务 */
async function handleDelete(row: MesWmStockTakingTaskApi.StockTakingTask) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.code]),
duration: 0,
});
try {
await deleteStockTaking(row.id!);
message.success($t('ui.actionMessage.deleteSuccess', [row.code]));
handleRefresh();
} finally {
hideLoading();
}
}
/** 导出表格 */
async function handleExport() {
const data = await exportStockTaking(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '盘点任务.xls', source: data });
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getStockTakingPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<MesWmStockTakingTaskApi.StockTakingTask>,
});
const StatusEnum = MesWmStockTakingTaskStatusEnum;
</script>
<template>
<Page auto-content-height>
<template #doc>
<DocAlert
title="【仓库】库存盘点"
url="https://doc.iocoder.cn/mes/wm/stocktaking/"
/>
</template>
<FormModal @success="handleRefresh" />
<Grid table-title="">
<template #toolbar-tools>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['盘点任务']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['mes:wm-stock-taking-task:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['mes:wm-stock-taking-task:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #code="{ row }">
<Button type="link" @click="handleDetail(row)">
{{ row.code }}
</Button>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['mes:wm-stock-taking-task:update'],
ifShow: row.status === StatusEnum.PREPARE,
onClick: handleEdit.bind(null, row),
},
{
label: '提交',
type: 'link',
auth: ['mes:wm-stock-taking-task:update'],
ifShow: row.status === StatusEnum.PREPARE,
onClick: handleSubmit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['mes:wm-stock-taking-task:delete'],
ifShow: row.status === StatusEnum.PREPARE,
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.code]),
confirm: handleDelete.bind(null, row),
},
},
{
label: '执行盘点',
type: 'link',
auth: ['mes:wm-stock-taking-task:update'],
ifShow: row.status === StatusEnum.APPROVING,
onClick: handleExecute.bind(null, row),
},
{
label: '取消',
type: 'link',
danger: true,
auth: ['mes:wm-stock-taking-task:update'],
ifShow: row.status === StatusEnum.APPROVING,
popConfirm: {
title: '确认取消该盘点任务?取消后不可恢复。',
confirm: handleCancel.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@ -0,0 +1,233 @@
<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesWmStockTakingTaskApi } from '#/api/mes/wm/stocktaking/task';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { useUserStore } from '@vben/stores';
import { Button, message, Popconfirm, Tabs } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import {
createStockTaking,
finishStockTaking,
getStockTaking,
submitStockTaking,
updateStockTaking,
} from '#/api/mes/wm/stocktaking/task';
import { $t } from '#/locales';
import { MesWmStockTakingTaskStatusEnum } from '#/views/mes/utils/constants';
import { useFormSchema } from '../data';
import LineList from './line-list.vue';
import ResultList from './result-list.vue';
const emit = defineEmits(['success']);
const userStore = useUserStore();
const formType = ref<FormType>('create');
const formData = ref<MesWmStockTakingTaskApi.StockTakingTask>();
const subTabsName = ref('lines'); // tab
const originalSnapshot = ref(''); //
const isEditable = computed(() =>
['create', 'update'].includes(formType.value),
);
const isExecute = computed(() => formType.value === 'execute'); //
const isSubmit = computed(() => formType.value === 'submit'); //
const canSubmit = computed(
() =>
formType.value === 'update' &&
formData.value?.status === MesWmStockTakingTaskStatusEnum.PREPARE,
);
const showLineTab = computed(() => !formData.value?.blindFlag); //
const showResultTab = computed(
() =>
isExecute.value ||
(!!formData.value?.status &&
formData.value.status !== MesWmStockTakingTaskStatusEnum.PREPARE),
);
// TODO @AI
const getTitle = computed(() => {
switch (formType.value) {
case 'detail': {
return $t('ui.actionTitle.view', ['盘点任务']);
}
case 'execute': {
return '执行盘点';
}
case 'submit': {
return '提交盘点任务';
}
case 'update': {
return $t('ui.actionTitle.edit', ['盘点任务']);
}
default: {
return $t('ui.actionTitle.create', ['盘点任务']);
}
}
});
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-1',
labelWidth: 110,
},
layout: 'horizontal',
schema: [],
showDefaultActions: false,
wrapperClass: 'grid-cols-3',
});
/** 提交盘点任务:表单有修改时先保存,再调用提交接口 */
async function handleSubmit() {
const { valid } = await formApi.validate();
if (!valid || !formData.value?.id) {
return;
}
modalApi.lock();
try {
const current = JSON.stringify(await formApi.getValues());
if (current !== originalSnapshot.value) {
const data =
(await formApi.getValues()) as MesWmStockTakingTaskApi.StockTakingTask;
await updateStockTaking({ ...formData.value, ...data });
originalSnapshot.value = current;
}
await submitStockTaking(formData.value.id);
message.success('提交成功');
await modalApi.close();
emit('success');
} finally {
modalApi.unlock();
}
}
/** 执行盘点 */
async function handleExecute() {
if (!formData.value?.id) {
return;
}
modalApi.lock();
try {
await finishStockTaking(formData.value.id);
message.success('执行盘点成功');
await modalApi.close();
emit('success');
} finally {
modalApi.unlock();
}
}
const [Modal, modalApi] = useVbenModal({
// TODO @AI
async onConfirm() {
if (!isEditable.value) {
await modalApi.close();
return;
}
const { valid } = await formApi.validate();
if (!valid) {
return;
}
modalApi.lock();
//
const data =
(await formApi.getValues()) as MesWmStockTakingTaskApi.StockTakingTask;
try {
if (formData.value?.id) {
await updateStockTaking({ ...formData.value, ...data });
formData.value = { ...formData.value, ...data };
} else {
const id = await createStockTaking(data);
formData.value = {
...data,
id,
status: MesWmStockTakingTaskStatusEnum.PREPARE,
};
await formApi.setFieldValue('id', id);
await formApi.setFieldValue('status', formData.value.status);
formType.value = 'update';
}
originalSnapshot.value = JSON.stringify(await formApi.getValues());
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
originalSnapshot.value = '';
return;
}
//
const data = modalApi.getData<{ formType: FormType; id?: number }>();
formType.value = data.formType;
subTabsName.value = data.formType === 'execute' ? 'results' : 'lines';
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
formApi.setDisabled(!isEditable.value);
modalApi.setState({ showConfirmButton: isEditable.value });
if (data?.id) {
modalApi.lock();
try {
formData.value = await getStockTaking(data.id);
// values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
} else {
//
await formApi.setFieldValue('userId', userStore.userInfo?.id);
}
originalSnapshot.value = JSON.stringify(await formApi.getValues());
},
});
</script>
<template>
<Modal :title="getTitle" class="w-4/5">
<Form class="mx-4" />
<Tabs
v-if="formData?.id"
v-model:active-key="subTabsName"
class="mx-4 mt-4"
type="card"
>
<Tabs.TabPane v-if="showLineTab" key="lines" tab="盘点清单">
<LineList :form-type="formType" :task-id="formData.id" />
</Tabs.TabPane>
<Tabs.TabPane v-if="showResultTab" key="results" tab="盘点结果">
<ResultList
:form-type="isExecute ? 'execute' : 'detail'"
:task-id="formData.id"
/>
</Tabs.TabPane>
</Tabs>
<template #prepend-footer>
<div class="flex flex-auto items-center gap-2">
<Popconfirm
v-if="canSubmit || isSubmit"
title="确认提交该盘点任务?【提交后将不能修改】"
@confirm="handleSubmit"
>
<Button type="primary">提交</Button>
</Popconfirm>
<Popconfirm
v-if="isExecute"
title="确认执行盘点操作?"
@confirm="handleExecute"
>
<Button type="primary">执行盘点</Button>
</Popconfirm>
</div>
</template>
</Modal>
</template>

View File

@ -0,0 +1,147 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
import type { MesWmStockTakingTaskLineApi } from '#/api/mes/wm/stocktaking/task/line';
import { computed, ref } from 'vue';
import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
createStockTakingTaskLine,
deleteStockTakingTaskLine,
getStockTakingTaskLinePage,
} from '#/api/mes/wm/stocktaking/task/line';
import { $t } from '#/locales';
import { WmMaterialStockSelectDialog } from '#/views/mes/wm/materialstock/components';
import { type FormType, useLineGridColumns } from '../data';
const props = defineProps<{
formType: FormType;
taskId: number;
}>();
const isEditable = computed(() => props.formType === 'update'); //
const dialogRef = ref<InstanceType<typeof WmMaterialStockSelectDialog>>();
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 打开库存选择弹窗批量添加物料 */
function handleAdd() {
dialogRef.value?.open([], { multiple: true });
}
/** 库存选择确认回调:将选中的库存记录批量创建为盘点行 */
async function handleStockSelected(rows: MesWmMaterialStockApi.MaterialStock[]) {
if (rows.length === 0) {
return;
}
for (const stock of rows) {
await createStockTakingTaskLine({
areaId: stock.areaId,
batchId: stock.batchId,
itemId: stock.itemId,
locationId: stock.locationId,
materialStockId: stock.id,
quantity: stock.quantity,
taskId: props.taskId,
warehouseId: stock.warehouseId,
});
}
message.success(`成功添加 ${rows.length} 条盘点行`);
handleRefresh();
}
/** 删除盘点行 */
async function handleDelete(
row: MesWmStockTakingTaskLineApi.StockTakingTaskLine,
) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.itemName]),
duration: 0,
});
try {
await deleteStockTakingTaskLine(row.id!);
message.success($t('ui.actionMessage.deleteSuccess', [row.itemName]));
handleRefresh();
} finally {
hideLoading();
}
}
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: useLineGridColumns(isEditable.value),
height: 360,
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }) => {
if (!props.taskId) {
return { list: [], total: 0 };
}
return await getStockTakingTaskLinePage({
pageNo: page.currentPage,
pageSize: page.pageSize,
taskId: props.taskId,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
},
} as VxeTableGridOptions<MesWmStockTakingTaskLineApi.StockTakingTaskLine>,
});
defineExpose({ refresh: handleRefresh });
</script>
<template>
<div>
<WmMaterialStockSelectDialog
ref="dialogRef"
@selected="handleStockSelected"
/>
<Grid table-title="">
<template v-if="isEditable" #toolbar-tools>
<TableAction
:actions="[
{
label: '添加物料',
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['mes:wm-stock-taking-task:update'],
onClick: handleAdd,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.itemName]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</div>
</template>

View File

@ -0,0 +1,106 @@
<script lang="ts" setup>
import type { MesWmStockTakingResultApi } from '#/api/mes/wm/stocktaking/task/result';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { getStockTakingTaskLineSimpleList } from '#/api/mes/wm/stocktaking/task/line';
import {
createStockTakingResult,
getStockTakingResult,
updateStockTakingResult,
} from '#/api/mes/wm/stocktaking/task/result';
import { $t } from '#/locales';
import { useResultFormSchema } from '../data';
const emit = defineEmits(['success']);
const formData = ref<MesWmStockTakingResultApi.StockTakingResult>();
const taskId = ref<number>(); // TODO @AI
const isExecute = ref(false); //
const getTitle = computed(() =>
formData.value?.id
? $t('ui.actionTitle.edit', ['盘点结果'])
: $t('ui.actionTitle.create', ['盘点结果']),
);
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-1',
labelWidth: 100,
},
layout: 'horizontal',
schema: [],
showDefaultActions: false,
wrapperClass: 'grid-cols-3',
});
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
const { valid } = await formApi.validate();
if (!valid) {
return;
}
modalApi.lock();
//
const data =
(await formApi.getValues()) as MesWmStockTakingResultApi.StockTakingResult;
data.taskId = taskId.value;
try {
await (formData.value?.id
? updateStockTakingResult({ ...data, id: formData.value.id })
: createStockTakingResult(data));
//
await modalApi.close();
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
return;
}
//
const data = modalApi.getData<{
execute?: boolean;
id?: number;
taskId: number;
}>();
taskId.value = data.taskId;
isExecute.value = !!data.execute;
//
const taskLines =
isExecute.value && !data.id
? await getStockTakingTaskLineSimpleList(data.taskId)
: [];
formApi.setState({ schema: useResultFormSchema(formApi, taskLines) });
if (!data.id) {
return;
}
modalApi.lock();
try {
formData.value = await getStockTakingResult(data.id);
// values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
},
});
</script>
<template>
<Modal :title="getTitle" class="w-3/5">
<Form />
</Modal>
</template>

View File

@ -0,0 +1,140 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingResultApi } from '#/api/mes/wm/stocktaking/task/result';
import { computed } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteStockTakingResult,
getStockTakingResultPage,
} from '#/api/mes/wm/stocktaking/task/result';
import { $t } from '#/locales';
import { useResultGridColumns } from '../data';
import ResultForm from './result-form.vue';
const props = defineProps<{
formType: 'detail' | 'execute';
taskId: number;
}>();
const isExecute = computed(() => props.formType === 'execute'); //
const [ResultFormModal, resultFormModalApi] = useVbenModal({
connectedComponent: ResultForm,
destroyOnClose: true,
});
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 新增盘点结果 */
function handleCreate() {
resultFormModalApi
.setData({ execute: true, taskId: props.taskId })
.open();
}
/** 编辑盘点结果 */
function handleEdit(row: MesWmStockTakingResultApi.StockTakingResult) {
resultFormModalApi.setData({ id: row.id, taskId: props.taskId }).open();
}
/** 删除盘点结果 */
async function handleDelete(
row: MesWmStockTakingResultApi.StockTakingResult,
) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.itemName]),
duration: 0,
});
try {
await deleteStockTakingResult(row.id!);
message.success($t('ui.actionMessage.deleteSuccess', [row.itemName]));
handleRefresh();
} finally {
hideLoading();
}
}
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: useResultGridColumns(isExecute.value),
height: 360,
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }) => {
if (!props.taskId) {
return { list: [], total: 0 };
}
return await getStockTakingResultPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
taskId: props.taskId,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
},
} as VxeTableGridOptions<MesWmStockTakingResultApi.StockTakingResult>,
});
defineExpose({ refresh: handleRefresh });
</script>
<template>
<div>
<ResultFormModal @success="handleRefresh" />
<Grid table-title="">
<template v-if="isExecute" #toolbar-tools>
<TableAction
:actions="[
{
label: $t('common.create'),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['mes:wm-stock-taking-task:update'],
onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.itemName]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</div>
</template>

View File

@ -53,6 +53,7 @@
"camunda-bpmn-moddle": "catalog:",
"cropperjs": "catalog:",
"dayjs": "catalog:",
"dhtmlx-gantt": "catalog:",
"diagram-js": "catalog:",
"element-plus": "catalog:",
"fast-xml-parser": "catalog:",

View File

@ -0,0 +1,62 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesProCardProcessApi {
/** MES 流转卡工序记录 */
export interface CardProcess {
id?: number; // 编号
cardId?: number; // 流转卡编号
sort?: number; // 序号
processId?: number; // 工序编号
processCode?: string; // 工序编码
processName?: string; // 工序名称
inputTime?: number; // 进入工序时间
outputTime?: number; // 出工序时间
inputQuantity?: number; // 投入数量
outputQuantity?: number; // 产出数量
unqualifiedQuantity?: number; // 不合格品数量
workstationId?: number; // 工位编号
workstationCode?: string; // 工位编码
workstationName?: string; // 工位名称
userId?: number; // 操作人编号
nickname?: string; // 操作人名称
ipqcId?: number; // 过程检验单编号
remark?: string; // 备注
}
/** MES 流转卡工序记录分页查询参数 */
export interface PageParams extends PageParam {
cardId?: number;
}
}
/** 查询流转卡工序记录分页 */
export function getCardProcessPage(params: MesProCardProcessApi.PageParams) {
return requestClient.get<PageResult<MesProCardProcessApi.CardProcess>>(
'/mes/pro/card-process/page',
{ params },
);
}
/** 查询流转卡工序记录详情 */
export function getCardProcess(id: number) {
return requestClient.get<MesProCardProcessApi.CardProcess>(
`/mes/pro/card-process/get?id=${id}`,
);
}
/** 新增流转卡工序记录 */
export function createCardProcess(data: MesProCardProcessApi.CardProcess) {
return requestClient.post('/mes/pro/card-process/create', data);
}
/** 修改流转卡工序记录 */
export function updateCardProcess(data: MesProCardProcessApi.CardProcess) {
return requestClient.put('/mes/pro/card-process/update', data);
}
/** 删除流转卡工序记录 */
export function deleteCardProcess(id: number) {
return requestClient.delete(`/mes/pro/card-process/delete?id=${id}`);
}

View File

@ -0,0 +1,62 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesProWorkOrderBomApi {
/** MES 生产工单 BOM */
export interface WorkOrderBom {
id?: number; // 编号
workOrderId?: number; // 生产工单编号
itemId?: number; // BOM 物料编号
itemName?: string; // 物料名称
itemCode?: string; // 物料编码
itemSpecification?: string; // 规格型号
unitMeasureId?: number; // 单位编号
unitMeasureName?: string; // 单位名称
quantity?: number; // 预计使用量
remark?: string; // 备注
itemOrProduct?: string; // 物料产品标识
}
/** MES 生产工单 BOM 分页查询参数 */
export interface PageParams extends PageParam {
workOrderId?: number;
}
}
/** 查询工单 BOM 分页 */
export function getWorkOrderBomPage(params: MesProWorkOrderBomApi.PageParams) {
return requestClient.get<PageResult<MesProWorkOrderBomApi.WorkOrderBom>>(
'/mes/pro/work-order-bom/page',
{ params },
);
}
/** 查询工单 BOM 详情 */
export function getWorkOrderBom(id: number) {
return requestClient.get<MesProWorkOrderBomApi.WorkOrderBom>(
`/mes/pro/work-order-bom/get?id=${id}`,
);
}
/** 新增工单 BOM */
export function createWorkOrderBom(data: MesProWorkOrderBomApi.WorkOrderBom) {
return requestClient.post('/mes/pro/work-order-bom/create', data);
}
/** 修改工单 BOM */
export function updateWorkOrderBom(data: MesProWorkOrderBomApi.WorkOrderBom) {
return requestClient.put('/mes/pro/work-order-bom/update', data);
}
/** 删除工单 BOM */
export function deleteWorkOrderBom(id: number) {
return requestClient.delete(`/mes/pro/work-order-bom/delete?id=${id}`);
}
/** 查询工单物料需求列表 */
export function getWorkOrderBomItemListByWorkOrderId(workOrderId: number) {
return requestClient.get<MesProWorkOrderBomApi.WorkOrderBom[]>(
`/mes/pro/work-order-bom/item-list-by-work-order-id?workOrderId=${workOrderId}`,
);
}

View File

@ -0,0 +1,68 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmArrivalNoticeApi {
/** MES 到货通知单 */
export interface ArrivalNotice {
id?: number; // 通知单编号
code?: string; // 通知单编号
name?: string; // 通知单名称
purchaseOrderCode?: string; // 采购订单编号
vendorId?: number; // 供应商编号
vendorCode?: string; // 供应商编码
vendorName?: string; // 供应商名称
arrivalDate?: number; // 到货日期
contactName?: string; // 联系人
contactTelephone?: string; // 联系方式
status?: number; // 单据状态
remark?: string; // 备注
createTime?: Date; // 创建时间
}
}
/** 查询到货通知单分页 */
export function getArrivalNoticePage(params: PageParam) {
return requestClient.get<PageResult<MesWmArrivalNoticeApi.ArrivalNotice>>(
'/mes/wm/arrival-notice/page',
{ params },
);
}
/** 查询到货通知单详情 */
export function getArrivalNotice(id: number) {
return requestClient.get<MesWmArrivalNoticeApi.ArrivalNotice>(
`/mes/wm/arrival-notice/get?id=${id}`,
);
}
/** 新增到货通知单 */
export function createArrivalNotice(
data: MesWmArrivalNoticeApi.ArrivalNotice,
) {
return requestClient.post<number>('/mes/wm/arrival-notice/create', data);
}
/** 修改到货通知单 */
export function updateArrivalNotice(
data: MesWmArrivalNoticeApi.ArrivalNotice,
) {
return requestClient.put('/mes/wm/arrival-notice/update', data);
}
/** 删除到货通知单 */
export function deleteArrivalNotice(id: number) {
return requestClient.delete(`/mes/wm/arrival-notice/delete?id=${id}`);
}
/** 提交到货通知单 */
export function submitArrivalNotice(id: number) {
return requestClient.put(`/mes/wm/arrival-notice/submit?id=${id}`);
}
/** 导出到货通知单 */
export function exportArrivalNotice(params: any) {
return requestClient.download('/mes/wm/arrival-notice/export-excel', {
params,
});
}

View File

@ -0,0 +1,58 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmArrivalNoticeLineApi {
/** MES 到货通知单行 */
export interface ArrivalNoticeLine {
id?: number; // 行编号
noticeId?: number; // 到货通知单编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 单位
arrivalQuantity?: number; // 到货数量
qualifiedQuantity?: number; // 合格数量
iqcCheckFlag?: boolean; // 是否检验
iqcId?: number; // 来料检验单编号
iqcCode?: string; // 来料检验单编码
remark?: string; // 备注
}
}
/** 查询到货通知单行分页 */
export function getArrivalNoticeLinePage(params: PageParam) {
return requestClient.get<
PageResult<MesWmArrivalNoticeLineApi.ArrivalNoticeLine>
>('/mes/wm/arrival-notice-line/page', { params });
}
/** 查询到货通知单行详情 */
export function getArrivalNoticeLine(id: number) {
return requestClient.get<MesWmArrivalNoticeLineApi.ArrivalNoticeLine>(
`/mes/wm/arrival-notice-line/get?id=${id}`,
);
}
/** 新增到货通知单行 */
export function createArrivalNoticeLine(
data: MesWmArrivalNoticeLineApi.ArrivalNoticeLine,
) {
return requestClient.post<number>(
'/mes/wm/arrival-notice-line/create',
data,
);
}
/** 修改到货通知单行 */
export function updateArrivalNoticeLine(
data: MesWmArrivalNoticeLineApi.ArrivalNoticeLine,
) {
return requestClient.put('/mes/wm/arrival-notice-line/update', data);
}
/** 删除到货通知单行 */
export function deleteArrivalNoticeLine(id: number) {
return requestClient.delete(`/mes/wm/arrival-notice-line/delete?id=${id}`);
}

View File

@ -0,0 +1,58 @@
import { requestClient } from '#/api/request';
export namespace MesWmItemReceiptDetailApi {
/** MES 采购入库明细 */
export interface ItemReceiptDetail {
id?: number; // 明细编号
lineId?: number; // 入库单行编号
receiptId?: number; // 入库单编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 单位
quantity?: number; // 数量
batchId?: number; // 批次编号
warehouseId?: number; // 仓库编号
warehouseName?: string; // 仓库名称
locationId?: number; // 库区编号
locationName?: string; // 库区名称
areaId?: number; // 库位编号
areaName?: string; // 库位名称
remark?: string; // 备注
}
}
/** 查询采购入库明细列表(按行编号) */
export function getItemReceiptDetailListByLineId(lineId: number) {
return requestClient.get<MesWmItemReceiptDetailApi.ItemReceiptDetail[]>(
'/mes/wm/item-receipt-detail/list-by-line',
{ params: { lineId } },
);
}
/** 查询采购入库明细详情 */
export function getItemReceiptDetail(id: number) {
return requestClient.get<MesWmItemReceiptDetailApi.ItemReceiptDetail>(
`/mes/wm/item-receipt-detail/get?id=${id}`,
);
}
/** 新增采购入库明细 */
export function createItemReceiptDetail(
data: MesWmItemReceiptDetailApi.ItemReceiptDetail,
) {
return requestClient.post<number>('/mes/wm/item-receipt-detail/create', data);
}
/** 修改采购入库明细 */
export function updateItemReceiptDetail(
data: MesWmItemReceiptDetailApi.ItemReceiptDetail,
) {
return requestClient.put('/mes/wm/item-receipt-detail/update', data);
}
/** 删除采购入库明细 */
export function deleteItemReceiptDetail(id: number) {
return requestClient.delete(`/mes/wm/item-receipt-detail/delete?id=${id}`);
}

View File

@ -0,0 +1,56 @@
import { requestClient } from '#/api/request';
export namespace MesWmProductIssueDetailApi {
/** MES 领料出库明细 */
export interface ProductIssueDetail {
id?: number; // 明细编号
issueId?: number; // 领料单编号
lineId?: number; // 领料单行编号
materialStockId?: number; // 库存记录编号
itemId?: number; // 物料编号
quantity?: number; // 数量
batchId?: number; // 批次编号
batchCode?: string; // 批次号
warehouseId?: number; // 仓库编号
warehouseName?: string; // 仓库名称
locationId?: number; // 库区编号
locationName?: string; // 库区名称
areaId?: number; // 库位编号
areaName?: string; // 库位名称
remark?: string; // 备注
}
}
/** 查询领料出库明细列表(按行编号) */
export function getProductIssueDetailListByLineId(lineId: number) {
return requestClient.get<MesWmProductIssueDetailApi.ProductIssueDetail[]>(
'/mes/wm/product-issue-detail/list-by-line',
{ params: { lineId } },
);
}
/** 查询领料出库明细详情 */
export function getProductIssueDetail(id: number) {
return requestClient.get<MesWmProductIssueDetailApi.ProductIssueDetail>(
`/mes/wm/product-issue-detail/get?id=${id}`,
);
}
/** 新增领料出库明细 */
export function createProductIssueDetail(
data: MesWmProductIssueDetailApi.ProductIssueDetail,
) {
return requestClient.post<number>('/mes/wm/product-issue-detail/create', data);
}
/** 修改领料出库明细 */
export function updateProductIssueDetail(
data: MesWmProductIssueDetailApi.ProductIssueDetail,
) {
return requestClient.put('/mes/wm/product-issue-detail/update', data);
}
/** 删除领料出库明细 */
export function deleteProductIssueDetail(id: number) {
return requestClient.delete(`/mes/wm/product-issue-detail/delete?id=${id}`);
}

View File

@ -0,0 +1,87 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmProductIssueApi {
/** MES 领料出库单 */
export interface ProductIssue {
id?: number; // 领料单编号
code?: string; // 领料单编号
name?: string; // 领料单名称
workstationId?: number; // 工作站编号
workstationCode?: string; // 工作站编码
workstationName?: string; // 工作站名称
workOrderId?: number; // 生产工单编号
workOrderCode?: string; // 生产工单编码
clientCode?: string; // 客户编码
clientName?: string; // 客户名称
requiredTime?: number; // 需求时间
status?: number; // 单据状态
remark?: string; // 备注
createTime?: Date; // 创建时间
}
}
/** 查询领料出库单分页 */
export function getProductIssuePage(params: PageParam) {
return requestClient.get<PageResult<MesWmProductIssueApi.ProductIssue>>(
'/mes/wm/product-issue/page',
{ params },
);
}
/** 查询领料出库单详情 */
export function getProductIssue(id: number) {
return requestClient.get<MesWmProductIssueApi.ProductIssue>(
`/mes/wm/product-issue/get?id=${id}`,
);
}
/** 新增领料出库单 */
export function createProductIssue(data: MesWmProductIssueApi.ProductIssue) {
return requestClient.post<number>('/mes/wm/product-issue/create', data);
}
/** 修改领料出库单 */
export function updateProductIssue(data: MesWmProductIssueApi.ProductIssue) {
return requestClient.put('/mes/wm/product-issue/update', data);
}
/** 删除领料出库单 */
export function deleteProductIssue(id: number) {
return requestClient.delete(`/mes/wm/product-issue/delete?id=${id}`);
}
/** 提交领料出库单 */
export function submitProductIssue(id: number) {
return requestClient.put(`/mes/wm/product-issue/submit?id=${id}`);
}
/** 执行拣货 */
export function stockProductIssue(id: number) {
return requestClient.put(`/mes/wm/product-issue/stock?id=${id}`);
}
/** 完成领料出库单 */
export function finishProductIssue(id: number) {
return requestClient.put(`/mes/wm/product-issue/finish?id=${id}`);
}
/** 取消领料出库单 */
export function cancelProductIssue(id: number) {
return requestClient.put(`/mes/wm/product-issue/cancel?id=${id}`);
}
/** 校验领料出库单拣货数量是否与领料数量一致 */
export function checkProductIssueQuantity(id: number) {
return requestClient.get<boolean>(
`/mes/wm/product-issue/check-quantity?id=${id}`,
);
}
/** 导出领料出库单 */
export function exportProductIssue(params: any) {
return requestClient.download('/mes/wm/product-issue/export-excel', {
params,
});
}

View File

@ -0,0 +1,53 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmProductIssueLineApi {
/** MES 领料出库单行 */
export interface ProductIssueLine {
id?: number; // 行编号
issueId?: number; // 领料单编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 单位
quantity?: number; // 领料数量
batchId?: number; // 批次编号
remark?: string; // 备注
}
}
/** 查询领料出库单行分页 */
export function getProductIssueLinePage(params: PageParam) {
return requestClient.get<PageResult<MesWmProductIssueLineApi.ProductIssueLine>>(
'/mes/wm/product-issue-line/page',
{ params },
);
}
/** 查询领料出库单行详情 */
export function getProductIssueLine(id: number) {
return requestClient.get<MesWmProductIssueLineApi.ProductIssueLine>(
`/mes/wm/product-issue-line/get?id=${id}`,
);
}
/** 新增领料出库单行 */
export function createProductIssueLine(
data: MesWmProductIssueLineApi.ProductIssueLine,
) {
return requestClient.post<number>('/mes/wm/product-issue-line/create', data);
}
/** 修改领料出库单行 */
export function updateProductIssueLine(
data: MesWmProductIssueLineApi.ProductIssueLine,
) {
return requestClient.put('/mes/wm/product-issue-line/update', data);
}
/** 删除领料出库单行 */
export function deleteProductIssueLine(id: number) {
return requestClient.delete(`/mes/wm/product-issue-line/delete?id=${id}`);
}

View File

@ -0,0 +1,58 @@
import { requestClient } from '#/api/request';
export namespace MesWmProductReceiptDetailApi {
/** MES 产品入库明细 */
export interface ProductReceiptDetail {
id?: number; // 明细编号
lineId?: number; // 入库单行编号
receiptId?: number; // 入库单编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
quantity?: number; // 数量
batchId?: number; // 批次编号
warehouseId?: number; // 仓库编号
warehouseName?: string; // 仓库名称
locationId?: number; // 库区编号
locationName?: string; // 库区名称
areaId?: number; // 库位编号
areaName?: string; // 库位名称
remark?: string; // 备注
}
}
/** 查询产品入库明细列表(按行编号) */
export function getProductReceiptDetailListByLineId(lineId: number) {
return requestClient.get<MesWmProductReceiptDetailApi.ProductReceiptDetail[]>(
'/mes/wm/product-receipt-detail/list-by-line',
{ params: { lineId } },
);
}
/** 查询产品入库明细详情 */
export function getProductReceiptDetail(id: number) {
return requestClient.get<MesWmProductReceiptDetailApi.ProductReceiptDetail>(
`/mes/wm/product-receipt-detail/get?id=${id}`,
);
}
/** 新增产品入库明细 */
export function createProductReceiptDetail(
data: MesWmProductReceiptDetailApi.ProductReceiptDetail,
) {
return requestClient.post<number>(
'/mes/wm/product-receipt-detail/create',
data,
);
}
/** 修改产品入库明细 */
export function updateProductReceiptDetail(
data: MesWmProductReceiptDetailApi.ProductReceiptDetail,
) {
return requestClient.put('/mes/wm/product-receipt-detail/update', data);
}
/** 删除产品入库明细 */
export function deleteProductReceiptDetail(id: number) {
return requestClient.delete(`/mes/wm/product-receipt-detail/delete?id=${id}`);
}

View File

@ -0,0 +1,91 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmProductReceiptApi {
/** MES 产品入库单 */
export interface ProductReceipt {
id?: number; // 入库单编号
code?: string; // 入库单编码
name?: string; // 入库单名称
workOrderId?: number; // 生产工单编号
workOrderCode?: string; // 生产工单编码
itemId?: number; // 产品物料编号
itemCode?: string; // 产品物料编码
itemName?: string; // 产品物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 单位
receiptDate?: number; // 入库日期
status?: number; // 单据状态
remark?: string; // 备注
createTime?: Date; // 创建时间
}
}
/** 查询产品入库单分页 */
export function getProductReceiptPage(params: PageParam) {
return requestClient.get<PageResult<MesWmProductReceiptApi.ProductReceipt>>(
'/mes/wm/product-receipt/page',
{ params },
);
}
/** 查询产品入库单详情 */
export function getProductReceipt(id: number) {
return requestClient.get<MesWmProductReceiptApi.ProductReceipt>(
`/mes/wm/product-receipt/get?id=${id}`,
);
}
/** 新增产品入库单 */
export function createProductReceipt(
data: MesWmProductReceiptApi.ProductReceipt,
) {
return requestClient.post<number>('/mes/wm/product-receipt/create', data);
}
/** 修改产品入库单 */
export function updateProductReceipt(
data: MesWmProductReceiptApi.ProductReceipt,
) {
return requestClient.put('/mes/wm/product-receipt/update', data);
}
/** 删除产品入库单 */
export function deleteProductReceipt(id: number) {
return requestClient.delete(`/mes/wm/product-receipt/delete?id=${id}`);
}
/** 提交产品入库单 */
export function submitProductReceipt(id: number) {
return requestClient.put(`/mes/wm/product-receipt/submit?id=${id}`);
}
/** 执行上架 */
export function stockProductReceipt(id: number) {
return requestClient.put(`/mes/wm/product-receipt/stock?id=${id}`);
}
/** 执行入库 */
export function finishProductReceipt(id: number) {
return requestClient.put(`/mes/wm/product-receipt/finish?id=${id}`);
}
/** 取消产品入库单 */
export function cancelProductReceipt(id: number) {
return requestClient.put(`/mes/wm/product-receipt/cancel?id=${id}`);
}
/** 校验产品入库单明细数量是否与行收货数量一致 */
export function checkProductReceiptQuantity(id: number) {
return requestClient.get<boolean>(
`/mes/wm/product-receipt/check-quantity?id=${id}`,
);
}
/** 导出产品入库单 */
export function exportProductReceipt(params: any) {
return requestClient.download('/mes/wm/product-receipt/export-excel', {
params,
});
}

View File

@ -0,0 +1,57 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmProductReceiptLineApi {
/** MES 产品入库单行 */
export interface ProductReceiptLine {
id?: number; // 行编号
receiptId?: number; // 入库单编号
itemId?: number; // 物料编号
materialStockId?: number; // 库存记录编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 单位
quantity?: number; // 入库数量
batchId?: number; // 批次编号
batchCode?: string; // 批次号
remark?: string; // 备注
}
}
/** 查询产品入库单行分页 */
export function getProductReceiptLinePage(params: PageParam) {
return requestClient.get<
PageResult<MesWmProductReceiptLineApi.ProductReceiptLine>
>('/mes/wm/product-receipt-line/page', { params });
}
/** 查询产品入库单行详情 */
export function getProductReceiptLine(id: number) {
return requestClient.get<MesWmProductReceiptLineApi.ProductReceiptLine>(
`/mes/wm/product-receipt-line/get?id=${id}`,
);
}
/** 新增产品入库单行 */
export function createProductReceiptLine(
data: MesWmProductReceiptLineApi.ProductReceiptLine,
) {
return requestClient.post<number>(
'/mes/wm/product-receipt-line/create',
data,
);
}
/** 修改产品入库单行 */
export function updateProductReceiptLine(
data: MesWmProductReceiptLineApi.ProductReceiptLine,
) {
return requestClient.put('/mes/wm/product-receipt-line/update', data);
}
/** 删除产品入库单行 */
export function deleteProductReceiptLine(id: number) {
return requestClient.delete(`/mes/wm/product-receipt-line/delete?id=${id}`);
}

View File

@ -0,0 +1,58 @@
import { requestClient } from '#/api/request';
export namespace MesWmProductSalesDetailApi {
/** MES 销售出库明细 */
export interface ProductSalesDetail {
id?: number; // 明细编号
lineId?: number; // 出库单行编号
salesId?: number; // 出库单编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
quantity?: number; // 数量
materialStockId?: number; // 库存记录编号
batchId?: number; // 批次编号
batchCode?: string; // 批次号
warehouseId?: number; // 仓库编号
warehouseName?: string; // 仓库名称
locationId?: number; // 库区编号
locationName?: string; // 库区名称
areaId?: number; // 库位编号
areaName?: string; // 库位名称
remark?: string; // 备注
}
}
/** 查询销售出库明细列表(按行编号) */
export function getProductSalesDetailListByLineId(lineId: number) {
return requestClient.get<MesWmProductSalesDetailApi.ProductSalesDetail[]>(
'/mes/wm/product-sales-detail/list-by-line',
{ params: { lineId } },
);
}
/** 查询销售出库明细详情 */
export function getProductSalesDetail(id: number) {
return requestClient.get<MesWmProductSalesDetailApi.ProductSalesDetail>(
`/mes/wm/product-sales-detail/get?id=${id}`,
);
}
/** 新增销售出库明细 */
export function createProductSalesDetail(
data: MesWmProductSalesDetailApi.ProductSalesDetail,
) {
return requestClient.post<number>('/mes/wm/product-sales-detail/create', data);
}
/** 修改销售出库明细 */
export function updateProductSalesDetail(
data: MesWmProductSalesDetailApi.ProductSalesDetail,
) {
return requestClient.put('/mes/wm/product-sales-detail/update', data);
}
/** 删除销售出库明细 */
export function deleteProductSalesDetail(id: number) {
return requestClient.delete(`/mes/wm/product-sales-detail/delete?id=${id}`);
}

View File

@ -0,0 +1,56 @@
import { requestClient } from '#/api/request';
export namespace MesWmReturnIssueDetailApi {
/** MES 生产退料明细 */
export interface ReturnIssueDetail {
id?: number; // 明细编号
issueId?: number; // 退料单编号
lineId?: number; // 退料单行编号
materialStockId?: number; // 库存记录编号
itemId?: number; // 物料编号
quantity?: number; // 数量
batchId?: number; // 批次编号
batchCode?: string; // 批次号
warehouseId?: number; // 仓库编号
warehouseName?: string; // 仓库名称
locationId?: number; // 库区编号
locationName?: string; // 库区名称
areaId?: number; // 库位编号
areaName?: string; // 库位名称
remark?: string; // 备注
}
}
/** 查询生产退料明细列表(按行编号) */
export function getReturnIssueDetailListByLineId(lineId: number) {
return requestClient.get<MesWmReturnIssueDetailApi.ReturnIssueDetail[]>(
'/mes/wm/return-issue-detail/list-by-line',
{ params: { lineId } },
);
}
/** 查询生产退料明细详情 */
export function getReturnIssueDetail(id: number) {
return requestClient.get<MesWmReturnIssueDetailApi.ReturnIssueDetail>(
`/mes/wm/return-issue-detail/get?id=${id}`,
);
}
/** 新增生产退料明细 */
export function createReturnIssueDetail(
data: MesWmReturnIssueDetailApi.ReturnIssueDetail,
) {
return requestClient.post<number>('/mes/wm/return-issue-detail/create', data);
}
/** 修改生产退料明细 */
export function updateReturnIssueDetail(
data: MesWmReturnIssueDetailApi.ReturnIssueDetail,
) {
return requestClient.put('/mes/wm/return-issue-detail/update', data);
}
/** 删除生产退料明细 */
export function deleteReturnIssueDetail(id: number) {
return requestClient.delete(`/mes/wm/return-issue-detail/delete?id=${id}`);
}

View File

@ -0,0 +1,78 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmReturnIssueApi {
/** MES 生产退料单 */
export interface ReturnIssue {
id?: number; // 退料单编号
code?: string; // 退料单编号
name?: string; // 退料单名称
workstationId?: number; // 工作站编号
workstationName?: string; // 工作站名称
workOrderId?: number; // 生产工单编号
workOrderCode?: string; // 生产工单编码
type?: number; // 退料类型
returnDate?: number; // 退料日期
status?: number; // 单据状态
remark?: string; // 备注
createTime?: Date; // 创建时间
}
}
/** 查询生产退料单分页 */
export function getReturnIssuePage(params: PageParam) {
return requestClient.get<PageResult<MesWmReturnIssueApi.ReturnIssue>>(
'/mes/wm/return-issue/page',
{ params },
);
}
/** 查询生产退料单详情 */
export function getReturnIssue(id: number) {
return requestClient.get<MesWmReturnIssueApi.ReturnIssue>(
`/mes/wm/return-issue/get?id=${id}`,
);
}
/** 新增生产退料单 */
export function createReturnIssue(data: MesWmReturnIssueApi.ReturnIssue) {
return requestClient.post<number>('/mes/wm/return-issue/create', data);
}
/** 修改生产退料单 */
export function updateReturnIssue(data: MesWmReturnIssueApi.ReturnIssue) {
return requestClient.put('/mes/wm/return-issue/update', data);
}
/** 删除生产退料单 */
export function deleteReturnIssue(id: number) {
return requestClient.delete(`/mes/wm/return-issue/delete?id=${id}`);
}
/** 提交生产退料单 */
export function submitReturnIssue(id: number) {
return requestClient.put(`/mes/wm/return-issue/submit?id=${id}`);
}
/** 入库上架 */
export function stockReturnIssue(id: number) {
return requestClient.put(`/mes/wm/return-issue/stock?id=${id}`);
}
/** 完成生产退料单 */
export function finishReturnIssue(id: number) {
return requestClient.put(`/mes/wm/return-issue/finish?id=${id}`);
}
/** 取消生产退料单 */
export function cancelReturnIssue(id: number) {
return requestClient.put(`/mes/wm/return-issue/cancel?id=${id}`);
}
/** 导出生产退料单 */
export function exportReturnIssue(params: any) {
return requestClient.download('/mes/wm/return-issue/export-excel', {
params,
});
}

View File

@ -0,0 +1,58 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmReturnIssueLineApi {
/** MES 生产退料单行 */
export interface ReturnIssueLine {
id?: number; // 行编号
issueId?: number; // 退料单编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 单位
materialStockId?: number; // 库存记录编号
quantity?: number; // 退料数量
batchId?: number; // 批次编号
batchCode?: string; // 批次号
rqcCheckFlag?: boolean; // 是否检测
qualityStatus?: number; // 质量状态
rqcId?: number; // 退货检验单编号
remark?: string; // 备注
}
}
/** 查询生产退料单行分页 */
export function getReturnIssueLinePage(params: PageParam) {
return requestClient.get<PageResult<MesWmReturnIssueLineApi.ReturnIssueLine>>(
'/mes/wm/return-issue-line/page',
{ params },
);
}
/** 查询生产退料单行详情 */
export function getReturnIssueLine(id: number) {
return requestClient.get<MesWmReturnIssueLineApi.ReturnIssueLine>(
`/mes/wm/return-issue-line/get?id=${id}`,
);
}
/** 新增生产退料单行 */
export function createReturnIssueLine(
data: MesWmReturnIssueLineApi.ReturnIssueLine,
) {
return requestClient.post<number>('/mes/wm/return-issue-line/create', data);
}
/** 修改生产退料单行 */
export function updateReturnIssueLine(
data: MesWmReturnIssueLineApi.ReturnIssueLine,
) {
return requestClient.put('/mes/wm/return-issue-line/update', data);
}
/** 删除生产退料单行 */
export function deleteReturnIssueLine(id: number) {
return requestClient.delete(`/mes/wm/return-issue-line/delete?id=${id}`);
}

View File

@ -0,0 +1,55 @@
import { requestClient } from '#/api/request';
export namespace MesWmReturnSalesDetailApi {
/** MES 销售退货明细 */
export interface ReturnSalesDetail {
id?: number; // 明细编号
returnId?: number; // 退货单编号
lineId?: number; // 退货单行编号
itemId?: number; // 物料编号
quantity?: number; // 数量
batchId?: number; // 批次编号
batchCode?: string; // 批次号
warehouseId?: number; // 仓库编号
warehouseName?: string; // 仓库名称
locationId?: number; // 库区编号
locationName?: string; // 库区名称
areaId?: number; // 库位编号
areaName?: string; // 库位名称
remark?: string; // 备注
}
}
/** 查询销售退货明细列表(按行编号) */
export function getReturnSalesDetailListByLineId(lineId: number) {
return requestClient.get<MesWmReturnSalesDetailApi.ReturnSalesDetail[]>(
'/mes/wm/return-sales-detail/list-by-line',
{ params: { lineId } },
);
}
/** 查询销售退货明细详情 */
export function getReturnSalesDetail(id: number) {
return requestClient.get<MesWmReturnSalesDetailApi.ReturnSalesDetail>(
`/mes/wm/return-sales-detail/get?id=${id}`,
);
}
/** 新增销售退货明细 */
export function createReturnSalesDetail(
data: MesWmReturnSalesDetailApi.ReturnSalesDetail,
) {
return requestClient.post<number>('/mes/wm/return-sales-detail/create', data);
}
/** 修改销售退货明细 */
export function updateReturnSalesDetail(
data: MesWmReturnSalesDetailApi.ReturnSalesDetail,
) {
return requestClient.put('/mes/wm/return-sales-detail/update', data);
}
/** 删除销售退货明细 */
export function deleteReturnSalesDetail(id: number) {
return requestClient.delete(`/mes/wm/return-sales-detail/delete?id=${id}`);
}

View File

@ -0,0 +1,78 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmReturnSalesApi {
/** MES 销售退货单 */
export interface ReturnSales {
id?: number; // 退货单编号
code?: string; // 退货单编号
name?: string; // 退货单名称
salesOrderCode?: string; // 销售订单号
clientId?: number; // 客户编号
clientCode?: string; // 客户编码
clientName?: string; // 客户名称
returnDate?: number; // 退货日期
returnReason?: string; // 退货原因
status?: number; // 单据状态
remark?: string; // 备注
createTime?: Date; // 创建时间
}
}
/** 查询销售退货单分页 */
export function getReturnSalesPage(params: PageParam) {
return requestClient.get<PageResult<MesWmReturnSalesApi.ReturnSales>>(
'/mes/wm/return-sales/page',
{ params },
);
}
/** 查询销售退货单详情 */
export function getReturnSales(id: number) {
return requestClient.get<MesWmReturnSalesApi.ReturnSales>(
`/mes/wm/return-sales/get?id=${id}`,
);
}
/** 新增销售退货单 */
export function createReturnSales(data: MesWmReturnSalesApi.ReturnSales) {
return requestClient.post<number>('/mes/wm/return-sales/create', data);
}
/** 修改销售退货单 */
export function updateReturnSales(data: MesWmReturnSalesApi.ReturnSales) {
return requestClient.put('/mes/wm/return-sales/update', data);
}
/** 删除销售退货单 */
export function deleteReturnSales(id: number) {
return requestClient.delete(`/mes/wm/return-sales/delete?id=${id}`);
}
/** 提交销售退货单 */
export function submitReturnSales(id: number) {
return requestClient.put(`/mes/wm/return-sales/submit?id=${id}`);
}
/** 执行退货 */
export function finishReturnSales(id: number) {
return requestClient.put(`/mes/wm/return-sales/finish?id=${id}`);
}
/** 执行上架 */
export function stockReturnSales(id: number) {
return requestClient.put(`/mes/wm/return-sales/stock?id=${id}`);
}
/** 取消销售退货单 */
export function cancelReturnSales(id: number) {
return requestClient.put(`/mes/wm/return-sales/cancel?id=${id}`);
}
/** 导出销售退货单 */
export function exportReturnSales(params: any) {
return requestClient.download('/mes/wm/return-sales/export-excel', {
params,
});
}

View File

@ -0,0 +1,57 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmReturnSalesLineApi {
/** MES 销售退货单行 */
export interface ReturnSalesLine {
id?: number; // 行编号
returnId?: number; // 退货单编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 单位
quantity?: number; // 退货数量
batchId?: number; // 批次编号
batchCode?: string; // 批次号
rqcCheckFlag?: boolean; // 是否需要质检
rqcId?: number; // 退货检验单编号
qualityStatus?: number; // 质量状态
remark?: string; // 备注
}
}
/** 查询销售退货单行分页 */
export function getReturnSalesLinePage(params: PageParam) {
return requestClient.get<PageResult<MesWmReturnSalesLineApi.ReturnSalesLine>>(
'/mes/wm/return-sales-line/page',
{ params },
);
}
/** 查询销售退货单行详情 */
export function getReturnSalesLine(id: number) {
return requestClient.get<MesWmReturnSalesLineApi.ReturnSalesLine>(
`/mes/wm/return-sales-line/get?id=${id}`,
);
}
/** 新增销售退货单行 */
export function createReturnSalesLine(
data: MesWmReturnSalesLineApi.ReturnSalesLine,
) {
return requestClient.post<number>('/mes/wm/return-sales-line/create', data);
}
/** 修改销售退货单行 */
export function updateReturnSalesLine(
data: MesWmReturnSalesLineApi.ReturnSalesLine,
) {
return requestClient.put('/mes/wm/return-sales-line/update', data);
}
/** 删除销售退货单行 */
export function deleteReturnSalesLine(id: number) {
return requestClient.delete(`/mes/wm/return-sales-line/delete?id=${id}`);
}

View File

@ -0,0 +1,56 @@
import { requestClient } from '#/api/request';
export namespace MesWmReturnVendorDetailApi {
/** MES 供应商退货明细 */
export interface ReturnVendorDetail {
id?: number; // 明细编号
returnId?: number; // 退货单编号
lineId?: number; // 退货单行编号
materialStockId?: number; // 库存记录编号
itemId?: number; // 物料编号
quantity?: number; // 数量
batchId?: number; // 批次编号
batchCode?: string; // 批次号
warehouseId?: number; // 仓库编号
warehouseName?: string; // 仓库名称
locationId?: number; // 库区编号
locationName?: string; // 库区名称
areaId?: number; // 库位编号
areaName?: string; // 库位名称
remark?: string; // 备注
}
}
/** 查询供应商退货明细列表(按行编号) */
export function getReturnVendorDetailListByLineId(lineId: number) {
return requestClient.get<MesWmReturnVendorDetailApi.ReturnVendorDetail[]>(
'/mes/wm/return-vendor-detail/list-by-line',
{ params: { lineId } },
);
}
/** 查询供应商退货明细详情 */
export function getReturnVendorDetail(id: number) {
return requestClient.get<MesWmReturnVendorDetailApi.ReturnVendorDetail>(
`/mes/wm/return-vendor-detail/get?id=${id}`,
);
}
/** 新增供应商退货明细 */
export function createReturnVendorDetail(
data: MesWmReturnVendorDetailApi.ReturnVendorDetail,
) {
return requestClient.post<number>('/mes/wm/return-vendor-detail/create', data);
}
/** 修改供应商退货明细 */
export function updateReturnVendorDetail(
data: MesWmReturnVendorDetailApi.ReturnVendorDetail,
) {
return requestClient.put('/mes/wm/return-vendor-detail/update', data);
}
/** 删除供应商退货明细 */
export function deleteReturnVendorDetail(id: number) {
return requestClient.delete(`/mes/wm/return-vendor-detail/delete?id=${id}`);
}

View File

@ -0,0 +1,88 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmReturnVendorApi {
/** MES 供应商退货单 */
export interface ReturnVendor {
id?: number; // 退货单编号
code?: string; // 退货单编号
name?: string; // 退货单名称
purchaseOrderCode?: string; // 采购订单号
vendorId?: number; // 供应商编号
vendorCode?: string; // 供应商编码
vendorName?: string; // 供应商名称
vendorNickname?: string; // 供应商简称
returnDate?: number; // 退货日期
returnReason?: string; // 退货原因
transportCode?: string; // 运单号
transportTelephone?: string; // 联系电话
status?: number; // 单据状态
remark?: string; // 备注
createTime?: Date; // 创建时间
}
}
/** 查询供应商退货单分页 */
export function getReturnVendorPage(params: PageParam) {
return requestClient.get<PageResult<MesWmReturnVendorApi.ReturnVendor>>(
'/mes/wm/return-vendor/page',
{ params },
);
}
/** 查询供应商退货单详情 */
export function getReturnVendor(id: number) {
return requestClient.get<MesWmReturnVendorApi.ReturnVendor>(
`/mes/wm/return-vendor/get?id=${id}`,
);
}
/** 新增供应商退货单 */
export function createReturnVendor(data: MesWmReturnVendorApi.ReturnVendor) {
return requestClient.post<number>('/mes/wm/return-vendor/create', data);
}
/** 修改供应商退货单 */
export function updateReturnVendor(data: MesWmReturnVendorApi.ReturnVendor) {
return requestClient.put('/mes/wm/return-vendor/update', data);
}
/** 删除供应商退货单 */
export function deleteReturnVendor(id: number) {
return requestClient.delete(`/mes/wm/return-vendor/delete?id=${id}`);
}
/** 提交供应商退货单 */
export function submitReturnVendor(id: number) {
return requestClient.put(`/mes/wm/return-vendor/submit?id=${id}`);
}
/** 执行拣货 */
export function stockReturnVendor(id: number) {
return requestClient.put(`/mes/wm/return-vendor/stock?id=${id}`);
}
/** 完成供应商退货单 */
export function finishReturnVendor(id: number) {
return requestClient.put(`/mes/wm/return-vendor/finish?id=${id}`);
}
/** 取消供应商退货单 */
export function cancelReturnVendor(id: number) {
return requestClient.put(`/mes/wm/return-vendor/cancel?id=${id}`);
}
/** 校验供应商退货单拣货数量是否与退货数量一致 */
export function checkReturnVendorQuantity(id: number) {
return requestClient.get<boolean>(
`/mes/wm/return-vendor/check-quantity?id=${id}`,
);
}
/** 导出供应商退货单 */
export function exportReturnVendor(params: any) {
return requestClient.download('/mes/wm/return-vendor/export-excel', {
params,
});
}

View File

@ -0,0 +1,54 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmReturnVendorLineApi {
/** MES 供应商退货单行 */
export interface ReturnVendorLine {
id?: number; // 行编号
returnId?: number; // 退货单编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 单位
quantity?: number; // 退货数量
batchId?: number; // 批次编号
batchCode?: string; // 批次号
remark?: string; // 备注
}
}
/** 查询供应商退货单行分页 */
export function getReturnVendorLinePage(params: PageParam) {
return requestClient.get<PageResult<MesWmReturnVendorLineApi.ReturnVendorLine>>(
'/mes/wm/return-vendor-line/page',
{ params },
);
}
/** 查询供应商退货单行详情 */
export function getReturnVendorLine(id: number) {
return requestClient.get<MesWmReturnVendorLineApi.ReturnVendorLine>(
`/mes/wm/return-vendor-line/get?id=${id}`,
);
}
/** 新增供应商退货单行 */
export function createReturnVendorLine(
data: MesWmReturnVendorLineApi.ReturnVendorLine,
) {
return requestClient.post<number>('/mes/wm/return-vendor-line/create', data);
}
/** 修改供应商退货单行 */
export function updateReturnVendorLine(
data: MesWmReturnVendorLineApi.ReturnVendorLine,
) {
return requestClient.put('/mes/wm/return-vendor-line/update', data);
}
/** 删除供应商退货单行 */
export function deleteReturnVendorLine(id: number) {
return requestClient.delete(`/mes/wm/return-vendor-line/delete?id=${id}`);
}

View File

@ -0,0 +1,65 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmSalesNoticeApi {
/** MES 发货通知单 */
export interface SalesNotice {
id?: number; // 通知单编号
code?: string; // 通知单编号
name?: string; // 通知单名称
salesOrderCode?: string; // 销售订单编号
clientId?: number; // 客户编号
clientCode?: string; // 客户编码
clientName?: string; // 客户名称
salesDate?: number; // 发货日期
recipientName?: string; // 收货人
recipientTelephone?: string; // 联系方式
recipientAddress?: string; // 收货地址
status?: number; // 单据状态
remark?: string; // 备注
createTime?: Date; // 创建时间
}
}
/** 查询发货通知单分页 */
export function getSalesNoticePage(params: PageParam) {
return requestClient.get<PageResult<MesWmSalesNoticeApi.SalesNotice>>(
'/mes/wm/sales-notice/page',
{ params },
);
}
/** 查询发货通知单详情 */
export function getSalesNotice(id: number) {
return requestClient.get<MesWmSalesNoticeApi.SalesNotice>(
`/mes/wm/sales-notice/get?id=${id}`,
);
}
/** 新增发货通知单 */
export function createSalesNotice(data: MesWmSalesNoticeApi.SalesNotice) {
return requestClient.post<number>('/mes/wm/sales-notice/create', data);
}
/** 修改发货通知单 */
export function updateSalesNotice(data: MesWmSalesNoticeApi.SalesNotice) {
return requestClient.put('/mes/wm/sales-notice/update', data);
}
/** 删除发货通知单 */
export function deleteSalesNotice(id: number) {
return requestClient.delete(`/mes/wm/sales-notice/delete?id=${id}`);
}
/** 提交发货通知单 */
export function submitSalesNotice(id: number) {
return requestClient.put(`/mes/wm/sales-notice/submit?id=${id}`);
}
/** 导出发货通知单 */
export function exportSalesNotice(params: any) {
return requestClient.download('/mes/wm/sales-notice/export-excel', {
params,
});
}

View File

@ -0,0 +1,55 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmSalesNoticeLineApi {
/** MES 发货通知单行 */
export interface SalesNoticeLine {
id?: number; // 行编号
noticeId?: number; // 发货通知单编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 单位
batchId?: number; // 批次编号
batchCode?: string; // 批次号
quantity?: number; // 发货数量
oqcCheckFlag?: boolean; // 是否检验
remark?: string; // 备注
}
}
/** 查询发货通知单行分页 */
export function getSalesNoticeLinePage(params: PageParam) {
return requestClient.get<PageResult<MesWmSalesNoticeLineApi.SalesNoticeLine>>(
'/mes/wm/sales-notice-line/page',
{ params },
);
}
/** 查询发货通知单行详情 */
export function getSalesNoticeLine(id: number) {
return requestClient.get<MesWmSalesNoticeLineApi.SalesNoticeLine>(
`/mes/wm/sales-notice-line/get?id=${id}`,
);
}
/** 新增发货通知单行 */
export function createSalesNoticeLine(
data: MesWmSalesNoticeLineApi.SalesNoticeLine,
) {
return requestClient.post<number>('/mes/wm/sales-notice-line/create', data);
}
/** 修改发货通知单行 */
export function updateSalesNoticeLine(
data: MesWmSalesNoticeLineApi.SalesNoticeLine,
) {
return requestClient.put('/mes/wm/sales-notice-line/update', data);
}
/** 删除发货通知单行 */
export function deleteSalesNoticeLine(id: number) {
return requestClient.delete(`/mes/wm/sales-notice-line/delete?id=${id}`);
}

View File

@ -0,0 +1,68 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmStockTakingPlanApi {
/** 盘点方案 */
export interface StockTakingPlan {
id?: number; // 方案编号
code?: string; // 方案编码
name?: string; // 方案名称
type?: number; // 盘点类型
startTime?: number; // 开始时间
endTime?: number; // 结束时间
blindFlag?: boolean; // 是否盲盘
frozen?: boolean; // 是否冻结库存
status?: number; // 状态
remark?: string; // 备注
createTime?: string; // 创建时间
}
}
/** 查询盘点方案分页 */
export function getStockTakingPlanPage(params: PageParam) {
return requestClient.get<PageResult<MesWmStockTakingPlanApi.StockTakingPlan>>(
'/mes/wm/stocktaking-plan/page',
{ params },
);
}
/** 查询盘点方案详情 */
export function getStockTakingPlan(id: number) {
return requestClient.get<MesWmStockTakingPlanApi.StockTakingPlan>(
`/mes/wm/stocktaking-plan/get?id=${id}`,
);
}
/** 新增盘点方案 */
export function createStockTakingPlan(
data: MesWmStockTakingPlanApi.StockTakingPlan,
) {
return requestClient.post<number>('/mes/wm/stocktaking-plan/create', data);
}
/** 修改盘点方案 */
export function updateStockTakingPlan(
data: MesWmStockTakingPlanApi.StockTakingPlan,
) {
return requestClient.put('/mes/wm/stocktaking-plan/update', data);
}
/** 修改盘点方案状态 */
export function updateStockTakingPlanStatus(id: number, status: number) {
return requestClient.put('/mes/wm/stocktaking-plan/update-status', null, {
params: { id, status },
});
}
/** 删除盘点方案 */
export function deleteStockTakingPlan(id: number) {
return requestClient.delete(`/mes/wm/stocktaking-plan/delete?id=${id}`);
}
/** 导出盘点方案 */
export function exportStockTakingPlan(params: any) {
return requestClient.download('/mes/wm/stocktaking-plan/export-excel', {
params,
});
}

View File

@ -0,0 +1,49 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmStockTakingPlanParamApi {
/** 盘点方案条件 */
export interface StockTakingPlanParam {
id?: number; // 条件编号
planId?: number; // 方案编号
type?: number; // 条件类型
valueId?: number; // 条件值编号
valueCode?: string; // 条件值编码
valueName?: string; // 条件值名称
remark?: string; // 备注
}
}
/** 查询盘点方案条件分页 */
export function getStockTakingPlanParamPage(params: PageParam) {
return requestClient.get<
PageResult<MesWmStockTakingPlanParamApi.StockTakingPlanParam>
>('/mes/wm/stocktaking-plan-param/page', { params });
}
/** 查询盘点方案条件详情 */
export function getStockTakingPlanParam(id: number) {
return requestClient.get<MesWmStockTakingPlanParamApi.StockTakingPlanParam>(
`/mes/wm/stocktaking-plan-param/get?id=${id}`,
);
}
/** 新增盘点方案条件 */
export function createStockTakingPlanParam(
data: MesWmStockTakingPlanParamApi.StockTakingPlanParam,
) {
return requestClient.post('/mes/wm/stocktaking-plan-param/create', data);
}
/** 修改盘点方案条件 */
export function updateStockTakingPlanParam(
data: MesWmStockTakingPlanParamApi.StockTakingPlanParam,
) {
return requestClient.put('/mes/wm/stocktaking-plan-param/update', data);
}
/** 删除盘点方案条件 */
export function deleteStockTakingPlanParam(id: number) {
return requestClient.delete(`/mes/wm/stocktaking-plan-param/delete?id=${id}`);
}

View File

@ -0,0 +1,86 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmStockTakingTaskApi {
/** 盘点任务 */
export interface StockTakingTask {
id?: number; // 任务编号
code?: string; // 任务编码
name?: string; // 任务名称
takingDate?: string; // 盘点日期
type?: number; // 盘点类型
userId?: number; // 盘点人用户编号
userNickname?: string; // 盘点人名称
planId?: number; // 盘点方案编号
planCode?: string; // 盘点方案编码
planName?: string; // 盘点方案名称
blindFlag?: boolean; // 是否盲盘
frozen?: boolean; // 是否冻结库存
startTime?: number; // 开始时间
endTime?: number; // 结束时间
status?: number; // 单据状态
remark?: string; // 备注
createTime?: string; // 创建时间
}
}
/** 查询盘点任务分页 */
export function getStockTakingPage(params: PageParam) {
return requestClient.get<PageResult<MesWmStockTakingTaskApi.StockTakingTask>>(
'/mes/wm/stocktaking-task/page',
{ params },
);
}
/** 查询盘点任务详情 */
export function getStockTaking(id: number) {
return requestClient.get<MesWmStockTakingTaskApi.StockTakingTask>(
`/mes/wm/stocktaking-task/get?id=${id}`,
);
}
/** 新增盘点任务 */
export function createStockTaking(
data: MesWmStockTakingTaskApi.StockTakingTask,
) {
return requestClient.post<number>('/mes/wm/stocktaking-task/create', data);
}
/** 修改盘点任务 */
export function updateStockTaking(
data: MesWmStockTakingTaskApi.StockTakingTask,
) {
return requestClient.put('/mes/wm/stocktaking-task/update', data);
}
/** 删除盘点任务 */
export function deleteStockTaking(id: number) {
return requestClient.delete(`/mes/wm/stocktaking-task/delete?id=${id}`);
}
/** 提交盘点任务 */
export function submitStockTaking(id: number) {
return requestClient.put('/mes/wm/stocktaking-task/submit', null, {
params: { id },
});
}
/** 取消盘点任务 */
export function cancelStockTaking(id: number) {
return requestClient.put('/mes/wm/stocktaking-task/cancel', null, {
params: { id },
});
}
/** 执行盘点任务 */
export function finishStockTaking(id: number) {
return requestClient.put('/mes/wm/stocktaking-task/finish', { id });
}
/** 导出盘点任务 */
export function exportStockTaking(params: any) {
return requestClient.download('/mes/wm/stocktaking-task/export-excel', {
params,
});
}

View File

@ -0,0 +1,72 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmStockTakingTaskLineApi {
/** 盘点任务行 */
export interface StockTakingTaskLine {
id?: number; // 盘点行编号
taskId?: number; // 任务编号
materialStockId?: number; // 库存编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 计量单位名称
batchId?: number; // 批次编号
batchCode?: string; // 批次号
quantity?: number; // 在库数量
takingQuantity?: number; // 盘点数量
differenceQuantity?: number; // 差异数量
warehouseId?: number; // 仓库编号
warehouseName?: string; // 仓库名称
locationId?: number; // 库区编号
locationName?: string; // 库区名称
areaId?: number; // 库位编号
areaName?: string; // 库位名称
status?: number; // 状态
remark?: string; // 备注
}
}
/** 查询盘点任务行分页 */
export function getStockTakingTaskLinePage(params: PageParam) {
return requestClient.get<
PageResult<MesWmStockTakingTaskLineApi.StockTakingTaskLine>
>('/mes/wm/stocktaking-task-line/page', { params });
}
/** 查询盘点任务行精简列表 */
export function getStockTakingTaskLineSimpleList(taskId: number) {
return requestClient.get<MesWmStockTakingTaskLineApi.StockTakingTaskLine[]>(
'/mes/wm/stocktaking-task-line/simple-list',
{ params: { taskId } },
);
}
/** 查询盘点任务行详情 */
export function getStockTakingTaskLine(id: number) {
return requestClient.get<MesWmStockTakingTaskLineApi.StockTakingTaskLine>(
'/mes/wm/stocktaking-task-line/get',
{ params: { id } },
);
}
/** 新增盘点任务行 */
export function createStockTakingTaskLine(
data: MesWmStockTakingTaskLineApi.StockTakingTaskLine,
) {
return requestClient.post('/mes/wm/stocktaking-task-line/create', data);
}
/** 修改盘点任务行 */
export function updateStockTakingTaskLine(
data: MesWmStockTakingTaskLineApi.StockTakingTaskLine,
) {
return requestClient.put('/mes/wm/stocktaking-task-line/update', data);
}
/** 删除盘点任务行 */
export function deleteStockTakingTaskLine(id: number) {
return requestClient.delete(`/mes/wm/stocktaking-task-line/delete?id=${id}`);
}

View File

@ -0,0 +1,64 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesWmStockTakingResultApi {
/** 盘点结果 */
export interface StockTakingResult {
id?: number; // 结果编号
taskId?: number; // 任务编号
lineId?: number; // 盘点行编号
materialStockId?: number; // 库存编号
itemId?: number; // 物料编号
itemCode?: string; // 物料编码
itemName?: string; // 物料名称
specification?: string; // 规格型号
unitMeasureName?: string; // 计量单位名称
batchId?: number; // 批次编号
batchCode?: string; // 批次号
warehouseId?: number; // 仓库编号
warehouseName?: string; // 仓库名称
locationId?: number; // 库区编号
locationName?: string; // 库区名称
areaId?: number; // 库位编号
areaName?: string; // 库位名称
quantity?: number; // 在库数量
takingQuantity?: number; // 盘点数量
remark?: string; // 备注
createTime?: string; // 创建时间
}
}
/** 查询盘点结果分页 */
export function getStockTakingResultPage(params: PageParam) {
return requestClient.get<
PageResult<MesWmStockTakingResultApi.StockTakingResult>
>('/mes/wm/stocktaking-task-result/page', { params });
}
/** 查询盘点结果详情 */
export function getStockTakingResult(id: number) {
return requestClient.get<MesWmStockTakingResultApi.StockTakingResult>(
'/mes/wm/stocktaking-task-result/get',
{ params: { id } },
);
}
/** 新增盘点结果 */
export function createStockTakingResult(
data: MesWmStockTakingResultApi.StockTakingResult,
) {
return requestClient.post('/mes/wm/stocktaking-task-result/create', data);
}
/** 修改盘点结果 */
export function updateStockTakingResult(
data: MesWmStockTakingResultApi.StockTakingResult,
) {
return requestClient.put('/mes/wm/stocktaking-task-result/update', data);
}
/** 删除盘点结果 */
export function deleteStockTakingResult(id: number) {
return requestClient.delete(`/mes/wm/stocktaking-task-result/delete?id=${id}`);
}

View File

@ -0,0 +1,2 @@
export { default as StockTakingPlanSelectDialog } from './stock-taking-plan-select-dialog.vue';
export { default as StockTakingPlanSelect } from './stock-taking-plan-select.vue';

View File

@ -0,0 +1,213 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import { nextTick, ref } from 'vue';
import { CommonStatusEnum } from '@vben/constants';
import { ElButton, ElDialog, ElMessage } from 'element-plus';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getStockTakingPlanPage } from '#/api/mes/wm/stocktaking/plan';
import { useSelectGridColumns, useSelectGridFormSchema } from '../data';
const emit = defineEmits<{
selected: [rows: MesWmStockTakingPlanApi.StockTakingPlan[]];
}>();
const open = ref(false); //
const multiple = ref(true); //
const selectedRows = ref<MesWmStockTakingPlanApi.StockTakingPlan[]>([]); //
const preSelectedIds = ref<number[]>([]); //
/** 获取多选记录,包含 VXE reserve 跨页记录 */
function getMultipleSelectedRows() {
const selectedMap = new Map<
number,
MesWmStockTakingPlanApi.StockTakingPlan
>();
const records = [
...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
...(gridApi.grid.getCheckboxRecords?.() ?? []),
] as MesWmStockTakingPlanApi.StockTakingPlan[];
records.forEach((row) => {
if (row.id != null) {
selectedMap.set(row.id, row);
}
});
return [...selectedMap.values()];
}
/** 处理勾选变化 */
function handleCheckboxSelectChange() {
selectedRows.value = getMultipleSelectedRows();
}
/** 处理单选变化 */
function handleRadioChange(row: MesWmStockTakingPlanApi.StockTakingPlan) {
selectedRows.value = [row];
}
/** 多选模式下切换行勾选 */
async function toggleMultipleRow(
row: MesWmStockTakingPlanApi.StockTakingPlan,
) {
const selected = gridApi.grid.isCheckedByCheckboxRow(row);
await gridApi.grid.setCheckboxRow(row, !selected);
selectedRows.value = getMultipleSelectedRows();
}
/** 处理行双击:单选直接确认;多选切换勾选 */
async function handleCellDblclick({
row,
}: {
row: MesWmStockTakingPlanApi.StockTakingPlan;
}) {
if (multiple.value) {
await toggleMultipleRow(row);
return;
}
selectedRows.value = [row];
await gridApi.grid.setRadioRow(row);
handleConfirm();
}
/** 回显预选盘点方案 */
async function applyPreSelection() {
if (preSelectedIds.value.length === 0) {
return;
}
const rows = gridApi.grid.getData() as MesWmStockTakingPlanApi.StockTakingPlan[];
for (const row of rows) {
if (row.id == null || !preSelectedIds.value.includes(row.id)) {
continue;
}
if (multiple.value) {
await gridApi.grid.setCheckboxRow(row, true);
} else {
await gridApi.grid.setRadioRow(row);
selectedRows.value = [row];
return;
}
}
if (multiple.value) {
selectedRows.value = getMultipleSelectedRows();
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useSelectGridFormSchema(),
},
gridOptions: {
columns: useSelectGridColumns(true),
height: 480,
keepSource: true,
checkboxConfig: {
highlight: true,
range: true,
reserve: true,
},
radioConfig: {
highlight: true,
trigger: 'row',
},
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getStockTakingPlanPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
//
status: CommonStatusEnum.ENABLE,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<MesWmStockTakingPlanApi.StockTakingPlan>,
gridEvents: {
cellDblclick: handleCellDblclick,
checkboxAll: handleCheckboxSelectChange,
checkboxChange: handleCheckboxSelectChange,
radioChange: ({
row,
}: {
row: MesWmStockTakingPlanApi.StockTakingPlan;
}) => {
handleRadioChange(row);
},
},
});
/** 重置查询和选择状态 */
async function resetQueryState() {
selectedRows.value = [];
await gridApi.grid.clearCheckboxRow();
await gridApi.grid.clearCheckboxReserve();
await gridApi.grid.clearRadioRow();
await gridApi.formApi.resetForm();
}
/** 打开盘点方案选择弹窗 */
async function openModal(
selectedIds?: number[],
options?: { multiple?: boolean },
) {
open.value = true;
multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || [];
await nextTick();
gridApi.setGridOptions({
columns: useSelectGridColumns(multiple.value),
});
await resetQueryState();
await gridApi.query();
await nextTick();
await applyPreSelection();
}
/** 关闭弹窗 */
function closeModal() {
open.value = false;
}
/** 确认选择 */
function handleConfirm() {
const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
if (rows.length === 0) {
ElMessage.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
return;
}
emit('selected', multiple.value ? rows : [rows[0]!]);
open.value = false;
}
defineExpose({ open: openModal });
</script>
<template>
<ElDialog
v-model="open"
destroy-on-close
title="盘点方案选择"
width="70%"
@close="closeModal"
>
<Grid table-title="" />
<template #footer>
<ElButton @click="closeModal"></ElButton>
<ElButton type="primary" @click="handleConfirm"></ElButton>
</template>
</ElDialog>
</template>

View File

@ -0,0 +1,135 @@
<script lang="ts" setup>
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import { computed, ref, useAttrs, watch } from 'vue';
import { CircleX, Search } from '@vben/icons';
import { ElInput, ElTooltip } from 'element-plus';
import { getStockTakingPlan } from '#/api/mes/wm/stocktaking/plan';
import StockTakingPlanSelectDialog from './stock-taking-plan-select-dialog.vue';
defineOptions({ name: 'StockTakingPlanSelect', inheritAttrs: false });
const props = withDefaults(
defineProps<{
clearable?: boolean;
disabled?: boolean;
modelValue?: number;
placeholder?: string;
}>(),
{
clearable: true,
disabled: false,
modelValue: undefined,
placeholder: '请选择盘点方案',
},
);
const emit = defineEmits<{
change: [item: MesWmStockTakingPlanApi.StockTakingPlan | undefined];
'update:modelValue': [value: number | undefined];
}>();
const attrs = useAttrs();
const dialogRef = ref<InstanceType<typeof StockTakingPlanSelectDialog>>();
const hovering = ref(false);
const selectedItem = ref<MesWmStockTakingPlanApi.StockTakingPlan>();
const displayLabel = computed(() => selectedItem.value?.name ?? '');
const showClear = computed(
() =>
props.clearable &&
!props.disabled &&
hovering.value &&
props.modelValue != null,
);
/** 根据编号单条查询盘点方案信息(用于编辑回显) */
async function resolveItemById(id: number | undefined) {
if (id == null) {
selectedItem.value = undefined;
return;
}
if (selectedItem.value?.id === id) {
return;
}
try {
selectedItem.value = await getStockTakingPlan(id);
} catch (error) {
console.error('[StockTakingPlanSelect] resolveItemById failed:', error);
}
}
watch(() => props.modelValue, resolveItemById, { immediate: true });
/** 清空已选盘点方案 */
function clearSelected() {
selectedItem.value = undefined;
emit('update:modelValue', undefined);
emit('change', undefined);
}
/** 打开盘点方案选择弹窗 */
function handleClick(event: MouseEvent) {
if (props.disabled) {
return;
}
const target = event.target as HTMLElement;
if (showClear.value && target.closest('.el-input__suffix')) {
event.stopPropagation();
clearSelected();
return;
}
const selectedIds = props.modelValue == null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds, { multiple: false });
}
/** 弹窗选中回调 */
function handleSelected(rows: MesWmStockTakingPlanApi.StockTakingPlan[]) {
const item = rows[0];
if (!item) {
return;
}
selectedItem.value = item;
emit('update:modelValue', item.id);
emit('change', item);
}
</script>
<template>
<div
v-bind="attrs"
class="w-full"
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
@click="handleClick"
@mouseenter="hovering = true"
@mouseleave="hovering = false"
>
<ElTooltip :disabled="!selectedItem" placement="top" :show-after="500">
<template #content>
<div v-if="selectedItem" class="leading-6">
<div>编码{{ selectedItem.code || '-' }}</div>
<div>名称{{ selectedItem.name || '-' }}</div>
<div>是否盲盘{{ selectedItem.blindFlag ? '是' : '否' }}</div>
<div>是否冻结库存{{ selectedItem.frozen ? '是' : '否' }}</div>
</div>
</template>
<ElInput
:disabled="disabled"
:model-value="displayLabel"
:placeholder="placeholder"
readonly
>
<template #suffix>
<CircleX v-if="showClear" class="size-4" />
<Search v-else class="size-4" />
</template>
</ElInput>
</ElTooltip>
</div>
<StockTakingPlanSelectDialog ref="dialogRef" @selected="handleSelected" />
</template>

View File

@ -0,0 +1,388 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import type { MesWmStockTakingPlanParamApi } from '#/api/mes/wm/stocktaking/plan/param';
import { h } from 'vue';
import { CommonStatusEnum, DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { ElButton } from 'element-plus';
import { z } from '#/adapter/form';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import {
MesAutoCodeRuleCode,
MesWmStockTakingTypeEnum,
} from '#/views/mes/utils/constants';
/** 表单类型 */
export type FormType = 'create' | 'detail' | 'update';
/** 新增/修改的表单 */
export function useFormSchema(
formType: FormType,
formApi?: VbenFormApi,
): VbenFormSchema[] {
return [
{
fieldName: 'id',
component: 'Input',
dependencies: {
triggerFields: [''],
show: () => false,
},
},
{
fieldName: 'code',
label: '方案编码',
component: 'Input',
componentProps: {
placeholder: '请输入方案编码',
},
rules: 'required',
suffix:
formType === 'detail'
? undefined
: () =>
h(
ElButton,
{
onClick: async () => {
const code = await generateAutoCode(
MesAutoCodeRuleCode.WM_STOCK_TAKING_PLAN_CODE,
);
await formApi?.setFieldValue('code', code);
},
},
{ default: () => '生成' },
),
},
{
fieldName: 'name',
label: '方案名称',
component: 'Input',
componentProps: {
placeholder: '请输入方案名称',
},
rules: 'required',
},
{
fieldName: 'type',
label: '盘点类型',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, 'number'),
placeholder: '请选择盘点类型',
},
rules: 'selectRequired',
},
{
fieldName: 'startTime',
label: '开始时间',
component: 'DatePicker',
componentProps: {
placeholder: '请选择开始时间',
type: 'datetime',
valueFormat: 'x',
},
dependencies: {
triggerFields: ['type'],
show: (values) => values.type === MesWmStockTakingTypeEnum.DYNAMIC,
},
},
{
fieldName: 'endTime',
label: '结束时间',
component: 'DatePicker',
componentProps: {
placeholder: '请选择结束时间',
type: 'datetime',
valueFormat: 'x',
},
dependencies: {
triggerFields: ['type'],
show: (values) => values.type === MesWmStockTakingTypeEnum.DYNAMIC,
},
},
{
fieldName: 'blindFlag',
label: '是否盲盘',
component: 'Switch',
componentProps: {
activeText: '是',
inactiveText: '否',
inlinePrompt: true,
},
rules: z.boolean().default(false),
},
{
fieldName: 'frozen',
label: '冻结库存',
component: 'Switch',
componentProps: {
activeText: '是',
inactiveText: '否',
inlinePrompt: true,
},
rules: z.boolean().default(false),
},
{
fieldName: 'remark',
label: '备注',
component: 'Textarea',
formItemClass: 'col-span-3',
componentProps: {
placeholder: '请输入备注',
rows: 3,
},
},
];
}
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'code',
label: '方案编码',
component: 'Input',
componentProps: {
clearable: true,
placeholder: '请输入方案编码',
},
},
{
fieldName: 'name',
label: '方案名称',
component: 'Input',
componentProps: {
clearable: true,
placeholder: '请输入方案名称',
},
},
{
fieldName: 'type',
label: '盘点类型',
component: 'Select',
componentProps: {
clearable: true,
options: getDictOptions(DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, 'number'),
placeholder: '请选择盘点类型',
},
},
];
}
/** 列表的字段 */
export function useGridColumns(
onStatusChange?: (
newStatus: number,
row: MesWmStockTakingPlanApi.StockTakingPlan,
) => Promise<boolean | undefined>,
): VxeTableGridOptions<MesWmStockTakingPlanApi.StockTakingPlan>['columns'] {
return [
{
field: 'code',
title: '方案编码',
minWidth: 160,
slots: { default: 'code' },
},
{
field: 'name',
title: '方案名称',
minWidth: 160,
},
{
field: 'type',
title: '盘点类型',
minWidth: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_TYPE },
},
},
{
field: 'startTime',
title: '开始时间',
width: 180,
formatter: 'formatDateTime',
},
{
field: 'endTime',
title: '结束时间',
width: 180,
formatter: 'formatDateTime',
},
{
field: 'blindFlag',
title: '是否盲盘',
width: 100,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
},
},
{
field: 'frozen',
title: '是否冻结库存',
width: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
},
},
{
field: 'status',
title: '状态',
width: 120,
cellRender: {
attrs: { beforeChange: onStatusChange },
name: 'CellSwitch',
props: {
activeValue: CommonStatusEnum.ENABLE,
inactiveValue: CommonStatusEnum.DISABLE,
},
},
},
{
title: '操作',
width: 160,
fixed: 'right',
slots: { default: 'actions' },
},
];
}
/** 盘点方案条件列表的字段 */
export function useParamGridColumns(
editable = true,
): VxeTableGridOptions<MesWmStockTakingPlanParamApi.StockTakingPlanParam>['columns'] {
return [
{
field: 'type',
title: '条件类型',
minWidth: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_PLAN_PARAM_TYPE },
},
},
{
field: 'valueCode',
title: '条件值编码',
minWidth: 140,
},
{
field: 'valueName',
title: '条件值名称',
minWidth: 160,
},
...(editable
? [
{
title: '操作',
width: 120,
fixed: 'right',
slots: { default: 'actions' },
} as const,
]
: []),
];
}
/** 选择弹窗的搜索表单 */
export function useSelectGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'code',
label: '方案编码',
component: 'Input',
componentProps: {
clearable: true,
placeholder: '请输入方案编码',
},
},
{
fieldName: 'name',
label: '方案名称',
component: 'Input',
componentProps: {
clearable: true,
placeholder: '请输入方案名称',
},
},
{
fieldName: 'type',
label: '盘点类型',
component: 'Select',
componentProps: {
clearable: true,
options: getDictOptions(DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, 'number'),
placeholder: '请选择盘点类型',
},
},
];
}
/** 选择弹窗的字段 */
export function useSelectGridColumns(
multiple = true,
): VxeTableGridOptions<MesWmStockTakingPlanApi.StockTakingPlan>['columns'] {
return [
{
type: multiple ? 'checkbox' : 'radio',
width: 50,
},
{
field: 'code',
title: '方案编码',
width: 200,
},
{
field: 'name',
title: '方案名称',
minWidth: 150,
},
{
field: 'type',
title: '盘点类型',
width: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_TYPE },
},
},
{
field: 'startTime',
title: '开始时间',
width: 180,
formatter: 'formatDateTime',
},
{
field: 'endTime',
title: '结束时间',
width: 180,
formatter: 'formatDateTime',
},
{
field: 'blindFlag',
title: '是否盲盘',
width: 100,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
},
},
{
field: 'frozen',
title: '是否冻结库存',
width: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
},
},
];
}

View File

@ -0,0 +1,184 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { CommonStatusEnum } from '@vben/constants';
import { downloadFileFromBlobPart } from '@vben/utils';
import { ElButton, ElLoading, ElMessage } from 'element-plus';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteStockTakingPlan,
exportStockTakingPlan,
getStockTakingPlanPage,
updateStockTakingPlanStatus,
} from '#/api/mes/wm/stocktaking/plan';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
destroyOnClose: true,
});
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 创建盘点方案 */
function handleCreate() {
formModalApi.setData({ formType: 'create' }).open();
}
/** 查看盘点方案 */
function handleDetail(row: MesWmStockTakingPlanApi.StockTakingPlan) {
formModalApi.setData({ formType: 'detail', id: row.id }).open();
}
/** 编辑盘点方案 */
function handleEdit(row: MesWmStockTakingPlanApi.StockTakingPlan) {
formModalApi.setData({ formType: 'update', id: row.id }).open();
}
/** 更新盘点方案状态 */
async function handleStatusChange(
newStatus: number,
row: MesWmStockTakingPlanApi.StockTakingPlan,
): Promise<boolean | undefined> {
try {
await confirm(
`确认要${newStatus === CommonStatusEnum.ENABLE ? '启用' : '停用'}"${row.name}"盘点方案吗?`,
);
} catch {
return false;
}
await updateStockTakingPlanStatus(row.id!, newStatus);
ElMessage.success($t('ui.actionMessage.operationSuccess'));
return true;
}
/** 删除盘点方案 */
async function handleDelete(row: MesWmStockTakingPlanApi.StockTakingPlan) {
const loadingInstance = ElLoading.service({
text: $t('ui.actionMessage.deleting', [row.name]),
});
try {
await deleteStockTakingPlan(row.id!);
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
handleRefresh();
} finally {
loadingInstance.close();
}
}
/** 导出表格 */
async function handleExport() {
const data = await exportStockTakingPlan(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '盘点方案.xls', source: data });
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(handleStatusChange),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getStockTakingPlanPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<MesWmStockTakingPlanApi.StockTakingPlan>,
});
const StatusEnum = CommonStatusEnum;
</script>
<template>
<Page auto-content-height>
<template #doc>
<DocAlert
title="【仓库】库存盘点"
url="https://doc.iocoder.cn/mes/wm/stocktaking/"
/>
</template>
<FormModal @success="handleRefresh" />
<Grid table-title="">
<template #toolbar-tools>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['盘点方案']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['mes:wm-stock-taking-plan:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['mes:wm-stock-taking-plan:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #code="{ row }">
<ElButton link type="primary" @click="handleDetail(row)">
{{ row.code }}
</ElButton>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'primary',
link: true,
icon: ACTION_ICON.EDIT,
auth: ['mes:wm-stock-taking-plan:update'],
disabled: row.status !== StatusEnum.DISABLE,
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'danger',
link: true,
icon: ACTION_ICON.DELETE,
auth: ['mes:wm-stock-taking-plan:delete'],
disabled: row.status !== StatusEnum.DISABLE,
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@ -0,0 +1,126 @@
<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { ElDivider, ElMessage } from 'element-plus';
import { useVbenForm } from '#/adapter/form';
import {
createStockTakingPlan,
getStockTakingPlan,
updateStockTakingPlan,
} from '#/api/mes/wm/stocktaking/plan';
import { $t } from '#/locales';
import { useFormSchema } from '../data';
import ParamList from './param-list.vue';
const emit = defineEmits(['success']);
const formType = ref<FormType>('create');
const formData = ref<MesWmStockTakingPlanApi.StockTakingPlan>();
const isDetail = computed(() => formType.value === 'detail');
const showParam = computed(
() =>
(formType.value === 'detail' || formType.value === 'update') &&
!!formData.value?.id,
);
const getTitle = computed(() => {
if (formType.value === 'detail') {
return $t('ui.actionTitle.view', ['盘点方案']);
}
return formType.value === 'update'
? $t('ui.actionTitle.edit', ['盘点方案'])
: $t('ui.actionTitle.create', ['盘点方案']);
});
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-1',
labelWidth: 110,
},
layout: 'horizontal',
schema: [],
showDefaultActions: false,
wrapperClass: 'grid-cols-3',
});
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
if (isDetail.value) {
await modalApi.close();
return;
}
const { valid } = await formApi.validate();
if (!valid) {
return;
}
modalApi.lock();
//
const data =
(await formApi.getValues()) as MesWmStockTakingPlanApi.StockTakingPlan;
try {
if (formData.value?.id) {
await updateStockTakingPlan({ ...data, id: formData.value.id });
//
await modalApi.close();
emit('success');
ElMessage.success($t('ui.actionMessage.operationSuccess'));
} else {
//
const id = await createStockTakingPlan(data);
formData.value = { ...data, id };
await formApi.setFieldValue('id', id);
formType.value = 'update';
emit('success');
ElMessage.success($t('ui.actionMessage.operationSuccess'));
}
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
return;
}
//
const data = modalApi.getData<{ formType: FormType; id?: number }>();
formType.value = data.formType;
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
formApi.setDisabled(formType.value === 'detail');
modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
if (!data?.id) {
return;
}
modalApi.lock();
try {
formData.value = await getStockTakingPlan(data.id);
// values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
},
});
</script>
<template>
<Modal :title="getTitle" class="w-3/5">
<Form class="mx-4" />
<!-- 编辑或详情时展示盘点参数 -->
<template v-if="showParam">
<ElDivider>盘点参数</ElDivider>
<div class="mx-4">
<ParamList :disabled="isDetail" :plan-id="formData!.id!" />
</div>
</template>
</Modal>
</template>

View File

@ -0,0 +1,288 @@
<script lang="ts" setup>
import type { MesWmStockTakingPlanParamApi } from '#/api/mes/wm/stocktaking/plan/param';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import {
ElForm,
ElFormItem,
ElInput,
ElMessage,
ElOption,
ElSelect,
} from 'element-plus';
import {
createStockTakingPlanParam,
getStockTakingPlanParam,
updateStockTakingPlanParam,
} from '#/api/mes/wm/stocktaking/plan/param';
import { getWarehouseArea } from '#/api/mes/wm/warehouse/area';
import { getWarehouseLocation } from '#/api/mes/wm/warehouse/location';
import { $t } from '#/locales';
import MdItemSelect from '#/views/mes/md/item/components/md-item-select.vue';
import { MesWmStockTakingParamTypeEnum } from '#/views/mes/utils/constants';
import { WmBatchSelect } from '#/views/mes/wm/batch/components';
import {
WmWarehouseAreaSelect,
WmWarehouseLocationSelect,
WmWarehouseSelect,
} from '#/views/mes/wm/warehouse/components';
const emit = defineEmits(['success']);
const formData = ref<MesWmStockTakingPlanParamApi.StockTakingPlanParam>({});
const planId = ref<number>();
const locationWarehouseId = ref<number>(); //
const areaWarehouseId = ref<number>(); //
const areaLocationId = ref<number>(); //
const paramTypeOptions = getDictOptions(
DICT_TYPE.MES_WM_STOCK_TAKING_PLAN_PARAM_TYPE,
'number',
).map(({ label, value }) => ({ label, value: Number(value) }));
const qualityStatusOptions = getDictOptions(
DICT_TYPE.MES_WM_QUALITY_STATUS,
'string',
).map(({ label, value }) => ({ label, value: String(value) }));
const getTitle = computed(() =>
formData.value?.id
? $t('ui.actionTitle.edit', ['盘点条件'])
: $t('ui.actionTitle.create', ['盘点条件']),
);
/** 条件类型变化:清空已选条件值和级联临时数据 */
function handleTypeChange() {
formData.value.valueId = undefined;
formData.value.valueCode = '';
formData.value.valueName = '';
locationWarehouseId.value = undefined;
areaWarehouseId.value = undefined;
areaLocationId.value = undefined;
}
/** 通用选择器变化:回填条件值编码、名称 */
function handleSelectorChange(item?: any) {
formData.value.valueId = item?.id;
formData.value.valueCode = item?.code || '';
formData.value.valueName = item?.name || item?.nickname || '';
}
/** 批次选择器变化 */
function handleBatchChange(batch?: any) {
formData.value.valueId = batch?.id;
formData.value.valueCode = batch?.code || '';
formData.value.valueName = batch?.code || '';
}
/** 质量状态选择器变化:无实体编号,仅记录字典编码和文案 */
function handleQualityStatusChange(value: any) {
const selected = qualityStatusOptions.find((item) => item.value === value);
formData.value.valueId = undefined;
formData.value.valueCode = value;
formData.value.valueName = selected?.label || '';
}
/** 库区仓库选择回调:清空库区 */
function handleLocationWarehouseChange() {
formData.value.valueId = undefined;
formData.value.valueCode = '';
formData.value.valueName = '';
}
/** 库位仓库选择回调:清空库区和库位 */
function handleAreaWarehouseChange() {
areaLocationId.value = undefined;
formData.value.valueId = undefined;
formData.value.valueCode = '';
formData.value.valueName = '';
}
/** 库位库区选择回调:清空库位 */
function handleAreaLocationChange() {
formData.value.valueId = undefined;
formData.value.valueCode = '';
formData.value.valueName = '';
}
/** 编辑时回填级联选择器的上级数据(库区所属仓库、库位所属仓库/库区) */
async function loadCascadeData() {
if (!formData.value.type || !formData.value.valueId) {
return;
}
const valueId = formData.value.valueId;
if (formData.value.type === MesWmStockTakingParamTypeEnum.LOCATION) {
const location = await getWarehouseLocation(valueId);
locationWarehouseId.value = location?.warehouseId;
} else if (formData.value.type === MesWmStockTakingParamTypeEnum.AREA) {
const area = await getWarehouseArea(valueId);
areaWarehouseId.value = area?.warehouseId;
areaLocationId.value = area?.locationId;
}
}
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
if (!formData.value.type) {
ElMessage.warning('请选择条件类型');
return;
}
// valueCode valueId
const valid =
formData.value.type === MesWmStockTakingParamTypeEnum.QUALITY_STATUS
? !!formData.value.valueCode
: formData.value.valueId != null;
if (!valid) {
ElMessage.warning('请选择条件值');
return;
}
modalApi.lock();
//
const data = {
...formData.value,
planId: planId.value,
} as MesWmStockTakingPlanParamApi.StockTakingPlanParam;
try {
await (formData.value.id
? updateStockTakingPlanParam(data)
: createStockTakingPlanParam(data));
//
await modalApi.close();
emit('success');
ElMessage.success($t('ui.actionMessage.operationSuccess'));
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = {};
locationWarehouseId.value = undefined;
areaWarehouseId.value = undefined;
areaLocationId.value = undefined;
return;
}
//
const data = modalApi.getData<{ id?: number; planId: number }>();
planId.value = data.planId;
if (!data.id) {
return;
}
modalApi.lock();
try {
formData.value = await getStockTakingPlanParam(data.id);
await loadCascadeData();
} finally {
modalApi.unlock();
}
},
});
const ParamTypeEnum = MesWmStockTakingParamTypeEnum;
</script>
<template>
<Modal :title="getTitle" class="w-3/5">
<ElForm class="mx-4" label-width="100px">
<ElFormItem label="条件类型" required>
<ElSelect
v-model="formData.type"
class="!w-full"
placeholder="请选择条件类型"
@change="handleTypeChange"
>
<ElOption
v-for="dict in paramTypeOptions"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</ElSelect>
</ElFormItem>
<ElFormItem v-if="formData.type" label="条件值" required>
<WmWarehouseSelect
v-if="formData.type === ParamTypeEnum.WAREHOUSE"
v-model="formData.valueId"
@change="handleSelectorChange"
/>
<div
v-else-if="formData.type === ParamTypeEnum.LOCATION"
class="w-full space-y-2"
>
<WmWarehouseSelect
v-model="locationWarehouseId"
placeholder="请选择仓库"
@change="handleLocationWarehouseChange"
/>
<WmWarehouseLocationSelect
v-if="locationWarehouseId"
v-model="formData.valueId"
placeholder="请选择库区"
:warehouse-id="locationWarehouseId"
@change="handleSelectorChange"
/>
</div>
<div
v-else-if="formData.type === ParamTypeEnum.AREA"
class="w-full space-y-2"
>
<WmWarehouseSelect
v-model="areaWarehouseId"
placeholder="请选择仓库"
@change="handleAreaWarehouseChange"
/>
<WmWarehouseLocationSelect
v-if="areaWarehouseId"
v-model="areaLocationId"
placeholder="请选择库区"
:warehouse-id="areaWarehouseId"
@change="handleAreaLocationChange"
/>
<WmWarehouseAreaSelect
v-if="areaLocationId"
v-model="formData.valueId"
:location-id="areaLocationId"
placeholder="请选择库位"
@change="handleSelectorChange"
/>
</div>
<MdItemSelect
v-else-if="formData.type === ParamTypeEnum.ITEM"
v-model="formData.valueId"
@change="handleSelectorChange"
/>
<WmBatchSelect
v-else-if="formData.type === ParamTypeEnum.BATCH"
v-model="formData.valueId"
@change="handleBatchChange"
/>
<ElSelect
v-else-if="formData.type === ParamTypeEnum.QUALITY_STATUS"
v-model="formData.valueCode"
class="!w-full"
placeholder="请选择质量状态"
@change="handleQualityStatusChange"
>
<ElOption
v-for="dict in qualityStatusOptions"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="备注">
<ElInput
v-model="formData.remark"
placeholder="请输入备注"
:rows="3"
type="textarea"
/>
</ElFormItem>
</ElForm>
</Modal>
</template>

View File

@ -0,0 +1,131 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingPlanParamApi } from '#/api/mes/wm/stocktaking/plan/param';
import { useVbenModal } from '@vben/common-ui';
import { ElLoading, ElMessage } from 'element-plus';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteStockTakingPlanParam,
getStockTakingPlanParamPage,
} from '#/api/mes/wm/stocktaking/plan/param';
import { $t } from '#/locales';
import { useParamGridColumns } from '../data';
import ParamForm from './param-form.vue';
const props = defineProps<{
disabled: boolean; //
planId: number; //
}>();
const [ParamFormModal, paramFormModalApi] = useVbenModal({
connectedComponent: ParamForm,
destroyOnClose: true,
});
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 添加条件 */
function handleCreate() {
paramFormModalApi.setData({ planId: props.planId }).open();
}
/** 编辑条件 */
function handleEdit(row: MesWmStockTakingPlanParamApi.StockTakingPlanParam) {
paramFormModalApi.setData({ id: row.id, planId: props.planId }).open();
}
/** 删除条件 */
async function handleDelete(
row: MesWmStockTakingPlanParamApi.StockTakingPlanParam,
) {
const loadingInstance = ElLoading.service({
text: $t('ui.actionMessage.deleting', [row.valueName]),
});
try {
await deleteStockTakingPlanParam(row.id!);
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.valueName]));
handleRefresh();
} finally {
loadingInstance.close();
}
}
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: useParamGridColumns(!props.disabled),
height: 320,
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }) => {
if (!props.planId) {
return { list: [], total: 0 };
}
return await getStockTakingPlanParamPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
planId: props.planId,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
},
} as VxeTableGridOptions<MesWmStockTakingPlanParamApi.StockTakingPlanParam>,
});
</script>
<template>
<div>
<ParamFormModal @success="handleRefresh" />
<Grid table-title="">
<template v-if="!disabled" #toolbar-tools>
<TableAction
:actions="[
{
label: '添加条件',
type: 'primary',
icon: ACTION_ICON.ADD,
onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'primary',
link: true,
icon: ACTION_ICON.EDIT,
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'danger',
link: true,
icon: ACTION_ICON.DELETE,
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.valueName]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</div>
</template>

View File

@ -0,0 +1,584 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingPlanApi } from '#/api/mes/wm/stocktaking/plan';
import type { MesWmStockTakingTaskApi } from '#/api/mes/wm/stocktaking/task';
import type { MesWmStockTakingTaskLineApi } from '#/api/mes/wm/stocktaking/task/line';
import type { MesWmStockTakingResultApi } from '#/api/mes/wm/stocktaking/task/result';
import { h, markRaw } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { ElButton } from 'element-plus';
import { z } from '#/adapter/form';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils';
import MdItemSelect from '#/views/mes/md/item/components/md-item-select.vue';
import {
MesAutoCodeRuleCode,
MesWmStockTakingTypeEnum,
} from '#/views/mes/utils/constants';
import { StockTakingPlanSelect } from '#/views/mes/wm/stocktaking/plan/components';
import {
WmWarehouseAreaSelect,
WmWarehouseLocationSelect,
WmWarehouseSelect,
} from '#/views/mes/wm/warehouse/components';
/** 表单类型 */
export type FormType = 'create' | 'detail' | 'execute' | 'submit' | 'update';
/** 表单头部是否只读(提交、执行盘点、详情态) */
function isHeaderReadonly(formType: FormType): boolean {
return (
formType === 'detail' ||
formType === 'execute' ||
formType === 'submit'
);
}
/** 新增/修改的表单 */
export function useFormSchema(
formType: FormType,
formApi?: VbenFormApi,
): VbenFormSchema[] {
const headerReadonly = isHeaderReadonly(formType);
return [
{
fieldName: 'id',
component: 'Input',
dependencies: {
triggerFields: [''],
show: () => false,
},
},
{
fieldName: 'status',
component: 'Input',
dependencies: {
triggerFields: [''],
show: () => false,
},
},
{
fieldName: 'code',
label: '任务编码',
component: 'Input',
componentProps: {
placeholder: '请输入任务编码',
},
rules: 'required',
suffix: headerReadonly
? undefined
: () =>
h(
ElButton,
{
onClick: async () => {
const code = await generateAutoCode(
MesAutoCodeRuleCode.WM_STOCK_TAKING_CODE,
);
await formApi?.setFieldValue('code', code);
},
},
{ default: () => '生成' },
),
},
{
fieldName: 'name',
label: '任务名称',
component: 'Input',
componentProps: {
placeholder: '请输入任务名称',
},
rules: 'required',
},
{
fieldName: 'planId',
label: '盘点方案',
component: markRaw(StockTakingPlanSelect),
componentProps: {
// 选择盘点方案后,自动带出名称、类型、起止时间和盲盘/冻结配置
onChange: async (plan?: MesWmStockTakingPlanApi.StockTakingPlan) => {
if (!plan) {
return;
}
await formApi?.setValues({
blindFlag: !!plan.blindFlag,
endTime: plan.endTime,
frozen: !!plan.frozen,
name: plan.name,
startTime: plan.startTime,
type: plan.type,
});
},
},
rules: 'selectRequired',
},
{
fieldName: 'type',
label: '盘点类型',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, 'number'),
placeholder: '请选择盘点类型',
},
rules: 'selectRequired',
},
{
fieldName: 'startTime',
label: '开始时间',
component: 'DatePicker',
componentProps: {
placeholder: '请选择开始时间',
type: 'datetime',
valueFormat: 'x',
},
dependencies: {
triggerFields: ['type'],
show: (values) => values.type === MesWmStockTakingTypeEnum.DYNAMIC,
},
},
{
fieldName: 'endTime',
label: '结束时间',
component: 'DatePicker',
componentProps: {
placeholder: '请选择结束时间',
type: 'datetime',
valueFormat: 'x',
},
dependencies: {
triggerFields: ['type'],
show: (values) => values.type === MesWmStockTakingTypeEnum.DYNAMIC,
},
},
{
fieldName: 'takingDate',
label: '盘点日期',
component: 'DatePicker',
componentProps: {
placeholder: '请选择盘点日期',
type: 'date',
valueFormat: 'YYYY-MM-DD',
},
rules: 'required',
},
{
fieldName: 'blindFlag',
label: '是否盲盘',
component: 'Switch',
componentProps: {
activeText: '是',
inactiveText: '否',
inlinePrompt: true,
},
rules: z.boolean().default(false),
},
{
fieldName: 'frozen',
label: '是否冻结库存',
component: 'Switch',
componentProps: {
activeText: '是',
inactiveText: '否',
inlinePrompt: true,
},
rules: z.boolean().default(false),
},
{
fieldName: 'userId',
label: '盘点人',
component: 'ApiSelect',
componentProps: {
api: getSimpleUserList,
clearable: true,
labelField: 'nickname',
placeholder: '请选择盘点人',
valueField: 'id',
},
rules: 'selectRequired',
},
{
fieldName: 'remark',
label: '备注',
component: 'Textarea',
formItemClass: 'col-span-3',
componentProps: {
placeholder: '请输入备注',
rows: 3,
},
},
];
}
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'type',
label: '盘点类型',
component: 'Select',
componentProps: {
clearable: true,
options: getDictOptions(DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, 'number'),
placeholder: '请选择盘点类型',
},
},
{
fieldName: 'code',
label: '任务编码',
component: 'Input',
componentProps: {
clearable: true,
placeholder: '请输入任务编码',
},
},
{
fieldName: 'name',
label: '任务名称',
component: 'Input',
componentProps: {
clearable: true,
placeholder: '请输入任务名称',
},
},
{
fieldName: 'takingDate',
label: '盘点日期',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
clearable: true,
},
},
{
fieldName: 'status',
label: '单据状态',
component: 'Select',
componentProps: {
clearable: true,
options: getDictOptions(
DICT_TYPE.MES_WM_STOCK_TAKING_TASK_STATUS,
'number',
),
placeholder: '请选择单据状态',
},
},
];
}
/** 列表的字段 */
export function useGridColumns(): VxeTableGridOptions<MesWmStockTakingTaskApi.StockTakingTask>['columns'] {
return [
{
field: 'code',
title: '任务编码',
minWidth: 160,
slots: { default: 'code' },
},
{
field: 'name',
title: '任务名称',
minWidth: 160,
},
{
field: 'type',
title: '盘点类型',
minWidth: 120,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_TYPE },
},
},
{
field: 'planName',
title: '盘点方案',
minWidth: 180,
},
{
field: 'takingDate',
title: '盘点日期',
minWidth: 180,
formatter: 'formatDate',
},
{
field: 'userNickname',
title: '盘点人',
minWidth: 120,
},
{
field: 'status',
title: '单据状态',
minWidth: 110,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_TASK_STATUS },
},
},
{
title: '操作',
width: 280,
fixed: 'right',
slots: { default: 'actions' },
},
];
}
/** 盘点任务行列表的字段 */
export function useLineGridColumns(
editable = true,
): VxeTableGridOptions<MesWmStockTakingTaskLineApi.StockTakingTaskLine>['columns'] {
return [
{
field: 'itemCode',
title: '物料编码',
minWidth: 140,
},
{
field: 'itemName',
title: '物料名称',
minWidth: 160,
},
{
field: 'specification',
title: '规格型号',
minWidth: 120,
},
{
field: 'unitMeasureName',
title: '单位',
width: 90,
},
{
field: 'batchCode',
title: '批次号',
minWidth: 120,
},
{
field: 'quantity',
title: '在库数量',
width: 120,
},
{
field: 'warehouseName',
title: '仓库',
minWidth: 120,
},
{
field: 'locationName',
title: '库区',
minWidth: 120,
},
{
field: 'areaName',
title: '库位',
minWidth: 120,
},
{
field: 'status',
title: '状态',
minWidth: 100,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_WM_STOCK_TAKING_LINE_STATUS },
},
},
...(editable
? [
{
title: '操作',
width: 80,
fixed: 'right',
slots: { default: 'actions' },
} as const,
]
: []),
];
}
/** 盘点结果列表的字段 */
export function useResultGridColumns(
editable = true,
): VxeTableGridOptions<MesWmStockTakingResultApi.StockTakingResult>['columns'] {
return [
{
field: 'itemCode',
title: '产品物料编码',
minWidth: 140,
},
{
field: 'itemName',
title: '产品物料名称',
minWidth: 160,
},
{
field: 'specification',
title: '规格型号',
minWidth: 120,
},
{
field: 'unitMeasureName',
title: '单位名称',
width: 90,
},
{
field: 'warehouseName',
title: '仓库',
minWidth: 120,
},
{
field: 'locationName',
title: '库区',
minWidth: 120,
},
{
field: 'areaName',
title: '库位',
minWidth: 120,
},
{
field: 'quantity',
title: '数量',
minWidth: 120,
},
{
field: 'takingQuantity',
title: '盘点数量',
minWidth: 120,
},
...(editable
? [
{
title: '操作',
width: 160,
fixed: 'right',
slots: { default: 'actions' },
} as const,
]
: []),
];
}
/** 盘点结果新增/修改的表单 */
export function useResultFormSchema(
formApi?: VbenFormApi,
taskLines: MesWmStockTakingTaskLineApi.StockTakingTaskLine[] = [],
): VbenFormSchema[] {
return [
{
fieldName: 'lineId',
label: '盘点清单',
component: 'Select',
componentProps: {
clearable: true,
// 选择盘点清单后,自动带出物料、批次和仓储位置信息
onChange: async (lineId?: number) => {
const line = taskLines.find((item) => item.id === lineId);
await formApi?.setValues({
areaId: line?.areaId,
batchCode: line?.batchCode,
itemId: line?.itemId,
locationId: line?.locationId,
materialStockId: line?.materialStockId,
warehouseId: line?.warehouseId,
});
},
options: taskLines.map((line) => ({
label: `${line.itemCode} - ${line.itemName} (${line.warehouseName}${
line.locationName ? ` / ${line.locationName}` : ''
}${line.areaName ? ` / ${line.areaName}` : ''})`,
value: line.id,
})),
placeholder: '请选择盘点清单(可选)',
},
formItemClass: 'col-span-3',
},
{
fieldName: 'itemId',
label: '物料',
component: markRaw(MdItemSelect),
componentProps: {
placeholder: '请选择物料',
},
rules: 'selectRequired',
},
{
fieldName: 'batchCode',
label: '批次编码',
component: 'Input',
componentProps: {
placeholder: '请输入批次编码',
},
},
{
fieldName: 'takingQuantity',
label: '盘点数量',
component: 'InputNumber',
componentProps: {
class: '!w-full',
controlsPosition: 'right',
placeholder: '请输入盘点数量',
precision: 2,
},
rules: 'required',
},
{
fieldName: 'warehouseId',
label: '仓库',
component: markRaw(WmWarehouseSelect),
componentProps: {
// 仓库变化时清空库区和库位
onChange: () =>
formApi?.setValues({
areaId: undefined,
locationId: undefined,
}),
placeholder: '请选择仓库',
},
rules: 'selectRequired',
},
{
fieldName: 'locationId',
label: '库区',
component: markRaw(WmWarehouseLocationSelect),
rules: 'selectRequired',
dependencies: {
triggerFields: ['warehouseId'],
show: (values) => !!values.warehouseId,
componentProps: (values) => ({
onChange: () => formApi?.setFieldValue('areaId', undefined),
placeholder: '请选择库区',
warehouseId: values.warehouseId,
}),
},
},
{
fieldName: 'areaId',
label: '库位',
component: markRaw(WmWarehouseAreaSelect),
rules: 'selectRequired',
dependencies: {
triggerFields: ['locationId'],
show: (values) => !!values.locationId,
componentProps: (values) => ({
locationId: values.locationId,
placeholder: '请选择库位',
}),
},
},
{
fieldName: 'remark',
label: '备注',
component: 'Textarea',
formItemClass: 'col-span-3',
componentProps: {
placeholder: '请输入备注',
rows: 3,
},
},
];
}

View File

@ -0,0 +1,211 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingTaskApi } from '#/api/mes/wm/stocktaking/task';
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { downloadFileFromBlobPart } from '@vben/utils';
import { ElButton, ElLoading, ElMessage } from 'element-plus';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
cancelStockTaking,
deleteStockTaking,
exportStockTaking,
getStockTakingPage,
} from '#/api/mes/wm/stocktaking/task';
import { $t } from '#/locales';
import { MesWmStockTakingTaskStatusEnum } from '#/views/mes/utils/constants';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
destroyOnClose: true,
});
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 创建盘点任务 */
function handleCreate() {
formModalApi.setData({ formType: 'create' }).open();
}
/** 查看盘点任务 */
function handleDetail(row: MesWmStockTakingTaskApi.StockTakingTask) {
formModalApi.setData({ formType: 'detail', id: row.id }).open();
}
/** 编辑盘点任务 */
function handleEdit(row: MesWmStockTakingTaskApi.StockTakingTask) {
formModalApi.setData({ formType: 'update', id: row.id }).open();
}
/** 提交盘点任务 */
function handleSubmit(row: MesWmStockTakingTaskApi.StockTakingTask) {
formModalApi.setData({ formType: 'submit', id: row.id }).open();
}
/** 执行盘点 */
function handleExecute(row: MesWmStockTakingTaskApi.StockTakingTask) {
formModalApi.setData({ formType: 'execute', id: row.id }).open();
}
/** 取消盘点任务 */
async function handleCancel(row: MesWmStockTakingTaskApi.StockTakingTask) {
await cancelStockTaking(row.id!);
ElMessage.success('取消成功');
handleRefresh();
}
/** 删除盘点任务 */
async function handleDelete(row: MesWmStockTakingTaskApi.StockTakingTask) {
const loadingInstance = ElLoading.service({
text: $t('ui.actionMessage.deleting', [row.code]),
});
try {
await deleteStockTaking(row.id!);
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.code]));
handleRefresh();
} finally {
loadingInstance.close();
}
}
/** 导出表格 */
async function handleExport() {
const data = await exportStockTaking(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '盘点任务.xls', source: data });
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getStockTakingPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<MesWmStockTakingTaskApi.StockTakingTask>,
});
const StatusEnum = MesWmStockTakingTaskStatusEnum;
</script>
<template>
<Page auto-content-height>
<template #doc>
<DocAlert
title="【仓库】库存盘点"
url="https://doc.iocoder.cn/mes/wm/stocktaking/"
/>
</template>
<FormModal @success="handleRefresh" />
<Grid table-title="">
<template #toolbar-tools>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['盘点任务']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['mes:wm-stock-taking-task:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['mes:wm-stock-taking-task:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #code="{ row }">
<ElButton link type="primary" @click="handleDetail(row)">
{{ row.code }}
</ElButton>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'primary',
link: true,
icon: ACTION_ICON.EDIT,
auth: ['mes:wm-stock-taking-task:update'],
ifShow: row.status === StatusEnum.PREPARE,
onClick: handleEdit.bind(null, row),
},
{
label: '提交',
type: 'primary',
link: true,
auth: ['mes:wm-stock-taking-task:update'],
ifShow: row.status === StatusEnum.PREPARE,
onClick: handleSubmit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'danger',
link: true,
icon: ACTION_ICON.DELETE,
auth: ['mes:wm-stock-taking-task:delete'],
ifShow: row.status === StatusEnum.PREPARE,
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.code]),
confirm: handleDelete.bind(null, row),
},
},
{
label: '执行盘点',
type: 'primary',
link: true,
auth: ['mes:wm-stock-taking-task:update'],
ifShow: row.status === StatusEnum.APPROVING,
onClick: handleExecute.bind(null, row),
},
{
label: '取消',
type: 'danger',
link: true,
auth: ['mes:wm-stock-taking-task:update'],
ifShow: row.status === StatusEnum.APPROVING,
popConfirm: {
title: '确认取消该盘点任务?取消后不可恢复。',
confirm: handleCancel.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@ -0,0 +1,237 @@
<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesWmStockTakingTaskApi } from '#/api/mes/wm/stocktaking/task';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { useUserStore } from '@vben/stores';
import { ElButton, ElMessage, ElPopconfirm, ElTabPane, ElTabs } from 'element-plus';
import { useVbenForm } from '#/adapter/form';
import {
createStockTaking,
finishStockTaking,
getStockTaking,
submitStockTaking,
updateStockTaking,
} from '#/api/mes/wm/stocktaking/task';
import { $t } from '#/locales';
import { MesWmStockTakingTaskStatusEnum } from '#/views/mes/utils/constants';
import { useFormSchema } from '../data';
import LineList from './line-list.vue';
import ResultList from './result-list.vue';
const emit = defineEmits(['success']);
const userStore = useUserStore();
const formType = ref<FormType>('create');
const formData = ref<MesWmStockTakingTaskApi.StockTakingTask>();
const subTabsName = ref('lines'); // tab
const originalSnapshot = ref(''); //
const isEditable = computed(() =>
['create', 'update'].includes(formType.value),
);
const isExecute = computed(() => formType.value === 'execute'); //
const isSubmit = computed(() => formType.value === 'submit'); //
const canSubmit = computed(
() =>
formType.value === 'update' &&
formData.value?.status === MesWmStockTakingTaskStatusEnum.PREPARE,
);
const showLineTab = computed(() => !formData.value?.blindFlag); //
const showResultTab = computed(
() =>
isExecute.value ||
(!!formData.value?.status &&
formData.value.status !== MesWmStockTakingTaskStatusEnum.PREPARE),
);
const getTitle = computed(() => {
switch (formType.value) {
case 'detail': {
return $t('ui.actionTitle.view', ['盘点任务']);
}
case 'execute': {
return '执行盘点';
}
case 'submit': {
return '提交盘点任务';
}
case 'update': {
return $t('ui.actionTitle.edit', ['盘点任务']);
}
default: {
return $t('ui.actionTitle.create', ['盘点任务']);
}
}
});
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-1',
labelWidth: 110,
},
layout: 'horizontal',
schema: [],
showDefaultActions: false,
wrapperClass: 'grid-cols-3',
});
/** 提交盘点任务:表单有修改时先保存,再调用提交接口 */
async function handleSubmit() {
const { valid } = await formApi.validate();
if (!valid || !formData.value?.id) {
return;
}
modalApi.lock();
try {
const current = JSON.stringify(await formApi.getValues());
if (current !== originalSnapshot.value) {
const data =
(await formApi.getValues()) as MesWmStockTakingTaskApi.StockTakingTask;
await updateStockTaking({ ...formData.value, ...data });
originalSnapshot.value = current;
}
await submitStockTaking(formData.value.id);
ElMessage.success('提交成功');
await modalApi.close();
emit('success');
} finally {
modalApi.unlock();
}
}
/** 执行盘点 */
async function handleExecute() {
if (!formData.value?.id) {
return;
}
modalApi.lock();
try {
await finishStockTaking(formData.value.id);
ElMessage.success('执行盘点成功');
await modalApi.close();
emit('success');
} finally {
modalApi.unlock();
}
}
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
if (!isEditable.value) {
await modalApi.close();
return;
}
const { valid } = await formApi.validate();
if (!valid) {
return;
}
modalApi.lock();
//
const data =
(await formApi.getValues()) as MesWmStockTakingTaskApi.StockTakingTask;
try {
if (formData.value?.id) {
await updateStockTaking({ ...formData.value, ...data });
formData.value = { ...formData.value, ...data };
} else {
const id = await createStockTaking(data);
formData.value = {
...data,
id,
status: MesWmStockTakingTaskStatusEnum.PREPARE,
};
await formApi.setFieldValue('id', id);
await formApi.setFieldValue('status', formData.value.status);
formType.value = 'update';
}
originalSnapshot.value = JSON.stringify(await formApi.getValues());
emit('success');
ElMessage.success($t('ui.actionMessage.operationSuccess'));
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
originalSnapshot.value = '';
return;
}
//
const data = modalApi.getData<{ formType: FormType; id?: number }>();
formType.value = data.formType;
subTabsName.value = data.formType === 'execute' ? 'results' : 'lines';
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
formApi.setDisabled(!isEditable.value);
modalApi.setState({ showConfirmButton: isEditable.value });
if (data?.id) {
modalApi.lock();
try {
formData.value = await getStockTaking(data.id);
// values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
} else {
//
await formApi.setFieldValue('userId', userStore.userInfo?.id);
}
originalSnapshot.value = JSON.stringify(await formApi.getValues());
},
});
</script>
<template>
<Modal :title="getTitle" class="w-4/5">
<Form class="mx-4" />
<ElTabs
v-if="formData?.id"
v-model="subTabsName"
class="mx-4 mt-4"
type="border-card"
>
<ElTabPane v-if="showLineTab" label="盘点清单" name="lines">
<LineList :form-type="formType" :task-id="formData.id" />
</ElTabPane>
<ElTabPane v-if="showResultTab" label="盘点结果" name="results">
<ResultList
:form-type="isExecute ? 'execute' : 'detail'"
:task-id="formData.id"
/>
</ElTabPane>
</ElTabs>
<template #prepend-footer>
<div class="flex flex-auto items-center gap-2">
<ElPopconfirm
v-if="canSubmit || isSubmit"
title="确认提交该盘点任务?【提交后将不能修改】"
width="240"
@confirm="handleSubmit"
>
<template #reference>
<ElButton type="primary">提交</ElButton>
</template>
</ElPopconfirm>
<ElPopconfirm
v-if="isExecute"
title="确认执行盘点操作?"
width="240"
@confirm="handleExecute"
>
<template #reference>
<ElButton type="primary">执行盘点</ElButton>
</template>
</ElPopconfirm>
</div>
</template>
</Modal>
</template>

View File

@ -0,0 +1,147 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
import type { MesWmStockTakingTaskLineApi } from '#/api/mes/wm/stocktaking/task/line';
import { computed, ref } from 'vue';
import { ElLoading, ElMessage } from 'element-plus';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
createStockTakingTaskLine,
deleteStockTakingTaskLine,
getStockTakingTaskLinePage,
} from '#/api/mes/wm/stocktaking/task/line';
import { $t } from '#/locales';
import { WmMaterialStockSelectDialog } from '#/views/mes/wm/materialstock/components';
import { type FormType, useLineGridColumns } from '../data';
const props = defineProps<{
formType: FormType;
taskId: number;
}>();
const isEditable = computed(() => props.formType === 'update'); //
const dialogRef = ref<InstanceType<typeof WmMaterialStockSelectDialog>>();
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 打开库存选择弹窗批量添加物料 */
function handleAdd() {
dialogRef.value?.open([], { multiple: true });
}
/** 库存选择确认回调:将选中的库存记录批量创建为盘点行 */
async function handleStockSelected(rows: MesWmMaterialStockApi.MaterialStock[]) {
if (rows.length === 0) {
return;
}
for (const stock of rows) {
await createStockTakingTaskLine({
areaId: stock.areaId,
batchId: stock.batchId,
itemId: stock.itemId,
locationId: stock.locationId,
materialStockId: stock.id,
quantity: stock.quantity,
taskId: props.taskId,
warehouseId: stock.warehouseId,
});
}
ElMessage.success(`成功添加 ${rows.length} 条盘点行`);
handleRefresh();
}
/** 删除盘点行 */
async function handleDelete(
row: MesWmStockTakingTaskLineApi.StockTakingTaskLine,
) {
const loadingInstance = ElLoading.service({
text: $t('ui.actionMessage.deleting', [row.itemName]),
});
try {
await deleteStockTakingTaskLine(row.id!);
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.itemName]));
handleRefresh();
} finally {
loadingInstance.close();
}
}
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: useLineGridColumns(isEditable.value),
height: 360,
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }) => {
if (!props.taskId) {
return { list: [], total: 0 };
}
return await getStockTakingTaskLinePage({
pageNo: page.currentPage,
pageSize: page.pageSize,
taskId: props.taskId,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
},
} as VxeTableGridOptions<MesWmStockTakingTaskLineApi.StockTakingTaskLine>,
});
defineExpose({ refresh: handleRefresh });
</script>
<template>
<div>
<WmMaterialStockSelectDialog
ref="dialogRef"
@selected="handleStockSelected"
/>
<Grid table-title="">
<template v-if="isEditable" #toolbar-tools>
<TableAction
:actions="[
{
label: '添加物料',
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['mes:wm-stock-taking-task:update'],
onClick: handleAdd,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.delete'),
type: 'danger',
link: true,
icon: ACTION_ICON.DELETE,
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.itemName]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</div>
</template>

View File

@ -0,0 +1,107 @@
<script lang="ts" setup>
import type { MesWmStockTakingResultApi } from '#/api/mes/wm/stocktaking/task/result';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { ElMessage } from 'element-plus';
import { useVbenForm } from '#/adapter/form';
import { getStockTakingTaskLineSimpleList } from '#/api/mes/wm/stocktaking/task/line';
import {
createStockTakingResult,
getStockTakingResult,
updateStockTakingResult,
} from '#/api/mes/wm/stocktaking/task/result';
import { $t } from '#/locales';
import { useResultFormSchema } from '../data';
const emit = defineEmits(['success']);
const formData = ref<MesWmStockTakingResultApi.StockTakingResult>();
const taskId = ref<number>();
const isExecute = ref(false); //
const getTitle = computed(() =>
formData.value?.id
? $t('ui.actionTitle.edit', ['盘点结果'])
: $t('ui.actionTitle.create', ['盘点结果']),
);
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-1',
labelWidth: 100,
},
layout: 'horizontal',
schema: [],
showDefaultActions: false,
wrapperClass: 'grid-cols-3',
});
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
const { valid } = await formApi.validate();
if (!valid) {
return;
}
modalApi.lock();
//
const data =
(await formApi.getValues()) as MesWmStockTakingResultApi.StockTakingResult;
data.taskId = taskId.value;
try {
await (formData.value?.id
? updateStockTakingResult({ ...data, id: formData.value.id })
: createStockTakingResult(data));
//
await modalApi.close();
emit('success');
ElMessage.success($t('ui.actionMessage.operationSuccess'));
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
return;
}
//
const data = modalApi.getData<{
execute?: boolean;
id?: number;
taskId: number;
}>();
taskId.value = data.taskId;
isExecute.value = !!data.execute;
//
const taskLines =
isExecute.value && !data.id
? await getStockTakingTaskLineSimpleList(data.taskId)
: [];
formApi.setState({ schema: useResultFormSchema(formApi, taskLines) });
if (!data.id) {
return;
}
modalApi.lock();
try {
formData.value = await getStockTakingResult(data.id);
// values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
},
});
</script>
<template>
<Modal :title="getTitle" class="w-3/5">
<Form />
</Modal>
</template>

View File

@ -0,0 +1,140 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmStockTakingResultApi } from '#/api/mes/wm/stocktaking/task/result';
import { computed } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { ElLoading, ElMessage } from 'element-plus';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteStockTakingResult,
getStockTakingResultPage,
} from '#/api/mes/wm/stocktaking/task/result';
import { $t } from '#/locales';
import { useResultGridColumns } from '../data';
import ResultForm from './result-form.vue';
const props = defineProps<{
formType: 'detail' | 'execute';
taskId: number;
}>();
const isExecute = computed(() => props.formType === 'execute'); //
const [ResultFormModal, resultFormModalApi] = useVbenModal({
connectedComponent: ResultForm,
destroyOnClose: true,
});
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 新增盘点结果 */
function handleCreate() {
resultFormModalApi
.setData({ execute: true, taskId: props.taskId })
.open();
}
/** 编辑盘点结果 */
function handleEdit(row: MesWmStockTakingResultApi.StockTakingResult) {
resultFormModalApi.setData({ id: row.id, taskId: props.taskId }).open();
}
/** 删除盘点结果 */
async function handleDelete(
row: MesWmStockTakingResultApi.StockTakingResult,
) {
const loadingInstance = ElLoading.service({
text: $t('ui.actionMessage.deleting', [row.itemName]),
});
try {
await deleteStockTakingResult(row.id!);
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.itemName]));
handleRefresh();
} finally {
loadingInstance.close();
}
}
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: useResultGridColumns(isExecute.value),
height: 360,
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }) => {
if (!props.taskId) {
return { list: [], total: 0 };
}
return await getStockTakingResultPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
taskId: props.taskId,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
},
} as VxeTableGridOptions<MesWmStockTakingResultApi.StockTakingResult>,
});
defineExpose({ refresh: handleRefresh });
</script>
<template>
<div>
<ResultFormModal @success="handleRefresh" />
<Grid table-title="">
<template v-if="isExecute" #toolbar-tools>
<TableAction
:actions="[
{
label: $t('common.create'),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['mes:wm-stock-taking-task:update'],
onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'primary',
link: true,
icon: ACTION_ICON.EDIT,
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'danger',
link: true,
icon: ACTION_ICON.DELETE,
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.itemName]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</div>
</template>

View File

@ -132,6 +132,7 @@ catalog:
czg: ^1.13.1
dayjs: ^1.11.20
defu: ^6.1.7
dhtmlx-gantt: ^9.1.1
diagram-js: ^12.8.1
dotenv: ^17.4.2
echarts: ^6.1.0