From 04e8e3a312ed1f2d9dc40354ae5847ef9cc11360 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期六, 29 八月 2026 16:22:35 +0800
Subject: [PATCH] fix: 新疆-绿能 1.报工等待时长倒计时 + 报工数量可选 2.报工自动生成产品入库单 + 勾选入库 + 不合格仓库兜底
---
src/views/wls/productreceipt/data.ts | 25 ++
src/views/mes/pro/process/data.ts | 23 ++
src/api/mes/pro/route/process/index.ts | 2
src/api/mes/wm/productreceipt/index.ts | 32 ++
src/api/mes/pro/task/index.ts | 8
src/views/mes/process-design/process/modules/form.vue | 7
src/views/wls/productreceipt/modules/form.vue | 6
src/views/mes/pro/feedback/modules/form.vue | 73 +++++-
src/views/mes/process-design/process/data.ts | 23 ++
src/views/wls/productreceipt/index.vue | 5
src/api/mes/pro/process/index.ts | 2
src/views/mes/workbench/components/tabs/FeedbackTab.vue | 65 +++++
src/views/mes/pro/feedback/data.ts | 39 +++
src/views/mes/pro/workorder-center/modules/quick-feedback.vue | 50 ++++
src/views/wls/productreceipt/modules/line-list.vue | 88 +++++++
src/api/mes/wm/productreceipt/line/index.ts | 2
src/views/mes/pro/feedback/param-state.ts | 14 +
src/views/mes/pro/process/modules/form.vue | 7
src/views/mes/process-design/route/data.ts | 12
src/views/mes/process-design/route/modules/process-form.vue | 17 +
src/views/mes/pro/feedback/modules/wait-countdown.vue | 137 ++++++++++++
21 files changed, 594 insertions(+), 43 deletions(-)
diff --git a/src/api/mes/pro/process/index.ts b/src/api/mes/pro/process/index.ts
index a4ff1ea..a8d922f 100644
--- a/src/api/mes/pro/process/index.ts
+++ b/src/api/mes/pro/process/index.ts
@@ -12,6 +12,8 @@
status?: number;
remark?: string;
createTime?: Date;
+ waitDuration?: number; // 绛夊緟鏃堕暱锛堝皬鏃讹級锛屾姤宸ュ墠闇�鍊掕鏃剁瓑寰�
+ needReportQuantity?: boolean; // 鏄惁闇�瑕佸~鍐欐姤宸ユ暟閲�
}
}
diff --git a/src/api/mes/pro/route/process/index.ts b/src/api/mes/pro/route/process/index.ts
index a08056f..ba1e191 100644
--- a/src/api/mes/pro/route/process/index.ts
+++ b/src/api/mes/pro/route/process/index.ts
@@ -36,6 +36,8 @@
remark?: string;
bomItems?: ProcessBomItem[];
createTime?: Date;
+ waitDuration?: number; // 绛夊緟鏃堕暱锛堝皬鏃讹級锛�>0 琛ㄧず鎶ュ伐鍓嶉渶鍊掕鏃剁瓑寰�
+ needReportQuantity?: boolean; // 鏄惁闇�瑕佸~鍐欐姤宸ユ暟閲忥紝false 琛ㄧず鎶ュ伐鏁伴噺鍏佽涓虹┖
}
}
diff --git a/src/api/mes/pro/task/index.ts b/src/api/mes/pro/task/index.ts
index 13fb333..b2edd00 100644
--- a/src/api/mes/pro/task/index.ts
+++ b/src/api/mes/pro/task/index.ts
@@ -59,6 +59,7 @@
reportableQuantity?: number; // 鍙姤宸ユ暟閲�
assigneeUserIds?: number[]; // 鎶ュ伐浜�
feedbackApprove?: boolean; // 鏄惁鎶ュ伐瀹℃壒锛堟淳鐢熻嚜宸ュ崟锛�
+ waitStartTime?: string; // 鎶ュ伐鍊掕鏃跺紑濮嬫椂闂达紙yyyy-MM-dd HH:mm:ss锛�
}
/** MES 鐢熶骇浠诲姟鍒嗛〉鏌ヨ鍙傛暟 */
@@ -209,3 +210,10 @@
export function resumeTask(data: { id: number }) {
return requestClient.put('/mes/pro/task/resume', data);
}
+
+/** 寮�濮嬫姤宸ュ�掕鏃讹紝杩斿洖鍊掕鏃跺紑濮嬫椂闂达紙yyyy-MM-dd HH:mm:ss锛� */
+export function startWaitCountdown(taskId: number) {
+ return requestClient.post<string>('/mes/pro/task/start-wait-countdown', {
+ taskId,
+ });
+}
diff --git a/src/api/mes/wm/productreceipt/index.ts b/src/api/mes/wm/productreceipt/index.ts
index 7a49b56..090f57f 100644
--- a/src/api/mes/wm/productreceipt/index.ts
+++ b/src/api/mes/wm/productreceipt/index.ts
@@ -18,7 +18,29 @@
receiptDate?: number; // 鍏ュ簱鏃ユ湡
status?: number; // 鍗曟嵁鐘舵��
remark?: string; // 澶囨敞
+ feedbackId?: number; // 鏉ユ簮鎶ュ伐缂栧彿锛堥潪绌鸿〃绀虹敱鎶ュ伐鑷姩鐢熸垚锛�
createTime?: Date; // 鍒涘缓鏃堕棿
+ }
+
+ /** 涓婃灦鍒嗛厤鏄庣粏 */
+ export interface StockDetail {
+ warehouseId: number; // 浠撳簱缂栧彿
+ locationId: number; // 搴撳尯缂栧彿
+ areaId: number; // 搴撲綅缂栧彿
+ quantity: number; // 鍒嗛厤鏁伴噺
+ }
+
+ /** 涓婃灦琛� */
+ export interface StockRow {
+ lineId: number; // 鍏ュ簱鍗曡缂栧彿
+ isStock: boolean; // 鏄惁鍏ュ簱
+ details?: StockDetail[]; // isStock=true 鏃剁殑浠撳簱/搴撳尯/搴撲綅鍒嗛厤
+ }
+
+ /** 鎵归噺涓婃灦璇锋眰 */
+ export interface StockRequest {
+ receiptId: number; // 浜у搧鍏ュ簱鍗曠紪鍙�
+ rows: StockRow[]; // 琛屼笂鏋跺垪琛�
}
}
@@ -83,6 +105,16 @@
);
}
+/** 鎵归噺涓婃灦锛堝嬀閫夋槸鍚﹀叆搴� + 鍒嗛厤浠撳簱/搴撳尯/搴撲綅鏁伴噺锛� */
+export function submitProductReceiptStock(
+ data: MesWmProductReceiptApi.StockRequest,
+) {
+ return requestClient.post<boolean>(
+ '/mes/wm/product-receipt/submit-stock',
+ data,
+ );
+}
+
/** 瀵煎嚭浜у搧鍏ュ簱鍗� */
export function exportProductReceipt(params: any) {
return requestClient.download('/mes/wm/product-receipt/export-excel', {
diff --git a/src/api/mes/wm/productreceipt/line/index.ts b/src/api/mes/wm/productreceipt/line/index.ts
index 04e113b..973954f 100644
--- a/src/api/mes/wm/productreceipt/line/index.ts
+++ b/src/api/mes/wm/productreceipt/line/index.ts
@@ -16,6 +16,8 @@
quantity?: number; // 鍏ュ簱鏁伴噺
batchId?: number; // 鎵规缂栧彿
batchCode?: string; // 鎵规鍙�
+ qualityStatus?: number; // 璐ㄩ噺鐘舵�侊紙瀛楀吀 MES_WM_QUALITY_STATUS 鍚堟牸/涓嶅悎鏍硷級
+ isStock?: boolean; // 鏄惁鍏ュ簱锛坒alse/绌�=鎵ц鍏ュ簱鏃堕粯璁よ繘涓嶅悎鏍间粨搴擄級
remark?: string; // 澶囨敞
}
}
diff --git a/src/views/mes/pro/feedback/data.ts b/src/views/mes/pro/feedback/data.ts
index 32056e7..fbe99f0 100644
--- a/src/views/mes/pro/feedback/data.ts
+++ b/src/views/mes/pro/feedback/data.ts
@@ -19,7 +19,13 @@
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process';
import { getSimpleUserList } from '#/api/system/user';
-import { loadParamDetails } from './param-state';
+import {
+ loadParamDetails,
+ resetWaitConfig,
+ waitDuration,
+ waitStartTime,
+ waitTaskId,
+} from './param-state';
import { getRangePickerDefaultProps } from '#/utils';
import { MdItemSelect } from '#/views/mes/md/item/components';
import { MdWorkstationSelect } from '#/views/mes/md/workstation/components';
@@ -195,6 +201,12 @@
defaultValue: true,
},
{
+ fieldName: 'needReportQuantity',
+ component: 'Input',
+ dependencies: { triggerFields: [''], show: () => false },
+ defaultValue: true,
+ },
+ {
fieldName: 'routeId',
component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
@@ -255,8 +267,10 @@
status: MesProWorkOrderStatusEnum.CONFIRMED,
// 璁㈠崟鍙樻洿锛氭竻绌轰换鍔″強浠诲姟甯﹀嚭鐨勪骇鍝佷俊鎭�佹暟閲忓尯鍩熸帶鍒朵綅
onChange: async () => {
+ resetWaitConfig();
await formApi?.setValues({
checkFlag: true,
+ needReportQuantity: true,
itemCode: undefined,
itemId: undefined,
itemName: undefined,
@@ -291,6 +305,8 @@
onChange: async (task?: MesProTaskApi.Task) => {
await loadParamDetails(task?.id);
if (!task) {
+ resetWaitConfig();
+ await formApi?.setFieldValue('needReportQuantity', true);
return;
}
await formApi?.setValues({
@@ -304,7 +320,9 @@
workstationId: task.workstationId,
reportableQuantity: task.reportableQuantity,
});
- // 宸ヨ壓璺嚎宸ュ簭鐨� checkFlag 鍐冲畾鏁伴噺鍖哄煙灞曠ず
+ waitTaskId.value = task.id;
+ waitStartTime.value = task.waitStartTime;
+ // 宸ヨ壓璺嚎宸ュ簭鐨� checkFlag / 绛夊緟鏃堕暱 / 鎶ュ伐鏁伴噺瑙勫垯鍐冲畾灞曠ず
if (task.routeId && task.processId) {
try {
const routeProcess = await getRouteProcessByRouteAndProcess(
@@ -315,9 +333,19 @@
'checkFlag',
routeProcess?.checkFlag ?? false,
);
+ await formApi?.setFieldValue(
+ 'needReportQuantity',
+ routeProcess?.needReportQuantity ?? true,
+ );
+ waitDuration.value = routeProcess?.waitDuration;
} catch {
await formApi?.setFieldValue('checkFlag', true);
+ await formApi?.setFieldValue('needReportQuantity', true);
+ waitDuration.value = undefined;
}
+ } else {
+ waitDuration.value = undefined;
+ await formApi?.setFieldValue('needReportQuantity', true);
}
},
},
@@ -405,7 +433,12 @@
precision: 2,
placeholder: '璇疯緭鍏ユ姤宸ユ暟閲�',
},
- rules: 'required',
+ dependencies: {
+ triggerFields: ['needReportQuantity'],
+ show: (values) => values.needReportQuantity !== false,
+ rules: (values) =>
+ values.needReportQuantity === false ? null : 'required',
+ },
},
{
fieldName: 'feedbackUserId',
diff --git a/src/views/mes/pro/feedback/modules/form.vue b/src/views/mes/pro/feedback/modules/form.vue
index 9e5a9a3..baf93c8 100644
--- a/src/views/mes/pro/feedback/modules/form.vue
+++ b/src/views/mes/pro/feedback/modules/form.vue
@@ -25,14 +25,24 @@
updateFeedback,
} from '#/api/mes/pro/feedback';
import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process';
+import { getTask } from '#/api/mes/pro/task';
import { getWorkOrder } from '#/api/mes/pro/workorder';
import { $t } from '#/locales';
import { useFormSchema } from '../data';
-import { findMissingRequiredParams, loadParamDetails, paramDetails } from '../param-state';
+import {
+ findMissingRequiredParams,
+ loadParamDetails,
+ paramDetails,
+ resetWaitConfig,
+ waitDuration,
+ waitStartTime,
+ waitTaskId,
+} from '../param-state';
import BatchInputDialog from './batch-input-dialog.vue';
import ItemConsumeList from './item-consume-list.vue';
import ProductProduceList from './product-produce-list.vue';
+import WaitCountdown from './wait-countdown.vue';
const emit = defineEmits(['success']);
const formType = ref<FormType>('create');
@@ -88,7 +98,13 @@
});
/** 鎻愪氦鍓嶅榻愭暟閲忥細鎶ュ伐鍙~鍐欐姤宸ユ暟閲忥紝鍚堟牸/涓嶈壇/搴熷搧鏁伴噺鐢辫川妫�纭畾锛屾姤宸ョ幆鑺備笉閲囬泦 */
-function alignQuantity(data: MesProFeedbackApi.Feedback) {
+function alignQuantity(
+ data: MesProFeedbackApi.Feedback,
+ needReportQuantity: boolean,
+) {
+ if (!needReportQuantity) {
+ data.feedbackQuantity = 0;
+ }
data.uncheckQuantity = data.checkFlag ? data.feedbackQuantity : 0;
delete data.qualifiedQuantity;
delete data.unqualifiedQuantity;
@@ -99,9 +115,12 @@
/** 璇诲彇骞舵寜鎻愪氦瑙勫垯瀵归綈鍚庣殑琛ㄥ崟鏁版嵁锛岀敤浜庡揩鐓у拰淇濆瓨 */
async function getAlignedData(): Promise<MesProFeedbackApi.Feedback> {
- const data = (await formApi.getValues()) as MesProFeedbackApi.Feedback;
+ const data = (await formApi.getValues()) as MesProFeedbackApi.Feedback & {
+ needReportQuantity?: boolean;
+ };
syncParamValues(data);
- alignQuantity(data);
+ alignQuantity(data, data.needReportQuantity !== false);
+ delete data.needReportQuantity;
return data;
}
@@ -292,19 +311,24 @@
}
}
-/** 鍔犺浇宸ュ簭鐨� checkFlag 鐢ㄤ簬鍥炴樉鏁伴噺鍖哄煙 */
-async function resolveCheckFlag(routeId?: number, processId?: number) {
+/** 鍔犺浇宸ュ簭閰嶇疆锛坈heckFlag / 绛夊緟鏃堕暱 / 鎶ュ伐鏁伴噺瑙勫垯锛夌敤浜庡洖鏄� */
+async function resolveRouteProcessConfig(routeId?: number, processId?: number) {
if (!routeId || !processId) {
- return true;
+ return { checkFlag: true, needReportQuantity: true };
}
try {
const routeProcess = await getRouteProcessByRouteAndProcess(
routeId,
processId,
);
- return routeProcess?.checkFlag ?? false;
+ waitDuration.value = routeProcess?.waitDuration;
+ return {
+ checkFlag: routeProcess?.checkFlag ?? false,
+ needReportQuantity: routeProcess?.needReportQuantity ?? true,
+ };
} catch {
- return true;
+ waitDuration.value = undefined;
+ return { checkFlag: true, needReportQuantity: true };
}
}
@@ -322,6 +346,7 @@
subTabsName.value = 'itemConsume';
originalSnapshot.value = '';
paramDetails.value = [];
+ resetWaitConfig();
return;
}
// 鍔犺浇鏁版嵁
@@ -357,12 +382,26 @@
try {
formData.value = await getFeedback(data.id);
await loadParamDetails(formData.value.taskId);
- const checkFlag = await resolveCheckFlag(
+ const config = await resolveRouteProcessConfig(
formData.value.routeId,
formData.value.processId,
);
+ waitTaskId.value = formData.value.taskId;
+ waitStartTime.value = undefined;
+ if (formData.value.taskId) {
+ try {
+ const task = await getTask(formData.value.taskId);
+ waitStartTime.value = task?.waitStartTime;
+ } catch {
+ // 浠诲姟涓嶅瓨鍦ㄦ椂蹇界暐鍊掕鏃跺洖鏄�
+ }
+ }
// 璁剧疆鍒� values
- await formApi.setValues({ ...formData.value, checkFlag });
+ await formApi.setValues({
+ ...formData.value,
+ checkFlag: config.checkFlag,
+ needReportQuantity: config.needReportQuantity,
+ });
// 璁板綍鍒濆蹇収锛堝惈鏁伴噺瀵归綈缁撴灉锛夛紝submit 鏃剁敤浜庤烦杩囨棤鍙樻洿鐨勪繚瀛樿姹�
originalSnapshot.value = JSON.stringify(await getAlignedData());
} finally {
@@ -375,6 +414,18 @@
<template>
<Modal :title="getTitle" class="w-3/5">
<Form class="mx-4" />
+ <div
+ v-if="waitDuration"
+ class="mx-4 mt-4 rounded border border-gray-200 p-4"
+ >
+ <div class="mb-2 font-medium">鎶ュ伐鍊掕鏃�</div>
+ <WaitCountdown
+ :task-id="waitTaskId"
+ :wait-duration="waitDuration"
+ :wait-start-time="waitStartTime"
+ :disabled="!isEditable"
+ />
+ </div>
<div v-if="paramDetails.length" class="mx-4 mt-4 rounded border border-gray-200 p-4">
<div class="mb-3 font-medium">宸ヨ壓鍙傛暟</div>
<div class="grid grid-cols-2 gap-3">
diff --git a/src/views/mes/pro/feedback/modules/wait-countdown.vue b/src/views/mes/pro/feedback/modules/wait-countdown.vue
new file mode 100644
index 0000000..760b8de
--- /dev/null
+++ b/src/views/mes/pro/feedback/modules/wait-countdown.vue
@@ -0,0 +1,137 @@
+<script lang="ts" setup>
+import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
+
+import dayjs from 'dayjs';
+
+import { Button, message } from 'ant-design-vue';
+
+import { startWaitCountdown } from '#/api/mes/pro/task';
+
+defineOptions({ name: 'WaitCountdown' });
+
+interface Props {
+ taskId?: number;
+ /** 绛夊緟鏃堕暱锛堝皬鏃讹級锛�>0 鎵嶅睍绀哄�掕鏃� */
+ waitDuration?: number;
+ /** 鍊掕鏃跺紑濮嬫椂闂达紙yyyy-MM-dd HH:mm:ss锛� */
+ waitStartTime?: string;
+ /** 鍙妯″紡锛堣鎯�/瀹℃壒锛変笅涓嶅睍绀哄紑濮嬫寜閽� */
+ disabled?: boolean;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+ taskId: undefined,
+ waitDuration: undefined,
+ waitStartTime: undefined,
+ disabled: false,
+});
+
+const starting = ref(false);
+const startTime = ref<string | undefined>(props.waitStartTime);
+const now = ref(Date.now());
+let timer: ReturnType<typeof setInterval> | undefined;
+
+const showCountdown = computed(
+ () => !!props.waitDuration && props.waitDuration > 0,
+);
+
+const endTime = computed(() => {
+ if (!startTime.value || !showCountdown.value) {
+ return undefined;
+ }
+ return dayjs(startTime.value, 'YYYY-MM-DD HH:mm:ss').valueOf() +
+ props.waitDuration! * 3600 * 1000;
+});
+
+const remainingMs = computed(() => {
+ if (!endTime.value) {
+ return 0;
+ }
+ return Math.max(0, endTime.value - now.value);
+});
+
+const counting = computed(() => remainingMs.value > 0);
+
+const remainingText = computed(() => {
+ const totalSeconds = Math.ceil(remainingMs.value / 1000);
+ const hours = Math.floor(totalSeconds / 3600);
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
+ const seconds = totalSeconds % 60;
+ const pad = (n: number) => String(n).padStart(2, '0');
+ if (hours > 0) {
+ return `鍓╀綑 ${hours} 灏忔椂 ${pad(minutes)} 鍒� ${pad(seconds)} 绉抈;
+ }
+ return `鍓╀綑 ${pad(minutes)} 鍒� ${pad(seconds)} 绉抈;
+});
+
+function startTimer() {
+ if (timer) {
+ clearInterval(timer);
+ }
+ timer = setInterval(() => {
+ now.value = Date.now();
+ if (remainingMs.value <= 0) {
+ clearInterval(timer);
+ timer = undefined;
+ }
+ }, 1000);
+}
+
+watch(
+ () => props.waitStartTime,
+ (value) => {
+ startTime.value = value;
+ if (value) {
+ now.value = Date.now();
+ startTimer();
+ }
+ },
+);
+
+onMounted(() => {
+ if (startTime.value) {
+ startTimer();
+ }
+});
+
+onBeforeUnmount(() => {
+ if (timer) {
+ clearInterval(timer);
+ timer = undefined;
+ }
+});
+
+async function handleStart() {
+ if (!props.taskId) {
+ message.warning('璇峰厛閫夋嫨浠诲姟');
+ return;
+ }
+ starting.value = true;
+ try {
+ const time = await startWaitCountdown(props.taskId);
+ startTime.value = time;
+ now.value = Date.now();
+ startTimer();
+ message.success('鍊掕鏃跺凡寮�濮�');
+ } finally {
+ starting.value = false;
+ }
+}
+</script>
+
+<template>
+ <div v-if="showCountdown" class="flex items-center gap-2">
+ <Button
+ v-if="!startTime && !disabled"
+ type="primary"
+ :loading="starting"
+ @click="handleStart"
+ >
+ 寮�濮嬪�掕鏃�
+ </Button>
+ <span v-if="startTime" class="text-sm">
+ <span v-if="counting" class="text-orange-500">{{ remainingText }}</span>
+ <span v-else class="text-green-600">鍊掕鏃跺凡缁撴潫锛屽彲鎶ュ伐</span>
+ </span>
+ </div>
+</template>
diff --git a/src/views/mes/pro/feedback/param-state.ts b/src/views/mes/pro/feedback/param-state.ts
index 2b3599a..ebd9559 100644
--- a/src/views/mes/pro/feedback/param-state.ts
+++ b/src/views/mes/pro/feedback/param-state.ts
@@ -45,3 +45,17 @@
.filter((detail) => detail.required && !detail.paramValue?.trim())
.map((detail) => detail.paramName || detail.paramCode || '');
}
+
+/** 褰撳墠鎶ュ伐浠诲姟缂栧彿锛堢敤浜庡�掕鏃舵帴鍙o級 */
+export const waitTaskId = ref<number>();
+/** 褰撳墠浠诲姟宸ュ簭鐨勭瓑寰呮椂闀匡紙灏忔椂锛夛紝>0 琛ㄧず鎶ュ伐鍓嶉渶鍊掕鏃� */
+export const waitDuration = ref<number>();
+/** 褰撳墠浠诲姟鎶ュ伐鍊掕鏃跺紑濮嬫椂闂达紙yyyy-MM-dd HH:mm:ss锛� */
+export const waitStartTime = ref<string>();
+
+/** 閲嶇疆鎶ュ伐鍊掕鏃剁浉鍏崇姸鎬� */
+export function resetWaitConfig() {
+ waitTaskId.value = undefined;
+ waitDuration.value = undefined;
+ waitStartTime.value = undefined;
+}
diff --git a/src/views/mes/pro/process/data.ts b/src/views/mes/pro/process/data.ts
index 1cf6134..a7b51ac 100644
--- a/src/views/mes/pro/process/data.ts
+++ b/src/views/mes/pro/process/data.ts
@@ -77,6 +77,29 @@
rules: z.number().default(CommonStatusEnum.ENABLE),
},
{
+ fieldName: 'waitDuration',
+ label: '绛夊緟鏃堕暱(灏忔椂)',
+ component: 'InputNumber',
+ formItemClass: 'col-span-2',
+ componentProps: {
+ class: '!w-full',
+ min: 0,
+ precision: 2,
+ placeholder: '鐣欑┖鎴� 0 琛ㄧず鏃犻渶鍊掕鏃�',
+ },
+ },
+ {
+ fieldName: 'needReportQuantity',
+ label: '闇�濉啓鎶ュ伐鏁伴噺',
+ component: 'Switch',
+ formItemClass: 'col-span-1',
+ defaultValue: true,
+ componentProps: {
+ checkedChildren: '鏄�',
+ unCheckedChildren: '鍚�',
+ },
+ },
+ {
fieldName: 'attention',
label: '宸ュ簭璇存槑',
component: 'Textarea',
diff --git a/src/views/mes/pro/process/modules/form.vue b/src/views/mes/pro/process/modules/form.vue
index 540c0be..643c235 100644
--- a/src/views/mes/pro/process/modules/form.vue
+++ b/src/views/mes/pro/process/modules/form.vue
@@ -88,8 +88,11 @@
modalApi.lock();
try {
formData.value = await getProcess(data.id);
- // 璁剧疆鍒� values
- await formApi.setValues(formData.value);
+ // 璁剧疆鍒� values锛堟棫鏁版嵁鏈厤缃姤宸ユ暟閲忓紑鍏虫椂鎸夐粯璁ら渶瑕佸~鍐欙級
+ await formApi.setValues({
+ ...formData.value,
+ needReportQuantity: formData.value.needReportQuantity ?? true,
+ });
} finally {
modalApi.unlock();
}
diff --git a/src/views/mes/pro/workorder-center/modules/quick-feedback.vue b/src/views/mes/pro/workorder-center/modules/quick-feedback.vue
index 9df47ce..fc91d88 100644
--- a/src/views/mes/pro/workorder-center/modules/quick-feedback.vue
+++ b/src/views/mes/pro/workorder-center/modules/quick-feedback.vue
@@ -9,10 +9,13 @@
import { message } from 'ant-design-vue';
import { quickCreateFeedback } from '#/api/mes/pro/feedback';
+import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process';
+import { getTask } from '#/api/mes/pro/task';
import {
loadParamDetails,
paramDetails,
} from '#/views/mes/pro/feedback/param-state';
+import WaitCountdown from '#/views/mes/pro/feedback/modules/wait-countdown.vue';
import ProTaskSelect from '#/views/mes/pro/task/components/select.vue';
defineOptions({ name: 'QuickFeedbackForm' });
@@ -41,9 +44,15 @@
feedbackQuantity: undefined,
remark: '',
};
- // 棰勮浠诲姟鏃跺姞杞藉叾宸ヨ壓鍙傛暟锛涘惁鍒欑瓑寰呴�夋嫨浠诲姟鍚庡啀鍔犺浇
+ waitDuration.value = undefined;
+ needReportQuantity.value = true;
+ waitStartTime.value = undefined;
+ // 棰勮浠诲姟鏃跺姞杞藉叾宸ヨ壓鍙傛暟涓庣瓑寰呴厤缃紱鍚﹀垯绛夊緟閫夋嫨浠诲姟鍚庡啀鍔犺浇
if (props.taskId) {
loadParamDetails(props.taskId);
+ getTask(props.taskId)
+ .then(applyTaskWaitConfig)
+ .catch(() => {});
} else {
paramDetails.value = [];
}
@@ -59,6 +68,9 @@
remark: '',
});
const submitting = ref(false);
+const waitDuration = ref<number>();
+const needReportQuantity = ref<boolean>(true);
+const waitStartTime = ref<string>();
const hasPredefinedTask = computed(() => !!props.taskId);
@@ -77,6 +89,27 @@
selectedTask.value = task;
selectedTaskId.value = task?.id;
loadParamDetails(task?.id);
+ applyTaskWaitConfig(task);
+}
+
+/** 鎸変换鍔″伐搴忓姞杞界瓑寰呮椂闀夸笌鎶ュ伐鏁伴噺瑙勫垯 */
+async function applyTaskWaitConfig(task?: MesProTaskApi.Task) {
+ waitDuration.value = undefined;
+ needReportQuantity.value = true;
+ waitStartTime.value = task?.waitStartTime;
+ if (!task?.routeId || !task?.processId) {
+ return;
+ }
+ try {
+ const routeProcess = await getRouteProcessByRouteAndProcess(
+ task.routeId,
+ task.processId,
+ );
+ waitDuration.value = routeProcess?.waitDuration;
+ needReportQuantity.value = routeProcess?.needReportQuantity ?? true;
+ } catch {
+ // 鍔犺浇澶辫触鎸夐粯璁ゅ鐞嗭紙鏃犻渶鍊掕鏃躲�佹暟閲忓繀濉級
+ }
}
async function handleSubmit() {
@@ -85,7 +118,7 @@
message.warning('璇烽�夋嫨浠诲姟');
return;
}
- if (!formData.value.feedbackQuantity) {
+ if (needReportQuantity.value && !formData.value.feedbackQuantity) {
message.warning('璇疯緭鍏ユ姤宸ユ暟閲�');
return;
}
@@ -93,7 +126,9 @@
try {
await quickCreateFeedback({
taskId,
- feedbackQuantity: formData.value.feedbackQuantity,
+ feedbackQuantity: needReportQuantity.value
+ ? (formData.value.feedbackQuantity ?? 0)
+ : 0,
paramValues: paramDetails.value.map((detail) => ({
detailId: detail.id,
value: detail.paramValue,
@@ -127,7 +162,14 @@
class="!w-full"
/>
</a-form-item>
- <a-form-item label="鏈鎶ュ伐鏁伴噺" required>
+ <a-form-item v-if="waitDuration" label="鎶ュ伐鍊掕鏃�">
+ <WaitCountdown
+ :task-id="selectedTaskId"
+ :wait-duration="waitDuration"
+ :wait-start-time="waitStartTime"
+ />
+ </a-form-item>
+ <a-form-item v-if="needReportQuantity" label="鏈鎶ュ伐鏁伴噺" required>
<a-input-number
v-model:value="formData.feedbackQuantity"
:min="0.01"
diff --git a/src/views/mes/process-design/process/data.ts b/src/views/mes/process-design/process/data.ts
index 1cf6134..a7b51ac 100644
--- a/src/views/mes/process-design/process/data.ts
+++ b/src/views/mes/process-design/process/data.ts
@@ -77,6 +77,29 @@
rules: z.number().default(CommonStatusEnum.ENABLE),
},
{
+ fieldName: 'waitDuration',
+ label: '绛夊緟鏃堕暱(灏忔椂)',
+ component: 'InputNumber',
+ formItemClass: 'col-span-2',
+ componentProps: {
+ class: '!w-full',
+ min: 0,
+ precision: 2,
+ placeholder: '鐣欑┖鎴� 0 琛ㄧず鏃犻渶鍊掕鏃�',
+ },
+ },
+ {
+ fieldName: 'needReportQuantity',
+ label: '闇�濉啓鎶ュ伐鏁伴噺',
+ component: 'Switch',
+ formItemClass: 'col-span-1',
+ defaultValue: true,
+ componentProps: {
+ checkedChildren: '鏄�',
+ unCheckedChildren: '鍚�',
+ },
+ },
+ {
fieldName: 'attention',
label: '宸ュ簭璇存槑',
component: 'Textarea',
diff --git a/src/views/mes/process-design/process/modules/form.vue b/src/views/mes/process-design/process/modules/form.vue
index 540c0be..643c235 100644
--- a/src/views/mes/process-design/process/modules/form.vue
+++ b/src/views/mes/process-design/process/modules/form.vue
@@ -88,8 +88,11 @@
modalApi.lock();
try {
formData.value = await getProcess(data.id);
- // 璁剧疆鍒� values
- await formApi.setValues(formData.value);
+ // 璁剧疆鍒� values锛堟棫鏁版嵁鏈厤缃姤宸ユ暟閲忓紑鍏虫椂鎸夐粯璁ら渶瑕佸~鍐欙級
+ await formApi.setValues({
+ ...formData.value,
+ needReportQuantity: formData.value.needReportQuantity ?? true,
+ });
} finally {
modalApi.unlock();
}
diff --git a/src/views/mes/process-design/route/data.ts b/src/views/mes/process-design/route/data.ts
index 7990b1e..5c310bb 100644
--- a/src/views/mes/process-design/route/data.ts
+++ b/src/views/mes/process-design/route/data.ts
@@ -166,7 +166,10 @@
}
/** 宸ヨ壓璺嚎宸ュ簭鏄庣粏琛ㄥ崟 */
-export function useRouteProcessFormSchema(processOptions: Array<{ label: string; value: number }>): VbenFormSchema[] {
+export function useRouteProcessFormSchema(
+ processOptions: Array<{ label: string; value: number }>,
+ onProcessChange?: (processId?: number) => void,
+): VbenFormSchema[] {
return [
{
fieldName: "id",
@@ -198,6 +201,7 @@
options: processOptions,
placeholder: "璇烽�夋嫨宸ュ簭",
showSearch: true,
+ onChange: onProcessChange,
},
rules: "selectRequired",
},
@@ -279,12 +283,12 @@
},
{
fieldName: "waitTime",
- label: "绛夊緟鏃堕棿(鍒�)",
+ label: "绛夊緟鏃堕暱(灏忔椂)",
component: "InputNumber",
componentProps: {
class: "!w-full",
min: 0,
- precision: 0,
+ precision: 2,
},
rules: z.number().default(0),
},
@@ -351,7 +355,7 @@
width: 160,
},
{ field: "prepareTime", title: "鍑嗗鏃堕棿(鍒�)", width: 110 },
- { field: "waitTime", title: "绛夊緟鏃堕棿(鍒�)", width: 110 },
+ { field: "waitTime", title: "绛夊緟鏃堕暱(灏忔椂)", width: 110 },
{
field: "colorCode",
title: "鐢樼壒鍥鹃鑹�",
diff --git a/src/views/mes/process-design/route/modules/process-form.vue b/src/views/mes/process-design/route/modules/process-form.vue
index 6dcab06..2006065 100644
--- a/src/views/mes/process-design/route/modules/process-form.vue
+++ b/src/views/mes/process-design/route/modules/process-form.vue
@@ -9,7 +9,7 @@
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
-import { getProcessSimpleList } from '#/api/mes/pro/process';
+import { getProcess, getProcessSimpleList } from '#/api/mes/pro/process';
import {
createRouteProcess,
getRouteProcess,
@@ -39,6 +39,19 @@
wrapperClass: 'grid-cols-2',
});
+/** 閫夋嫨宸ュ簭鍚庯紝鑷姩甯﹀嚭璇ュ伐搴忛厤缃殑绛夊緟鏃堕暱锛堢洿鎺ュ~灏忔椂鏁帮紝涓嶆崲绠楋級 */
+async function handleProcessChange(processId?: number) {
+ if (!processId) {
+ return;
+ }
+ try {
+ const process = await getProcess(processId);
+ await formApi.setFieldValue('waitTime', process.waitDuration ?? 0);
+ } catch {
+ // 宸ュ簭璇︽儏鑾峰彇澶辫触鏃跺拷鐣ュ弽鏄�
+ }
+}
+
/** 鍔犺浇宸ュ簭閫夐」鍚庡啀鐢熸垚 schema锛堥伩鍏嶄笅鎷夌┖閫夐」锛� */
async function loadSchema(): Promise<VbenFormSchema[]> {
const list = await getProcessSimpleList();
@@ -46,7 +59,7 @@
label: item.name ?? '',
value: item.id!,
}));
- return useRouteProcessFormSchema(options);
+ return useRouteProcessFormSchema(options, handleProcessChange);
}
const [Modal, modalApi] = useVbenModal({
diff --git a/src/views/mes/workbench/components/tabs/FeedbackTab.vue b/src/views/mes/workbench/components/tabs/FeedbackTab.vue
index 86b6e25..2166e3f 100644
--- a/src/views/mes/workbench/components/tabs/FeedbackTab.vue
+++ b/src/views/mes/workbench/components/tabs/FeedbackTab.vue
@@ -26,14 +26,20 @@
updateFeedback,
} from '#/api/mes/pro/feedback';
import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process';
+import { getTask } from '#/api/mes/pro/task';
import { $t } from '#/locales';
import { useFormSchema } from '../../../pro/feedback/data';
import {
loadParamDetails,
paramDetails,
+ resetWaitConfig,
+ waitDuration,
+ waitStartTime,
+ waitTaskId,
} from '../../../pro/feedback/param-state';
import BatchInputDialog from '../../../pro/feedback/modules/batch-input-dialog.vue';
+import WaitCountdown from '../../../pro/feedback/modules/wait-countdown.vue';
const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>();
const batchActionType = ref<'' | 'submit' | 'approve'>('');
const pendingSubmitMode = ref<'saveAndSubmit' | 'submit'>('submit');
@@ -79,12 +85,16 @@
/** 鎻愪氦鍓嶅榻愭暟閲忥細鎶ュ伐鍙~鍐欐姤宸ユ暟閲忥紝鍚堟牸/涓嶈壇/搴熷搧鏁伴噺鐢辫川妫�纭畾锛屾姤宸ョ幆鑺備笉閲囬泦 */
function alignQuantity(data: any) {
+ if (data.needReportQuantity === false) {
+ data.feedbackQuantity = 0;
+ }
data.uncheckQuantity = data.checkFlag ? data.feedbackQuantity : 0;
delete data.qualifiedQuantity;
delete data.unqualifiedQuantity;
delete data.laborScrapQuantity;
delete data.materialScrapQuantity;
delete data.otherScrapQuantity;
+ delete data.needReportQuantity;
}
async function getAlignedData() {
@@ -102,14 +112,21 @@
}));
}
-/** 鍔犺浇 checkFlag */
-async function resolveCheckFlag(routeId?: number, processId?: number) {
- if (!routeId || !processId) return true;
+/** 鍔犺浇宸ュ簭閰嶇疆锛坈heckFlag / 绛夊緟鏃堕暱 / 鎶ュ伐鏁伴噺瑙勫垯锛� */
+async function resolveRouteProcessConfig(routeId?: number, processId?: number) {
+ if (!routeId || !processId) {
+ return { checkFlag: true, needReportQuantity: true };
+ }
try {
const rp = await getRouteProcessByRouteAndProcess(routeId, processId);
- return rp?.checkFlag ?? false;
+ waitDuration.value = rp?.waitDuration;
+ return {
+ checkFlag: rp?.checkFlag ?? false,
+ needReportQuantity: rp?.needReportQuantity ?? true,
+ };
} catch {
- return true;
+ waitDuration.value = undefined;
+ return { checkFlag: true, needReportQuantity: true };
}
}
@@ -119,13 +136,17 @@
feedbackId.value = undefined;
feedbackStatus.value = undefined;
originalSnapshot.value = '';
+ resetWaitConfig();
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
const code = await generateAutoCode(MesAutoCodeRuleCode.PRO_FEEDBACK_CODE);
- const checkFlag = await resolveCheckFlag(task.routeId, task.processId);
+ const config = await resolveRouteProcessConfig(task.routeId, task.processId);
// 鍔犺浇浠诲姟缁戝畾鐨勫伐鑹哄弬鏁帮紙蹇呭~鍙傛暟鏍¢獙 + 鍙傛暟鍊煎洖鏄撅級
await loadParamDetails(task.id);
+
+ waitTaskId.value = task.id;
+ waitStartTime.value = task.waitStartTime;
await formApi.setValues({
code,
@@ -145,7 +166,8 @@
reportableQuantity: task.reportableQuantity,
scheduledQuantity: task.quantity,
feedbackApprove: task.feedbackApprove,
- checkFlag,
+ checkFlag: config.checkFlag,
+ needReportQuantity: config.needReportQuantity,
});
originalSnapshot.value = JSON.stringify(await getAlignedData());
}
@@ -337,9 +359,23 @@
feedbackStatus.value = data.status;
formType.value = 'update';
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
- const checkFlag = await resolveCheckFlag(data.routeId, data.processId);
+ const config = await resolveRouteProcessConfig(data.routeId, data.processId);
await loadParamDetails(data.taskId);
- await formApi.setValues({ ...data, checkFlag });
+ waitTaskId.value = data.taskId;
+ waitStartTime.value = undefined;
+ if (data.taskId) {
+ try {
+ const task = await getTask(data.taskId);
+ waitStartTime.value = task?.waitStartTime;
+ } catch {
+ // 浠诲姟涓嶅瓨鍦ㄦ椂蹇界暐鍊掕鏃跺洖鏄�
+ }
+ }
+ await formApi.setValues({
+ ...data,
+ checkFlag: config.checkFlag,
+ needReportQuantity: config.needReportQuantity,
+ });
originalSnapshot.value = JSON.stringify(await getAlignedData());
}
@@ -377,6 +413,17 @@
<Form class="feedback-tab__form" />
+ <!-- 鎶ュ伐鍊掕鏃讹紙宸ュ簭閰嶇疆绛夊緟鏃堕暱鏃跺睍绀猴級 -->
+ <div v-if="waitDuration" class="feedback-tab__params">
+ <div class="feedback-tab__params-title">鎶ュ伐鍊掕鏃�</div>
+ <WaitCountdown
+ :task-id="waitTaskId"
+ :wait-duration="waitDuration"
+ :wait-start-time="waitStartTime"
+ :disabled="!isEditable"
+ />
+ </div>
+
<!-- 宸ヨ壓鍙傛暟锛堜换鍔$粦瀹氬弬鏁版椂灞曠ず锛屽繀濉弬鏁版湭濉細琚悗绔嫤鎴級 -->
<div v-if="paramDetails.length" class="feedback-tab__params">
<div class="feedback-tab__params-title">宸ヨ壓鍙傛暟</div>
diff --git a/src/views/wls/productreceipt/data.ts b/src/views/wls/productreceipt/data.ts
index f249a84..c7c6db6 100644
--- a/src/views/wls/productreceipt/data.ts
+++ b/src/views/wls/productreceipt/data.ts
@@ -192,6 +192,12 @@
},
},
{
+ field: 'feedbackId',
+ title: '鏉ユ簮',
+ width: 100,
+ slots: { default: 'source' },
+ },
+ {
title: '鎿嶄綔',
width: 240,
fixed: 'right',
@@ -237,10 +243,29 @@
width: 100,
},
{
+ field: 'qualityStatus',
+ title: '璐ㄩ噺鐘舵��',
+ width: 100,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.MES_WM_QUALITY_STATUS },
+ },
+ },
+ {
field: 'batchCode',
title: '鎵规鍙�',
minWidth: 120,
},
+ ...(stockable
+ ? [
+ {
+ field: 'isStock',
+ title: '鏄惁鍏ュ簱',
+ width: 100,
+ slots: { default: 'isStock' },
+ } as const,
+ ]
+ : []),
...(editable || stockable
? [
{
diff --git a/src/views/wls/productreceipt/index.vue b/src/views/wls/productreceipt/index.vue
index f557590..76bdd54 100644
--- a/src/views/wls/productreceipt/index.vue
+++ b/src/views/wls/productreceipt/index.vue
@@ -6,7 +6,7 @@
import { MesWmProductReceiptStatusEnum } from '@vben/constants';
import { downloadFileFromBlobPart } from '@vben/utils';
-import { Button, message } from 'ant-design-vue';
+import { Button, message, Tag } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
@@ -143,6 +143,9 @@
{{ row.code }}
</Button>
</template>
+ <template #source="{ row }">
+ <Tag v-if="row.feedbackId" color="blue">鑷姩鐢熸垚</Tag>
+ </template>
<template #actions="{ row }">
<TableAction
:actions="[
diff --git a/src/views/wls/productreceipt/modules/form.vue b/src/views/wls/productreceipt/modules/form.vue
index d9fa648..2b3d5a8 100644
--- a/src/views/wls/productreceipt/modules/form.vue
+++ b/src/views/wls/productreceipt/modules/form.vue
@@ -219,7 +219,11 @@
<template v-if="formData?.id">
<Divider>鐗╂枡淇℃伅</Divider>
<div class="mx-4">
- <LineList :form-type="formType" :receipt-id="formData.id" />
+ <LineList
+ :form-type="formType"
+ :receipt-id="formData.id"
+ :auto-generated="!!formData.feedbackId"
+ />
</div>
</template>
<template #prepend-footer>
diff --git a/src/views/wls/productreceipt/modules/line-list.vue b/src/views/wls/productreceipt/modules/line-list.vue
index 26442fb..e28cdac 100644
--- a/src/views/wls/productreceipt/modules/line-list.vue
+++ b/src/views/wls/productreceipt/modules/line-list.vue
@@ -2,6 +2,7 @@
import type { FormType } from '../data';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesWmProductReceiptApi } from '#/api/mes/wm/productreceipt';
import type { MesWmProductReceiptDetailApi } from '#/api/mes/wm/productreceipt/detail';
import type { MesWmProductReceiptLineApi } from '#/api/mes/wm/productreceipt/line';
@@ -10,9 +11,10 @@
import { useVbenModal } from '@vben/common-ui';
import { BarcodeBizTypeEnum } from '@vben/constants';
-import { message } from 'ant-design-vue';
+import { Button, Checkbox, message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import { submitProductReceiptStock } from '#/api/mes/wm/productreceipt';
import { getProductReceiptDetailListByLineId } from '#/api/mes/wm/productreceipt/detail';
import {
deleteProductReceiptLine,
@@ -29,16 +31,22 @@
const props = defineProps<{
formType: FormType;
receiptId: number;
+ autoGenerated?: boolean; // 鏄惁鐢辨姤宸ヨ嚜鍔ㄧ敓鎴愶紙鑷姩鐢熸垚鐨勮涓嶅彲鎵嬪伐澧炲垹鏀癸級
}>();
const isEditable = computed(() =>
// 鏄惁鍙紪杈戞槑缁嗚
['create', 'update'].includes(props.formType),
);
+const canEditLine = computed(
+ // 鑷姩鐢熸垚鐨勫叆搴撳崟琛屼笉鍙墜宸ュ鍒犳敼
+ () => isEditable.value && !props.autoGenerated,
+);
const isStock = computed(() => props.formType === 'stock'); // 鏄惁涓轰笂鏋舵ā寮�
const detailMap = reactive<
Record<number, MesWmProductReceiptDetailApi.ProductReceiptDetail[]>
>({}); // 宸插睍寮�琛岀殑涓婃灦鏄庣粏缂撳瓨
+const checkedMap = reactive<Record<number, boolean>>({}); // 琛屾槸鍚﹀叆搴撳嬀閫夌姸鎬佺紦瀛�
const barcodeDetailRef = ref(); // 鏉$爜璇︽儏寮圭獥瀹炰緥
const [LineFormModal, lineFormModalApi] = useVbenModal({
@@ -55,6 +63,9 @@
function handleRefresh() {
for (const id of Object.keys(detailMap)) {
delete detailMap[Number(id)];
+ }
+ for (const id of Object.keys(checkedMap)) {
+ delete checkedMap[Number(id)];
}
gridApi.query();
}
@@ -99,6 +110,64 @@
row.itemCode,
row.itemName,
);
+}
+
+/** 琛屾槸鍚﹀叆搴撳嬀閫夌姸鎬侊紙鏈墜鍔ㄥ嬀閫夋椂鍥為��鍒拌鑷甫鍊硷級 */
+function isLineStock(row: MesWmProductReceiptLineApi.ProductReceiptLine) {
+ return checkedMap[row.id!] ?? row.isStock ?? false;
+}
+
+/** 鍒囨崲琛屾槸鍚﹀叆搴� */
+function setLineStock(
+ row: MesWmProductReceiptLineApi.ProductReceiptLine,
+ checked: boolean,
+) {
+ checkedMap[row.id!] = checked;
+}
+
+/** 淇濆瓨涓婃灦鍒嗛厤锛氭敹闆嗘墍鏈夎鐨勫嬀閫夌姸鎬佷笌浠撳簱/搴撳尯/搴撲綅鍒嗛厤锛岃皟鐢ㄦ壒閲忎笂鏋舵帴鍙� */
+async function handleSaveStock() {
+ if (!props.receiptId) {
+ return;
+ }
+ const hideLoading = message.loading({
+ content: '姝e湪淇濆瓨涓婃灦鍒嗛厤...',
+ duration: 0,
+ });
+ try {
+ const { list } = await getProductReceiptLinePage({
+ pageNo: 1,
+ pageSize: 10000,
+ receiptId: props.receiptId,
+ });
+ const rows: MesWmProductReceiptApi.StockRow[] = [];
+ for (const line of list) {
+ const isStock = isLineStock(line);
+ const row: MesWmProductReceiptApi.StockRow = {
+ lineId: line.id!,
+ isStock,
+ };
+ if (isStock) {
+ let details = detailMap[line.id!];
+ if (!details) {
+ details = await getProductReceiptDetailListByLineId(line.id!);
+ detailMap[line.id!] = details;
+ }
+ row.details = details.map((detail) => ({
+ warehouseId: detail.warehouseId!,
+ locationId: detail.locationId!,
+ areaId: detail.areaId!,
+ quantity: detail.quantity!,
+ }));
+ }
+ rows.push(row);
+ }
+ await submitProductReceiptStock({ receiptId: props.receiptId, rows });
+ message.success('涓婃灦鍒嗛厤淇濆瓨鎴愬姛');
+ handleRefresh();
+ } finally {
+ hideLoading();
+ }
}
/** 鎵撳紑涓婃灦鏄庣粏琛ㄥ崟 */
@@ -185,7 +254,7 @@
<LineFormModal @success="handleRefresh" />
<DetailFormModal @success="handleDetailSuccess" />
<Grid table-title="鐗╂枡淇℃伅">
- <template v-if="isEditable" #toolbar-tools>
+ <template v-if="canEditLine" #toolbar-tools>
<TableAction
:actions="[
{
@@ -197,6 +266,9 @@
]"
/>
</template>
+ <template v-if="isStock" #toolbar-tools>
+ <Button type="primary" @click="handleSaveStock">淇濆瓨涓婃灦鍒嗛厤</Button>
+ </template>
<template #detail="{ row }">
<DetailList
:details="getExpandedDetails(row)"
@@ -204,6 +276,14 @@
@edit="(detailId) => openDetailForm(row.id!, row.itemId, detailId)"
@refresh="loadLineDetails(row.id!)"
/>
+ </template>
+ <template #isStock="{ row }">
+ <div class="flex justify-center">
+ <Checkbox
+ :checked="isLineStock(row)"
+ @update:checked="(checked: boolean) => setLineStock(row, checked)"
+ />
+ </div>
</template>
<template #actions="{ row }">
<div class="flex items-center justify-center">
@@ -213,7 +293,7 @@
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
- ifShow: isEditable,
+ ifShow: canEditLine,
onClick: handleEdit.bind(null, row),
},
{
@@ -221,7 +301,7 @@
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
- ifShow: isEditable,
+ ifShow: canEditLine,
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.itemName]),
confirm: handleDelete.bind(null, row),
--
Gitblit v1.9.3