From b8de520fa696d8a95393aa3ff544c5a6f5c36001 Mon Sep 17 00:00:00 2001
From: 吴羡 <16976746+Xian-TT3@user.noreply.gitee.com>
Date: 星期三, 30 九月 2026 15:33:41 +0800
Subject: [PATCH] 宁夏万通需求:出库单行支持主/辅单位录入;物料型号维护;考勤节假日配置时间段;设备批量删除;质检导出按筛选条件

---
 src/views/wls/itemreceipt/data.ts |  394 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 380 insertions(+), 14 deletions(-)

diff --git a/src/views/wls/itemreceipt/data.ts b/src/views/wls/itemreceipt/data.ts
index 291714f..c2e3f70 100644
--- a/src/views/wls/itemreceipt/data.ts
+++ b/src/views/wls/itemreceipt/data.ts
@@ -1,10 +1,13 @@
 import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MdmItemModelApi } from '#/api/mdm/item-model';
+import type { MesMdItemApi } from '#/api/mes/md/item';
 import type { MesWmArrivalNoticeApi } from '#/api/mes/wm/arrivalnotice';
 import type { MesWmArrivalNoticeLineApi } from '#/api/mes/wm/arrivalnotice/line';
 import type { MesWmItemReceiptApi } from '#/api/mes/wm/itemreceipt';
 import type { MesWmItemReceiptDetailApi } from '#/api/mes/wm/itemreceipt/detail';
 import type { MesWmItemReceiptLineApi } from '#/api/mes/wm/itemreceipt/line';
+import type { SrmSupplierApi } from '#/api/srm/supplier';
 
 import { h, markRaw } from 'vue';
 
@@ -16,10 +19,12 @@
 
 import { Button } from 'ant-design-vue';
 
+import { getItem as getMdmItem } from '#/api/mdm/item';
+import { getItemModelListByItem } from '#/api/mdm/item-model';
 import { generateAutoCode } from '#/api/mes/md/autocode/record';
 import { getRangePickerDefaultProps } from '#/utils';
 import { MdItemSelect } from '#/views/mes/md/item/components';
-import { ErpSupplierSelect } from '#/views/erp/purchase/supplier/components';
+import { SrmSupplierSelect } from '#/views/srm/supplier/components';
 import {
   WmArrivalNoticeLineSelect,
   WmArrivalNoticeSelect,
@@ -132,10 +137,10 @@
     {
       fieldName: 'vendorId',
       label: '渚涘簲鍟�',
-      component: markRaw(ErpSupplierSelect),
+      component: markRaw(SrmSupplierSelect),
       componentProps: {
         placeholder: '璇烽�夋嫨渚涘簲鍟�',
-        onChange: async (supplier?: any) => {
+        onChange: async (supplier?: SrmSupplierApi.SupplierVO) => {
           await formApi?.setFieldValue('vendorName', supplier?.name);
         },
       },
@@ -193,7 +198,7 @@
     {
       fieldName: 'vendorId',
       label: '渚涘簲鍟�',
-      component: markRaw(ErpSupplierSelect),
+      component: markRaw(SrmSupplierSelect),
       componentProps: {
         placeholder: '璇烽�夋嫨渚涘簲鍟�',
       },
@@ -285,9 +290,24 @@
       minWidth: 120,
     },
     {
+      field: 'itemModel',
+      title: '鐗╂枡鍨嬪彿',
+      minWidth: 120,
+    },
+    {
       field: 'unitMeasureName',
-      title: '鍗曚綅',
-      width: 80,
+      title: '涓昏閲忓崟浣�',
+      width: 100,
+    },
+    {
+      field: 'enterUnitMeasureName',
+      title: '褰曞叆鍗曚綅',
+      width: 90,
+    },
+    {
+      field: 'enterQuantity',
+      title: '褰曞叆鏁伴噺',
+      width: 100,
     },
     {
       field: 'receivedQuantity',
@@ -312,11 +332,221 @@
   ];
 }
 
+/** 褰曞叆鍗曚綅閫夐」锛堝惈鍒扮墿鏂欎富鍗曚綅鐨勬崲绠楁瘮鐜囷級 */
+export interface ItemEnterUnitOption {
+  id: number;
+  name: string;
+  rate: number;
+}
+
+/** 鍨嬪彿涓嬫媺閫夐」锛欰piSelect 浼氭妸 id/model 鏄犲皠鎴� value/label锛屽叾浣欏瓧娈靛師鏍蜂繚鐣� */
+type ItemModelSelectOption = Pick<
+  MdmItemModelApi.ItemModel,
+  'unitMeasureId' | 'unitMeasureName' | 'unitMeasureRate'
+>;
+
+/** 鍏ュ簱鍗曡琛ㄥ崟涓笌鍙屽崟浣嶆崲绠楃浉鍏崇殑瀛楁 */
+interface LineUnitFormValues {
+  enterQuantity?: number;
+  enterUnitMeasureId?: number;
+  enterUnitMeasureName?: string;
+  enterUnitRate?: number;
+  itemId?: number;
+  itemModelId?: number;
+  itemPrimaryUnitId?: number;
+  itemPrimaryUnitName?: string;
+}
+
+/** 鍙屽崟浣嶆崲绠楃敤鐨勪复鏃惰〃鍗曞瓧娈碉紙鍙緵鍓嶇鎹㈢畻涓庡睍绀猴紝鎻愪氦鍓嶅繀椤诲墧闄わ級 */
+const LINE_TEMP_FIELDS = [
+  'itemPrimaryUnitId',
+  'itemPrimaryUnitName',
+  'enterUnitMeasureName',
+  'enterUnitRate',
+] as const;
+
+/** 鍓旈櫎鍙屽崟浣嶆崲绠楃敤鐨勪复鏃跺瓧娈碉紝寰楀埌鍙洿鎺ユ彁浜ょ殑琛屾暟鎹� */
+export function omitLineTempFields(
+  values: Record<string, unknown>,
+): MesWmItemReceiptLineApi.ItemReceiptLine {
+  const data = { ...values };
+  for (const field of LINE_TEMP_FIELDS) {
+    delete data[field];
+  }
+  return data as MesWmItemReceiptLineApi.ItemReceiptLine;
+}
+
+/** 鎹㈢畻姣旂巼鏄惁鏈夋晥锛堝繀椤诲ぇ浜� 0锛� */
+function isValidRate(rate?: number): rate is number {
+  return typeof rate === 'number' && rate > 0;
+}
+
+/** 鎹㈢畻缁撴灉鎸� 6 浣嶅皬鏁板睍绀猴紙鏈�缁堜互鍚庣鎹㈢畻缁撴灉涓哄噯锛� */
+function roundQuantity(quantity: number): number {
+  return Math.round(quantity * 1e6) / 1e6;
+}
+
+/**
+ * 鏌ヨ鐗╂枡鍙�夌殑褰曞叆鍗曚綅锛氫富鍗曚綅 + 杈呭崟浣�
+ *
+ * 閫夊畾鍨嬪彿鏃跺彧淇濈暀銆岀墿鏂欎富鍗曚綅銆嶄笌銆屽瀷鍙疯閲忓崟浣嶃�嶄袱绉嶅彛寰勶紝涓庡悗绔崲绠楄鍒欎繚鎸佷竴鑷�
+ */
+export async function getItemEnterUnits(params: {
+  itemId?: number;
+  itemModelId?: number;
+}): Promise<ItemEnterUnitOption[]> {
+  const itemId = params.itemId;
+  if (!itemId) {
+    return [];
+  }
+  const item = await getMdmItem(itemId);
+  if (!item?.unitMeasureId) {
+    return [];
+  }
+  // 涓诲崟浣嶅缁堝彲閫夛紝鎸変富鍗曚綅褰曞叆鏃朵笉鍋氭崲绠�
+  const primaryUnit: ItemEnterUnitOption = {
+    id: item.unitMeasureId,
+    name: item.unitMeasureName || '鐗╂枡涓诲崟浣�',
+    rate: 1,
+  };
+  const unitOptions: ItemEnterUnitOption[] = [primaryUnit];
+  const pushUnit = (id?: number, name?: string, rate?: number) => {
+    if (
+      !id ||
+      id === primaryUnit.id ||
+      !isValidRate(rate) ||
+      unitOptions.some((unit) => unit.id === id)
+    ) {
+      return;
+    }
+    unitOptions.push({ id, name: name || '杈呭崟浣�', rate });
+  };
+  if (params.itemModelId) {
+    const models = await getItemModelListByItem(itemId);
+    const modelOption = models?.find((option) => option.id === params.itemModelId);
+    pushUnit(
+      modelOption?.unitMeasureId,
+      modelOption?.unitMeasureName,
+      modelOption?.unitMeasureRate,
+    );
+    return unitOptions;
+  }
+  // 鏈�夊瀷鍙锋椂锛岀墿鏂欎富鏁版嵁涓婇厤缃簡鎹㈢畻姣旂巼鐨勮緟鍗曚綅鍙��
+  pushUnit(item.unitMeasureId2, item.unitMeasureName2, item.unitMeasureRate1);
+  pushUnit(item.unitMeasureId3, item.unitMeasureName3, item.unitMeasureRate2);
+  return unitOptions;
+}
+
+/** 鏌ヨ鏌愪釜褰曞叆鍗曚綅鍒扮墿鏂欎富鍗曚綅鐨勬崲绠楁瘮鐜囷紙缂栬緫鍥炴樉鏃惰ˉ榻愬墠绔崲绠椾笂涓嬫枃锛� */
+export async function getEnterUnitRate(
+  itemId?: number,
+  itemModelId?: number,
+  enterUnitMeasureId?: number,
+): Promise<number | undefined> {
+  if (!itemId || !enterUnitMeasureId) {
+    return undefined;
+  }
+  const unitOptions = await getItemEnterUnits({ itemId, itemModelId });
+  return unitOptions.find((unit) => unit.id === enterUnitMeasureId)?.rate;
+}
+
 /** 鍏ュ簱鍗曡鏂板/淇敼鐨勮〃鍗� */
 export function useLineFormSchema(
   hasNotice: boolean,
   formApi?: VbenFormApi,
 ): VbenFormSchema[] {
+  /** 鏄惁鎸夌墿鏂欎富鍗曚綅褰曞叆锛堟湭閫夊綍鍏ュ崟浣嶃�佹垨褰曞叆鍗曚綅灏辨槸鐗╂枡涓诲崟浣嶏級 */
+  function isPrimaryUnitEnter(values: LineUnitFormValues) {
+    return (
+      !values.enterUnitMeasureId ||
+      values.enterUnitMeasureId === values.itemPrimaryUnitId
+    );
+  }
+
+  /** 鍒囨崲鐗╂枡锛氶噸鏂拌褰曠墿鏂欎富鍗曚綅锛屽苟娓呯┖鍨嬪彿銆佸綍鍏ュ崟浣嶄笌鏁伴噺锛堝彛寰勯兘闅忕墿鏂欏彉鍖栵級 */
+  async function handleItemChange(itemId?: number) {
+    // 鍏堟竻绌猴紝閬垮厤涓婁竴涓墿鏂欑殑鍨嬪彿涓庡崟浣嶅彛寰勬畫鐣�
+    await formApi?.setValues({
+      itemId,
+      itemModelId: undefined,
+      itemPrimaryUnitId: undefined,
+      itemPrimaryUnitName: undefined,
+      enterUnitMeasureId: undefined,
+      enterUnitMeasureName: undefined,
+      enterUnitRate: undefined,
+      enterQuantity: undefined,
+      receivedQuantity: undefined,
+    });
+    if (!itemId) {
+      return;
+    }
+    const item = await getMdmItem(itemId);
+    await formApi?.setValues({
+      itemPrimaryUnitId: item?.unitMeasureId,
+      itemPrimaryUnitName: item?.unitMeasureName,
+    });
+  }
+
+  /** 閫夋嫨鍨嬪彿锛氶粯璁ゆ寜鍨嬪彿璁¢噺鍗曚綅褰曞叆锛屽苟璁板綍鎹㈢畻姣旂巼 */
+  async function handleModelChange(
+    option: ItemModelSelectOption | undefined,
+    values: LineUnitFormValues,
+  ) {
+    const enterUnitMeasureId =
+      option?.unitMeasureId && option.unitMeasureId !== values.itemPrimaryUnitId
+        ? option.unitMeasureId
+        : undefined;
+    await formApi?.setValues({
+      enterUnitMeasureId,
+      enterUnitMeasureName: enterUnitMeasureId
+        ? option?.unitMeasureName
+        : undefined,
+      enterUnitRate: enterUnitMeasureId ? option?.unitMeasureRate : undefined,
+      enterQuantity: undefined,
+      // 鍒囧埌鍨嬪彿鍗曚綅褰曞叆鍚庡叆搴撴暟閲忕敱绯荤粺鎹㈢畻锛屽厛娓呮帀涓婁竴娆″彛寰勭殑鏁板��
+      ...(enterUnitMeasureId ? { receivedQuantity: undefined } : {}),
+    });
+  }
+
+  /** 鍒囨崲褰曞叆鍗曚綅锛氳褰曟崲绠楁瘮鐜囧苟绔嬪嵆閲嶇畻鍏ュ簱鏁伴噺锛涙敼鍥炰富鍗曚綅鏃舵竻鎺夊綍鍏ユ暟閲� */
+  async function handleEnterUnitChange(
+    value: number | undefined,
+    option: { label?: string; rate?: number } | undefined,
+    values: LineUnitFormValues,
+  ) {
+    if (!value || value === values.itemPrimaryUnitId) {
+      await formApi?.setValues({
+        enterUnitMeasureName: undefined,
+        enterUnitRate: undefined,
+        enterQuantity: undefined,
+      });
+      return;
+    }
+    await formApi?.setValues({
+      enterUnitMeasureName: option?.label,
+      enterUnitRate: option?.rate,
+      ...(values.enterQuantity == null || !isValidRate(option?.rate)
+        ? { receivedQuantity: undefined }
+        : {
+            receivedQuantity: roundQuantity(values.enterQuantity * option.rate),
+          }),
+    });
+  }
+
+  /** 褰曞叆鏁伴噺鍙樺寲锛氭寜褰曞叆鍗曚綅涓庢崲绠楁瘮鐜囪瘯绠楀叆搴撴暟閲忥紝渚涚敤鎴锋牳瀵规崲绠楃粨鏋� */
+  async function handleEnterQuantityChange(
+    value: number | undefined,
+    values: LineUnitFormValues,
+  ) {
+    if (isPrimaryUnitEnter(values) || !isValidRate(values.enterUnitRate)) {
+      return;
+    }
+    await formApi?.setFieldValue(
+      'receivedQuantity',
+      value == null ? undefined : roundQuantity(value * values.enterUnitRate),
+    );
+  }
+
   return [
     {
       fieldName: 'noticeId',
@@ -331,14 +561,17 @@
       label: '鍒拌揣閫氱煡鍗曡',
       component: markRaw(WmArrivalNoticeLineSelect),
       componentProps: {
-        // 閫夋嫨鍒拌揣閫氱煡鍗曡鍚庯紝鑷姩鍥炲~鐗╂枡鍜屽叆搴撴暟閲�
+        // 閫夋嫨鍒拌揣閫氱煡鍗曡鍚庯紝鑷姩鍥炲~鐗╂枡涓庡叆搴撴暟閲�
         onChange: async (
           line?: MesWmArrivalNoticeLineApi.ArrivalNoticeLine,
         ) => {
-          await formApi?.setValues({
-            itemId: line?.itemId,
-            receivedQuantity: line?.arrivalQuantity,
-          });
+          await handleItemChange(line?.itemId);
+          if (line?.arrivalQuantity != null) {
+            await formApi?.setFieldValue(
+              'receivedQuantity',
+              line.arrivalQuantity,
+            );
+          }
         },
       },
       dependencies: {
@@ -355,6 +588,8 @@
       component: markRaw(MdItemSelect),
       componentProps: {
         placeholder: '璇烽�夋嫨鐗╂枡',
+        // 鍒囨崲鐗╂枡鍚庯紝鍨嬪彿銆佸綍鍏ュ崟浣嶄笌鏁伴噺閮借閲嶆柊閫�
+        onChange: (item?: MesMdItemApi.Item) => handleItemChange(item?.id),
       },
       rules: 'selectRequired',
       dependencies: {
@@ -365,6 +600,121 @@
         }),
       },
     },
+    // 鐗╂枡涓诲崟浣嶉殢鎵�閫夌墿鏂欏彉鍖栵紝浣嗚〃鍗� props 鍙兘闈欐�佸0鏄庯紝鏁呯敤闅愯棌瀛楁鎵胯浇
+    {
+      fieldName: 'itemPrimaryUnitId',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'itemPrimaryUnitName',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'itemModelId',
+      label: '鐗╂枡鍨嬪彿',
+      component: 'ApiSelect',
+      componentProps: {
+        labelField: 'model',
+        valueField: 'id',
+        allowClear: true,
+        api: (params: { itemId?: number }) =>
+          getItemModelListByItem(params.itemId!),
+        shouldFetch: (params: { itemId?: number }) => !!params?.itemId,
+        placeholder: '璇峰厛閫夋嫨鐗╂枡',
+      },
+      dependencies: {
+        triggerFields: ['itemId'],
+        componentProps: (values) => ({
+          disabled: !values.itemId,
+          params: { itemId: values.itemId },
+          placeholder: values.itemId ? '璇烽�夋嫨鐗╂枡鍨嬪彿' : '璇峰厛閫夋嫨鐗╂枡',
+          // 閫夊畾鍨嬪彿鍚庨粯璁ゆ寜鍏惰閲忓崟浣嶅綍鍏�
+          onChange: (option?: ItemModelSelectOption) =>
+            handleModelChange(option, values),
+        }),
+      },
+    },
+    {
+      fieldName: 'enterUnitMeasureId',
+      label: '褰曞叆鍗曚綅',
+      component: 'ApiSelect',
+      componentProps: {
+        allowClear: true,
+        labelField: 'name',
+        valueField: 'id',
+        api: (params: { itemId?: number; itemModelId?: number }) =>
+          getItemEnterUnits(params),
+        shouldFetch: (params: { itemId?: number }) => !!params?.itemId,
+        placeholder: '榛樿涓虹墿鏂欎富璁¢噺鍗曚綅',
+      },
+      dependencies: {
+        triggerFields: ['itemId', 'itemModelId', 'itemPrimaryUnitName'],
+        componentProps: (values) => ({
+          disabled: !values.itemId,
+          params: { itemId: values.itemId, itemModelId: values.itemModelId },
+          placeholder: values.itemId
+            ? `榛樿鎸夌墿鏂欎富鍗曚綅${values.itemPrimaryUnitName ? `銆�${values.itemPrimaryUnitName}銆慲 : ''}褰曞叆`
+            : '璇峰厛閫夋嫨鐗╂枡',
+          onChange: (
+            value: number | undefined,
+            option?: { label?: string; rate?: number },
+          ) => handleEnterUnitChange(value, option, values),
+        }),
+      },
+    },
+    // 褰曞叆鍗曚綅鍚嶇О涓庢崲绠楁瘮鐜囬殢鎵�閫夊崟浣嶅彉鍖栵紝鍚屾牱鐢ㄩ殣钘忓瓧娈垫壙杞�
+    {
+      fieldName: 'enterUnitMeasureName',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'enterUnitRate',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'enterQuantity',
+      label: '褰曞叆鏁伴噺',
+      component: 'InputNumber',
+      componentProps: {
+        class: '!w-full',
+        min: 0,
+        precision: 6,
+      },
+      dependencies: {
+        triggerFields: [
+          'enterUnitMeasureId',
+          'enterUnitMeasureName',
+          'itemPrimaryUnitId',
+        ],
+        show: (values) => !isPrimaryUnitEnter(values),
+        // 杩斿洖 null 琛ㄧず涓嶈拷鍔犺鍒欙紙鍥為��鍩虹 rules锛�
+        rules: (values) => (isPrimaryUnitEnter(values) ? null : 'required'),
+        componentProps: (values) => ({
+          placeholder: values.enterUnitMeasureName
+            ? `璇峰~鍐欐寜銆�${values.enterUnitMeasureName}銆戣鐨勬暟閲廯
+            : '璇锋寜褰曞叆鍗曚綅濉啓鏁伴噺',
+          // 褰曞叆鏁伴噺鍙樺寲鏃剁珛鍗宠瘯绠楀叆搴撴暟閲�
+          onChange: (value: number | undefined) =>
+            handleEnterQuantityChange(value, values),
+        }),
+      },
+    },
     {
       fieldName: 'receivedQuantity',
       label: '鍏ュ簱鏁伴噺',
@@ -372,10 +722,26 @@
       componentProps: {
         class: '!w-full',
         min: 0,
-        placeholder: '璇疯緭鍏ュ叆搴撴暟閲�',
-        precision: 2,
+        precision: 6,
       },
-      rules: 'required',
+      dependencies: {
+        triggerFields: [
+          'enterUnitMeasureId',
+          'itemPrimaryUnitId',
+          'itemPrimaryUnitName',
+        ],
+        // 鎸変富鍗曚綅褰曞叆鏃舵墜宸ュ~鍐欍�佸繀濉紱鎸夎緟鍗曚綅褰曞叆鏃朵负绯荤粺鎹㈢畻缁撴灉锛屾棤闇�鏍¢獙
+        rules: (values) => (isPrimaryUnitEnter(values) ? 'required' : null),
+        componentProps: (values) => {
+          const primary = isPrimaryUnitEnter(values);
+          return {
+            disabled: !primary,
+            placeholder: primary
+              ? `璇锋寜鐗╂枡涓诲崟浣�${values.itemPrimaryUnitName ? `銆�${values.itemPrimaryUnitName}銆慲 : ''}濉啓鏁伴噺`
+              : '鎸夊綍鍏ュ崟浣嶄笌鎹㈢畻姣旂巼鑷姩鎹㈢畻',
+          };
+        },
+      },
     },
     {
       fieldName: 'productionDate',

--
Gitblit v1.9.3