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