From 557c2b3c50027c2eb6fb9f7e562fc2c7b2b38203 Mon Sep 17 00:00:00 2001
From: quxin <592870522@qq.com>
Date: 星期五, 04 九月 2026 11:37:17 +0800
Subject: [PATCH] 修复: 生产领料后的退料应该是选择领料的记录,不是选择库存的记录(同时库存数据需要根据领料退料变动,比如库存100,领用80,然后领用80中退料30,库存应由原来的20变成50)
---
src/views/wls/productissue/components/index.ts | 2
src/views/wls/productissue/components/select-dialog.vue | 106 +++++++++++++++++++++
src/views/wls/productissue/components/select.vue | 141 ++++++++++++++++++++++++++++
3 files changed, 249 insertions(+), 0 deletions(-)
diff --git a/src/views/wls/productissue/components/index.ts b/src/views/wls/productissue/components/index.ts
new file mode 100644
index 0000000..aa8b15e
--- /dev/null
+++ b/src/views/wls/productissue/components/index.ts
@@ -0,0 +1,2 @@
+export { default as WmProductIssueLineSelect } from './select.vue';
+export { default as WmProductIssueLineSelectDialog } from './select-dialog.vue';
diff --git a/src/views/wls/productissue/components/select-dialog.vue b/src/views/wls/productissue/components/select-dialog.vue
new file mode 100644
index 0000000..09bb732
--- /dev/null
+++ b/src/views/wls/productissue/components/select-dialog.vue
@@ -0,0 +1,106 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesWmProductIssueLineApi } from '#/api/mes/wm/productissue/line';
+
+import { nextTick, ref } from 'vue';
+
+import { message, Modal } from 'ant-design-vue';
+
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { getReturnableIssueLines } from '#/api/mes/wm/productissue/line';
+
+const props = defineProps<{
+ workOrderId?: number;
+}>();
+
+const emit = defineEmits<{
+ selected: [rows: MesWmProductIssueLineApi.ProductIssueLine[]];
+}>();
+
+const open = ref(false);
+const selectedRows = ref<MesWmProductIssueLineApi.ProductIssueLine[]>([]);
+
+const columns: VxeTableGridOptions<MesWmProductIssueLineApi.ProductIssueLine>['columns'] = [
+ { type: 'radio', width: 60 },
+ { field: 'itemCode', title: '鐗╂枡缂栫爜', minWidth: 120 },
+ { field: 'itemName', title: '鐗╂枡鍚嶇О', minWidth: 140 },
+ { field: 'specification', title: '瑙勬牸鍨嬪彿', minWidth: 120 },
+ { field: 'unitMeasureName', title: '鍗曚綅', width: 80 },
+ { field: 'quantity', title: '鍙��鏁伴噺', width: 100 },
+ { field: 'batchCode', title: '鎵规鍙�', minWidth: 120 },
+];
+
+const [Grid, gridApi] = useVbenVxeGrid({
+ gridOptions: {
+ columns,
+ height: 480,
+ keepSource: true,
+ radioConfig: { highlight: true, trigger: 'row' },
+ proxyConfig: {
+ ajax: {
+ query: async () => {
+ if (!props.workOrderId) {
+ return { list: [], total: 0 };
+ }
+ const list = await getReturnableIssueLines(props.workOrderId);
+ return { list, total: list.length };
+ },
+ },
+ },
+ rowConfig: {
+ keyField: 'id',
+ isHover: true,
+ },
+ toolbarConfig: {
+ refresh: true,
+ },
+ } as VxeTableGridOptions<MesWmProductIssueLineApi.ProductIssueLine>,
+ gridEvents: {
+ radioChange: ({ row }: { row: MesWmProductIssueLineApi.ProductIssueLine }) => {
+ selectedRows.value = [row];
+ },
+ cellDblclick: async ({ row }: { row: MesWmProductIssueLineApi.ProductIssueLine }) => {
+ selectedRows.value = [row];
+ await gridApi.grid.setRadioRow(row);
+ handleConfirm();
+ },
+ },
+});
+
+async function openModal() {
+ open.value = true;
+ selectedRows.value = [];
+ await nextTick();
+ await gridApi.grid.clearRadioRow();
+ await gridApi.query();
+}
+
+function closeModal() {
+ open.value = false;
+ selectedRows.value = [];
+}
+
+function handleConfirm() {
+ if (selectedRows.value.length === 0) {
+ message.warning('璇烽�夋嫨涓�鏉¢鏂欒褰�');
+ return;
+ }
+ emit('selected', selectedRows.value);
+ 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>
diff --git a/src/views/wls/productissue/components/select.vue b/src/views/wls/productissue/components/select.vue
new file mode 100644
index 0000000..8a86b5f
--- /dev/null
+++ b/src/views/wls/productissue/components/select.vue
@@ -0,0 +1,141 @@
+<script lang="ts" setup>
+import type { MesWmProductIssueLineApi } from '#/api/mes/wm/productissue/line';
+
+import { computed, ref, useAttrs, watch } from 'vue';
+
+import { IconifyIcon } from '@vben/icons';
+
+import { Input, Tooltip } from 'ant-design-vue';
+
+import { getProductIssueLine } from '#/api/mes/wm/productissue/line';
+
+import ProductIssueLineSelectDialog from './select-dialog.vue';
+
+defineOptions({ name: 'WmProductIssueLineSelect', inheritAttrs: false });
+
+const props = withDefaults(
+ defineProps<{
+ allowClear?: boolean;
+ disabled?: boolean;
+ modelValue?: number;
+ placeholder?: string;
+ workOrderId?: number;
+ }>(),
+ {
+ allowClear: true,
+ disabled: false,
+ modelValue: undefined,
+ placeholder: '璇烽�夋嫨棰嗘枡璁板綍',
+ workOrderId: undefined,
+ },
+);
+
+const emit = defineEmits<{
+ change: [item: MesWmProductIssueLineApi.ProductIssueLine | undefined];
+ 'update:modelValue': [value: number | undefined];
+}>();
+
+const attrs = useAttrs();
+const dialogRef = ref<InstanceType<typeof ProductIssueLineSelectDialog>>();
+const hovering = ref(false);
+const selectedItem = ref<MesWmProductIssueLineApi.ProductIssueLine>();
+
+const displayLabel = computed(() => {
+ const item = selectedItem.value;
+ if (!item) {
+ return '';
+ }
+ return `${item.itemCode || '-'} | ${item.itemName || '-'} | 鏁伴噺:${item.quantity}`;
+});
+
+const showClear = computed(
+ () =>
+ props.allowClear &&
+ !props.disabled &&
+ hovering.value &&
+ props.modelValue !== null,
+);
+
+async function resolveItemById(id: number | undefined) {
+ if (!id) {
+ selectedItem.value = undefined;
+ return;
+ }
+ if (selectedItem.value?.id === id) {
+ return;
+ }
+ selectedItem.value = await getProductIssueLine(id);
+}
+
+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;
+ }
+ dialogRef.value?.open();
+}
+
+function handleSelected(rows: MesWmProductIssueLineApi.ProductIssueLine[]) {
+ 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.itemCode || '-' }}</div>
+ <div>鐗╂枡鍚嶇О锛歿{ selectedItem.itemName || '-' }}</div>
+ <div>瑙勬牸鍨嬪彿锛歿{ selectedItem.specification || '-' }}</div>
+ <div>鍙��鏁伴噺锛歿{ selectedItem.quantity ?? '-' }}</div>
+ <div>鎵规鍙凤細{{ selectedItem.batchCode || '-' }}</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>
+ <ProductIssueLineSelectDialog
+ ref="dialogRef"
+ :work-order-id="workOrderId"
+ @selected="handleSelected"
+ />
+</template>
--
Gitblit v1.9.3