| | |
| | | taskId: number; |
| | | paramValues?: MesProFeedbackApi.ParamValue[]; |
| | | feedbackQuantity: number; |
| | | qualifiedQuantity?: number; |
| | | unqualifiedQuantity?: number; |
| | | remark?: string; |
| | | } |
| | | |
| | |
| | | feedbackId?: number; |
| | | taskStatus?: number; |
| | | workOrderStatus?: number; |
| | | /** æ¥å·¥åå©ä½å¯æ¥å·¥æ°é */ |
| | | reportableQuantity?: number; |
| | | } |
| | | |
| | | /** æ¹éæ¥å·¥è¯·æ± */ |
| | | export interface BatchCreateReq { |
| | | taskIds: number[]; |
| | | feedbackQuantity: number; |
| | | qualifiedQuantity?: number; |
| | | unqualifiedQuantity?: number; |
| | | remark?: string; |
| | | } |
| | | |
| | |
| | | status?: number; |
| | | remark?: string; |
| | | createTime?: Date; |
| | | waitDuration?: number; // çå¾
æ¶é¿ï¼å°æ¶ï¼ï¼æ¥å·¥åéå计æ¶çå¾
|
| | | needReportQuantity?: boolean; // æ¯å¦éè¦å¡«åæ¥å·¥æ°é |
| | | } |
| | | } |
| | | |
| | |
| | | remark?: string; |
| | | bomItems?: ProcessBomItem[]; |
| | | createTime?: Date; |
| | | waitDuration?: number; // çå¾
æ¶é¿ï¼å°æ¶ï¼ï¼>0 表示æ¥å·¥åéå计æ¶çå¾
|
| | | needReportQuantity?: boolean; // æ¯å¦éè¦å¡«åæ¥å·¥æ°éï¼false 表示æ¥å·¥æ°éå
许为空 |
| | | } |
| | | } |
| | | |
| | |
| | | pauseRecords?: PauseRecord[]; // åå·¥è®°å½ |
| | | reportableQuantity?: number; // 坿¥å·¥æ°é |
| | | assigneeUserIds?: number[]; // æ¥å·¥äºº |
| | | feedbackApprove?: boolean; // æ¯å¦æ¥å·¥å®¡æ¹ï¼æ´¾çèªå·¥åï¼ |
| | | waitStartTime?: string; // æ¥å·¥å计æ¶å¼å§æ¶é´ï¼yyyy-MM-dd HH:mm:ssï¼ |
| | | } |
| | | |
| | | /** MES ç产任å¡å页æ¥è¯¢åæ° */ |
| | |
| | | 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, |
| | | }); |
| | | } |
| | |
| | | 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[]; // è¡ä¸æ¶å表 |
| | | } |
| | | } |
| | | |
| | |
| | | ); |
| | | } |
| | | |
| | | /** æ¹é䏿¶ï¼å¾éæ¯å¦å
¥åº + åé
ä»åº/åºåº/åºä½æ°éï¼ */ |
| | | 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', { |
| | |
| | | quantity?: number; // å
¥åºæ°é |
| | | batchId?: number; // æ¹æ¬¡ç¼å· |
| | | batchCode?: string; // æ¹æ¬¡å· |
| | | qualityStatus?: number; // è´¨éç¶æï¼åå
¸ MES_WM_QUALITY_STATUS åæ ¼/ä¸åæ ¼ï¼ |
| | | isStock?: boolean; // æ¯å¦å
¥åºï¼false/空=æ§è¡å
¥åºæ¶é»è®¤è¿ä¸åæ ¼ä»åºï¼ |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | } |
| | |
| | | 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'; |
| | |
| | | * - create / updateï¼å½å
¥æ¥å·¥ä¸»ä½ä¿¡æ¯åæ°é |
| | | * - submit / approve / detailï¼ä¸»ä½å段åªè¯» |
| | | * |
| | | * æ°éåºåæ ¹æ® `checkFlag` å `unqualifiedQuantity` 卿æ¾ç¤ºï¼ |
| | | * - éè´¨æ£å·¥åºï¼æ¥å·¥æ°é = åæ ¼ + ä¸è¯ï¼ä¸è¯ > 0 æ¶åå±å¼å·¥åº/æåº/å
¶ä»åºå |
| | | * - è´¨æ£å·¥åºï¼åªå¡«æ¥å·¥æ°éï¼è§ä¸ºå¾
æ£æ°éï¼ |
| | | * æ¥å·¥åªå¡«å"æ¥å·¥æ°é"ï¼åæ ¼å/ä¸è¯åæ°éç±è´¨æ£ç¡®å®ï¼æ¥å·¥ç¯èä¸ééã |
| | | * è´¨æ£å·¥åºæ¥å·¥åè¿å
¥å¾
æ£éªï¼uncheckQuantity = æ¥å·¥æ°éï¼ï¼éè´¨æ£å·¥åºç´æ¥å®ç»ã |
| | | */ |
| | | export function useFormSchema( |
| | | formType: FormType, |
| | |
| | | }, |
| | | { |
| | | fieldName: 'checkFlag', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | defaultValue: true, |
| | | }, |
| | | { |
| | | fieldName: 'needReportQuantity', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | defaultValue: true, |
| | |
| | | status: MesProWorkOrderStatusEnum.CONFIRMED, |
| | | // 订ååæ´ï¼æ¸
空任å¡åä»»å¡å¸¦åºç产åä¿¡æ¯ãæ°éåºåæ§å¶ä½ |
| | | onChange: async () => { |
| | | resetWaitConfig(); |
| | | await formApi?.setValues({ |
| | | checkFlag: true, |
| | | needReportQuantity: true, |
| | | itemCode: undefined, |
| | | itemId: undefined, |
| | | itemName: undefined, |
| | |
| | | onChange: async (task?: MesProTaskApi.Task) => { |
| | | await loadParamDetails(task?.id); |
| | | if (!task) { |
| | | resetWaitConfig(); |
| | | await formApi?.setFieldValue('needReportQuantity', true); |
| | | return; |
| | | } |
| | | await formApi?.setValues({ |
| | |
| | | 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( |
| | |
| | | '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); |
| | | } |
| | | }, |
| | | }, |
| | |
| | | class: 'w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥æ¥å·¥æ°é', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['checkFlag'], |
| | | // éè´¨æ£å·¥åºæ¶ï¼æ¥å·¥æ°é = åæ ¼ + ä¸è¯ï¼ç¦ç¨ç´æ¥ç¼è¾ |
| | | componentProps: (values) => ({ |
| | | class: 'w-full', |
| | | disabled: !values.checkFlag, |
| | | min: 0, |
| | | placeholder: '请è¾å
¥æ¥å·¥æ°é', |
| | | precision: 2, |
| | | }), |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'qualifiedQuantity', |
| | | label: 'åæ ¼åæ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | // åæ ¼/ä¸è¯åæ´ï¼èªå¨ç´¯è®¡ä¸ºæ¥å·¥æ°é |
| | | onChange: async () => { |
| | | const values = await formApi?.getValues(); |
| | | await formApi?.setFieldValue( |
| | | 'feedbackQuantity', |
| | | (values?.qualifiedQuantity || 0) + |
| | | (values?.unqualifiedQuantity || 0), |
| | | ); |
| | | }, |
| | | }, |
| | | defaultValue: 0, |
| | | dependencies: { |
| | | triggerFields: ['checkFlag'], |
| | | show: (values) => !values.checkFlag, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'unqualifiedQuantity', |
| | | label: 'ä¸è¯åæ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | // åæ ¼/ä¸è¯åæ´ï¼èªå¨ç´¯è®¡ä¸ºæ¥å·¥æ°é |
| | | onChange: async () => { |
| | | const values = await formApi?.getValues(); |
| | | await formApi?.setFieldValue( |
| | | 'feedbackQuantity', |
| | | (values?.qualifiedQuantity || 0) + |
| | | (values?.unqualifiedQuantity || 0), |
| | | ); |
| | | }, |
| | | }, |
| | | defaultValue: 0, |
| | | dependencies: { |
| | | triggerFields: ['checkFlag'], |
| | | show: (values) => !values.checkFlag, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'laborScrapQuantity', |
| | | label: 'å·¥åºæ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | // åºååç±»åæ´ï¼èªå¨ç´¯è®¡ä¸ºä¸è¯åæ°é忥工æ°é |
| | | onChange: async () => { |
| | | const values = await formApi?.getValues(); |
| | | const unqualified = |
| | | (values?.laborScrapQuantity || 0) + |
| | | (values?.materialScrapQuantity || 0) + |
| | | (values?.otherScrapQuantity || 0); |
| | | await formApi?.setValues({ |
| | | feedbackQuantity: (values?.qualifiedQuantity || 0) + unqualified, |
| | | unqualifiedQuantity: unqualified, |
| | | }); |
| | | }, |
| | | }, |
| | | defaultValue: 0, |
| | | dependencies: { |
| | | triggerFields: ['checkFlag', 'unqualifiedQuantity'], |
| | | show: (values) => |
| | | !values.checkFlag && (values.unqualifiedQuantity || 0) > 0, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'materialScrapQuantity', |
| | | label: 'æåºæ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | // åºååç±»åæ´ï¼èªå¨ç´¯è®¡ä¸ºä¸è¯åæ°é忥工æ°é |
| | | onChange: async () => { |
| | | const values = await formApi?.getValues(); |
| | | const unqualified = |
| | | (values?.laborScrapQuantity || 0) + |
| | | (values?.materialScrapQuantity || 0) + |
| | | (values?.otherScrapQuantity || 0); |
| | | await formApi?.setValues({ |
| | | feedbackQuantity: (values?.qualifiedQuantity || 0) + unqualified, |
| | | unqualifiedQuantity: unqualified, |
| | | }); |
| | | }, |
| | | }, |
| | | defaultValue: 0, |
| | | dependencies: { |
| | | triggerFields: ['checkFlag', 'unqualifiedQuantity'], |
| | | show: (values) => |
| | | !values.checkFlag && (values.unqualifiedQuantity || 0) > 0, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'otherScrapQuantity', |
| | | label: 'å
¶ä»åºå', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | // åºååç±»åæ´ï¼èªå¨ç´¯è®¡ä¸ºä¸è¯åæ°é忥工æ°é |
| | | onChange: async () => { |
| | | const values = await formApi?.getValues(); |
| | | const unqualified = |
| | | (values?.laborScrapQuantity || 0) + |
| | | (values?.materialScrapQuantity || 0) + |
| | | (values?.otherScrapQuantity || 0); |
| | | await formApi?.setValues({ |
| | | feedbackQuantity: (values?.qualifiedQuantity || 0) + unqualified, |
| | | unqualifiedQuantity: unqualified, |
| | | }); |
| | | }, |
| | | }, |
| | | defaultValue: 0, |
| | | dependencies: { |
| | | triggerFields: ['checkFlag', 'unqualifiedQuantity'], |
| | | show: (values) => |
| | | !values.checkFlag && (values.unqualifiedQuantity || 0) > 0, |
| | | triggerFields: ['needReportQuantity'], |
| | | show: (values) => values.needReportQuantity !== false, |
| | | rules: (values) => |
| | | values.needReportQuantity === false ? null : 'required', |
| | | }, |
| | | }, |
| | | { |
| | |
| | | render: (val) => |
| | | h(DictTag, { type: DICT_TYPE.MES_PRO_FEEDBACK_TYPE, value: val }), |
| | | }, |
| | | { field: 'workOrderId', label: 'ç产订å', render: (_val, data) => data.workOrderCode }, |
| | | { field: 'taskId', label: 'ç产任å¡', render: (_val, data) => data.taskCode }, |
| | | { field: 'workOrderId', label: 'ç产订å', render: (_val, data) => data?.workOrderCode }, |
| | | { field: 'taskId', label: 'ç产任å¡', render: (_val, data) => data?.taskCode }, |
| | | { field: 'workstationName', label: 'å·¥ä½ç«' }, |
| | | { field: 'itemCode', label: '产åç¼ç ' }, |
| | | { field: 'itemName', label: '产ååç§°' }, |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesProTaskApi } from '#/api/mes/pro/task'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { Button, message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { batchCreateFeedback } from '#/api/mes/pro/feedback'; |
| | | import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process'; |
| | | import ProTaskSelectDialog from '#/views/mes/pro/task/components/select-dialog.vue'; |
| | | |
| | | defineOptions({ name: 'BatchFeedbackForm' }); |
| | |
| | | selectedTasks.value = []; |
| | | formData.value = { |
| | | feedbackQuantity: undefined, |
| | | qualifiedQuantity: undefined, |
| | | unqualifiedQuantity: undefined, |
| | | remark: '', |
| | | }; |
| | | } |
| | |
| | | |
| | | const taskDialogRef = ref<InstanceType<typeof ProTaskSelectDialog>>(); |
| | | const selectedTasks = ref<MesProTaskApi.Task[]>([]); |
| | | const needReportQuantityMap = ref<Record<number, boolean>>({}); |
| | | const formData = ref({ |
| | | feedbackQuantity: undefined as number | undefined, |
| | | qualifiedQuantity: undefined as number | undefined, |
| | | unqualifiedQuantity: undefined as number | undefined, |
| | | remark: '', |
| | | }); |
| | | const submitting = ref(false); |
| | | |
| | | /** æ¯å¦éè¦å¡«åæ¥å·¥æ°éï¼ä»»ä¸éä¸ä»»å¡å·¥åºè¦æ±å¡«åæ°éå³å¿
å¡«ï¼ */ |
| | | const needQuantity = computed(() => |
| | | selectedTasks.value.some( |
| | | (t) => needReportQuantityMap.value[t.id!] ?? true, |
| | | ), |
| | | ); |
| | | |
| | | function handleOpenTaskDialog() { |
| | | taskDialogRef.value?.open([], { multiple: true }); |
| | | } |
| | | |
| | | function handleTaskSelected(rows: MesProTaskApi.Task[]) { |
| | | /** æéä¸ä»»å¡å·¥åºå è½½æ¯å¦éè¦æ¥å·¥æ°é */ |
| | | async function handleTaskSelected(rows: MesProTaskApi.Task[]) { |
| | | selectedTasks.value = rows; |
| | | needReportQuantityMap.value = {}; |
| | | for (const task of rows) { |
| | | if (!task.routeId || !task.processId) { |
| | | continue; |
| | | } |
| | | try { |
| | | const routeProcess = await getRouteProcessByRouteAndProcess( |
| | | task.routeId, |
| | | task.processId, |
| | | ); |
| | | needReportQuantityMap.value[task.id!] = |
| | | routeProcess?.needReportQuantity ?? true; |
| | | } catch { |
| | | // å 载失败æé»è®¤å¤çï¼æ°éå¿
å¡«ï¼ |
| | | needReportQuantityMap.value[task.id!] = true; |
| | | } |
| | | } |
| | | } |
| | | |
| | | function handleRemoveTask(taskId: number) { |
| | | selectedTasks.value = selectedTasks.value.filter((t) => t.id !== taskId); |
| | | delete needReportQuantityMap.value[taskId]; |
| | | } |
| | | |
| | | async function handleSubmit() { |
| | |
| | | message.warning('è¯·éæ©ä»»å¡'); |
| | | return; |
| | | } |
| | | if (!formData.value.feedbackQuantity) { |
| | | if (needQuantity.value && !formData.value.feedbackQuantity) { |
| | | message.warning('请è¾å
¥æ¥å·¥æ°é'); |
| | | return; |
| | | } |
| | |
| | | try { |
| | | const result = await batchCreateFeedback({ |
| | | taskIds: selectedTasks.value.map((t) => t.id!), |
| | | feedbackQuantity: formData.value.feedbackQuantity, |
| | | qualifiedQuantity: formData.value.qualifiedQuantity, |
| | | unqualifiedQuantity: formData.value.unqualifiedQuantity, |
| | | feedbackQuantity: needQuantity.value |
| | | ? (formData.value.feedbackQuantity ?? 0) |
| | | : 0, |
| | | remark: formData.value.remark || undefined, |
| | | }); |
| | | if (result.failCount) { |
| | |
| | | </Tag> |
| | | </div> |
| | | </a-form-item> |
| | | <a-form-item label="æ¥å·¥æ°é" required> |
| | | <a-form-item |
| | | v-if="needQuantity" |
| | | label="æ¥å·¥æ°é" |
| | | required |
| | | tooltip="å·¥åºé
置为ä¸é填忥工æ°éæ¶ï¼åæ ¼å/ä¸è¯åæ°éç±è´¨æ£ç¡®å®" |
| | | > |
| | | <a-input-number |
| | | v-model:value="formData.feedbackQuantity" |
| | | :min="0.01" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥æ¥å·¥æ°éï¼åºç¨å°ææéä¸ä»»å¡ï¼" |
| | | class="!w-full" |
| | | /> |
| | | </a-form-item> |
| | | <a-form-item label="åæ ¼åæ°é"> |
| | | <a-input-number |
| | | v-model:value="formData.qualifiedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥åæ ¼åæ°é" |
| | | class="!w-full" |
| | | /> |
| | | </a-form-item> |
| | | <a-form-item label="ä¸è¯åæ°é"> |
| | | <a-input-number |
| | | v-model:value="formData.unqualifiedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥ä¸è¯åæ°é" |
| | | class="!w-full" |
| | | /> |
| | | </a-form-item> |
| | |
| | | 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'); |
| | |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | /** æäº¤å坹齿°éï¼æ ¹æ® checkFlag å³å® uncheck/åæ ¼/ä¸è¯å½é¶çç¥ */ |
| | | function alignQuantity(data: MesProFeedbackApi.Feedback) { |
| | | if (data.checkFlag) { |
| | | data.uncheckQuantity = data.feedbackQuantity; |
| | | data.qualifiedQuantity = 0; |
| | | data.unqualifiedQuantity = 0; |
| | | data.laborScrapQuantity = 0; |
| | | data.materialScrapQuantity = 0; |
| | | data.otherScrapQuantity = 0; |
| | | } else { |
| | | data.feedbackQuantity = |
| | | (data.qualifiedQuantity || 0) + (data.unqualifiedQuantity || 0); |
| | | data.uncheckQuantity = 0; |
| | | /** æäº¤å坹齿°éï¼æ¥å·¥åªå¡«åæ¥å·¥æ°éï¼åæ ¼/ä¸è¯/åºåæ°éç±è´¨æ£ç¡®å®ï¼æ¥å·¥ç¯èä¸éé */ |
| | | 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; |
| | | delete data.laborScrapQuantity; |
| | | delete data.materialScrapQuantity; |
| | | delete data.otherScrapQuantity; |
| | | } |
| | | |
| | | /** 读åå¹¶ææäº¤è§å对é½åçè¡¨åæ°æ®ï¼ç¨äºå¿«ç
§åä¿å */ |
| | | 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; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>(); |
| | | const batchActionType = ref<'submit' | 'approve'>(''); |
| | | const batchActionType = ref<'' | 'submit' | 'approve'>(''); |
| | | |
| | | function syncParamValues(data: MesProFeedbackApi.Feedback) { |
| | | data.paramValues = paramDetails.value.map((detail) => ({ |
| | |
| | | } |
| | | } |
| | | |
| | | /** å 载工åºç checkFlag ç¨äºåæ¾æ°éåºå */ |
| | | async function resolveCheckFlag(routeId?: number, processId?: number) { |
| | | /** å 载工åºé
ç½®ï¼checkFlag / çå¾
æ¶é¿ / æ¥å·¥æ°éè§åï¼ç¨äºåæ¾ */ |
| | | 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 }; |
| | | } |
| | | } |
| | | |
| | |
| | | subTabsName.value = 'itemConsume'; |
| | | originalSnapshot.value = ''; |
| | | paramDetails.value = []; |
| | | resetWaitConfig(); |
| | | return; |
| | | } |
| | | // å è½½æ°æ® |
| | |
| | | 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 { |
| | |
| | | <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"> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| | |
| | | .filter((detail) => detail.required && !detail.paramValue?.trim()) |
| | | .map((detail) => detail.paramName || detail.paramCode || ''); |
| | | } |
| | | |
| | | /** å½åæ¥å·¥ä»»å¡ç¼å·ï¼ç¨äºåè®¡æ¶æ¥å£ï¼ */ |
| | | 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; |
| | | } |
| | |
| | | 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', |
| | |
| | | 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(); |
| | | } |
| | |
| | | let wid: number | undefined; |
| | | |
| | | if (data?.id) { |
| | | // ç¼è¾æ¨¡å¼ï¼å
设置表å schemaï¼åå填任塿°æ®ï¼é¿å
åæ®µæªå»ºç«æ¶ setValues 丢失 |
| | | formApi.setState({ schema: useTaskFormSchema(formApi, workOrderQuantity) }); |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getTask(data.id); |
| | |
| | | }; |
| | | wid = data.workOrderId; |
| | | workOrderQuantity.value = data.quantity; |
| | | // æ°å¢æ¨¡å¼ï¼å
设置表å schemaï¼å设置åå§å¼ |
| | | formApi.setState({ schema: useTaskFormSchema(formApi, workOrderQuantity) }); |
| | | await formApi.setValues({ |
| | | colorCode: data.colorCode || '#00AEF3', |
| | | duration: 1, |
| | | quantity: data.quantity, |
| | | }); |
| | | } |
| | | |
| | | formApi.setState({ schema: useTaskFormSchema(formApi, workOrderQuantity) }); |
| | | }, |
| | | }); |
| | | </script> |
| | |
| | | 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' }); |
| | |
| | | selectedTask.value = undefined; |
| | | formData.value = { |
| | | feedbackQuantity: undefined, |
| | | qualifiedQuantity: undefined, |
| | | unqualifiedQuantity: 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 = []; |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | |
| | | const selectedTask = ref<MesProTaskApi.Task>(); |
| | | const formData = ref({ |
| | | feedbackQuantity: undefined as number | undefined, |
| | | qualifiedQuantity: undefined as number | undefined, |
| | | unqualifiedQuantity: undefined as number | undefined, |
| | | remark: '', |
| | | }); |
| | | const submitting = ref(false); |
| | | const waitDuration = ref<number>(); |
| | | const needReportQuantity = ref<boolean>(true); |
| | | const waitStartTime = ref<string>(); |
| | | |
| | | const hasPredefinedTask = computed(() => !!props.taskId); |
| | | |
| | |
| | | function handleTaskChange(task: MesProTaskApi.Task | undefined) { |
| | | 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() { |
| | |
| | | message.warning('è¯·éæ©ä»»å¡'); |
| | | return; |
| | | } |
| | | if (!formData.value.feedbackQuantity) { |
| | | if (needReportQuantity.value && !formData.value.feedbackQuantity) { |
| | | message.warning('请è¾å
¥æ¥å·¥æ°é'); |
| | | return; |
| | | } |
| | |
| | | try { |
| | | await quickCreateFeedback({ |
| | | taskId, |
| | | feedbackQuantity: formData.value.feedbackQuantity, |
| | | qualifiedQuantity: formData.value.qualifiedQuantity, |
| | | unqualifiedQuantity: formData.value.unqualifiedQuantity, |
| | | feedbackQuantity: needReportQuantity.value |
| | | ? (formData.value.feedbackQuantity ?? 0) |
| | | : 0, |
| | | paramValues: paramDetails.value.map((detail) => ({ |
| | | detailId: detail.id, |
| | | value: detail.paramValue, |
| | | })), |
| | | remark: formData.value.remark || undefined, |
| | | }); |
| | | message.success('æ¥å·¥æå'); |
| | |
| | | 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" |
| | |
| | | class="!w-full" |
| | | /> |
| | | </a-form-item> |
| | | <a-form-item label="åæ ¼åæ°é"> |
| | | <a-input-number |
| | | v-model:value="formData.qualifiedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥åæ ¼åæ°é" |
| | | class="!w-full" |
| | | /> |
| | | </a-form-item> |
| | | <a-form-item label="ä¸è¯åæ°é"> |
| | | <a-input-number |
| | | v-model:value="formData.unqualifiedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥ä¸è¯åæ°é" |
| | | class="!w-full" |
| | | /> |
| | | </a-form-item> |
| | | <div v-if="paramDetails.length" class="mb-4"> |
| | | <div class="mb-1 font-medium">å·¥èºåæ°</div> |
| | | <div |
| | | v-for="detail in paramDetails" |
| | | :key="detail.id" |
| | | class="mb-2 flex items-center gap-2" |
| | | > |
| | | <span class="w-40 flex-shrink-0 text-right text-sm text-gray-600"> |
| | | <span v-if="detail.required" class="text-red-500">* </span> |
| | | {{ detail.paramName || detail.paramCode |
| | | }}{{ |
| | | detail.unitMeasureName ? `ï¼${detail.unitMeasureName}ï¼` : '' |
| | | }} |
| | | </span> |
| | | <a-input |
| | | v-model:value="detail.paramValue" |
| | | placeholder="请è¾å
¥åæ°å¼" |
| | | class="!w-full" |
| | | /> |
| | | </div> |
| | | </div> |
| | | <a-form-item label="夿³¨"> |
| | | <a-textarea |
| | | v-model:value="formData.remark" |
| | |
| | | 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', |
| | |
| | | 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(); |
| | | } |
| | |
| | | } |
| | | |
| | | /** å·¥èºè·¯çº¿å·¥åºæç»è¡¨å */ |
| | | 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", |
| | |
| | | options: processOptions, |
| | | placeholder: "è¯·éæ©å·¥åº", |
| | | showSearch: true, |
| | | onChange: onProcessChange, |
| | | }, |
| | | rules: "selectRequired", |
| | | }, |
| | |
| | | }, |
| | | { |
| | | fieldName: "waitTime", |
| | | label: "çå¾
æ¶é´(å)", |
| | | label: "çå¾
æ¶é¿(å°æ¶)", |
| | | component: "InputNumber", |
| | | componentProps: { |
| | | class: "!w-full", |
| | | min: 0, |
| | | precision: 0, |
| | | precision: 2, |
| | | }, |
| | | rules: z.number().default(0), |
| | | }, |
| | |
| | | width: 160, |
| | | }, |
| | | { field: "prepareTime", title: "å夿¶é´(å)", width: 110 }, |
| | | { field: "waitTime", title: "çå¾
æ¶é´(å)", width: 110 }, |
| | | { field: "waitTime", title: "çå¾
æ¶é¿(å°æ¶)", width: 110 }, |
| | | { |
| | | field: "colorCode", |
| | | title: "çç¹å¾é¢è²", |
| | |
| | | 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, |
| | |
| | | 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(); |
| | |
| | | label: item.name ?? '', |
| | | value: item.id!, |
| | | })); |
| | | return useRouteProcessFormSchema(options); |
| | | return useRouteProcessFormSchema(options, handleProcessChange); |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | |
| | | <script lang="ts" setup> |
| | | import type { PendingTask } from '../../../types'; |
| | | import type { PendingTask } from '../../types'; |
| | | import type { FormType } from '../../../pro/feedback/data'; |
| | | |
| | | import { computed, ref, watch } from 'vue'; |
| | |
| | | } from '@vben/constants'; |
| | | import { useUserStore } from '@vben/stores'; |
| | | |
| | | import { Button, message, Modal, Popconfirm } from 'ant-design-vue'; |
| | | import { Button, Input, message, Modal, Popconfirm } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | |
| | | 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 batchActionType = ref<'' | 'submit' | 'approve'>(''); |
| | | const pendingSubmitMode = ref<'saveAndSubmit' | 'submit'>('submit'); |
| | | defineOptions({ name: 'WorkbenchFeedbackTab' }); |
| | | |
| | |
| | | wrapperClass: 'grid-cols-3', |
| | | }); |
| | | |
| | | /** æ ¹æ® checkFlag 坹齿°é */ |
| | | /** æäº¤å坹齿°éï¼æ¥å·¥åªå¡«åæ¥å·¥æ°éï¼åæ ¼/ä¸è¯/åºåæ°éç±è´¨æ£ç¡®å®ï¼æ¥å·¥ç¯èä¸éé */ |
| | | function alignQuantity(data: any) { |
| | | if (data.checkFlag) { |
| | | data.uncheckQuantity = data.feedbackQuantity; |
| | | data.qualifiedQuantity = 0; |
| | | data.unqualifiedQuantity = 0; |
| | | data.laborScrapQuantity = 0; |
| | | data.materialScrapQuantity = 0; |
| | | data.otherScrapQuantity = 0; |
| | | } else { |
| | | data.feedbackQuantity = |
| | | (data.qualifiedQuantity || 0) + (data.unqualifiedQuantity || 0); |
| | | data.uncheckQuantity = 0; |
| | | 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() { |
| | | const data = (await formApi.getValues()) as any; |
| | | syncParamValues(data); |
| | | alignQuantity(data); |
| | | return data; |
| | | } |
| | | |
| | | /** å è½½ checkFlag */ |
| | | async function resolveCheckFlag(routeId?: number, processId?: number) { |
| | | if (!routeId || !processId) return true; |
| | | /** 忥工èºåæ°å¼å°æ¥å·¥æ°æ®ï¼ä¸æäº¤æ°æ®ä¸ååéç»åç«¯ï¼ */ |
| | | function syncParamValues(data: any) { |
| | | data.paramValues = paramDetails.value.map((detail) => ({ |
| | | detailId: detail.id, |
| | | value: detail.paramValue, |
| | | })); |
| | | } |
| | | |
| | | /** å 载工åºé
ç½®ï¼checkFlag / çå¾
æ¶é¿ / æ¥å·¥æ°éè§åï¼ */ |
| | | 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 }; |
| | | } |
| | | } |
| | | |
| | |
| | | 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, |
| | |
| | | itemName: task.itemName, |
| | | itemSpecification: task.itemSpecification, |
| | | unitMeasureName: task.unitMeasureName, |
| | | reportableQuantity: task.reportableQuantity, |
| | | scheduledQuantity: task.quantity, |
| | | feedbackApprove: task.feedbackApprove, |
| | | checkFlag, |
| | | checkFlag: config.checkFlag, |
| | | needReportQuantity: config.needReportQuantity, |
| | | }); |
| | | originalSnapshot.value = JSON.stringify(await getAlignedData()); |
| | | } |
| | |
| | | pendingSubmitMode.value = 'saveAndSubmit'; |
| | | |
| | | // 4. è°ç¨æäº¤æ¥å£ |
| | | const isDone = await handleOnSubmit(feedbackId.value!,props.task.feedbackApprove); |
| | | const isDone = await handleOnSubmit(feedbackId.value!,props.task?.feedbackApprove); |
| | | if (!isDone) return; |
| | | |
| | | feedbackId.value = record.id; |
| | |
| | | } |
| | | |
| | | pendingSubmitMode.value = 'submit'; |
| | | const isDone = await handleOnSubmit(feedbackId.value!,props.task.feedbackApprove); |
| | | const isDone = await handleOnSubmit(feedbackId.value!,props.task?.feedbackApprove); |
| | | if (!isDone) return; |
| | | |
| | | message.success('æ¥å·¥åå·²æäº¤'); |
| | |
| | | feedbackStatus.value = data.status; |
| | | formType.value = 'update'; |
| | | formApi.setState({ schema: useFormSchema(formType.value, formApi) }); |
| | | const checkFlag = await resolveCheckFlag(data.routeId, data.processId); |
| | | await formApi.setValues({ ...data, checkFlag }); |
| | | const config = await resolveRouteProcessConfig(data.routeId, data.processId); |
| | | await loadParamDetails(data.taskId); |
| | | 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()); |
| | | } |
| | | |
| | | // çå¬ä»»å¡åæ´ï¼ä»
å½ä»»å¡ ID çæ£ååæ¶æé置表åï¼ |
| | | // çå¬ä»»å¡åæ´ï¼ä»»å¡å¯¹è±¡ååï¼éæ©ä»»å¡ / åè¡¨å·æ°ååæ¥çææ°æ°æ®ï¼æ¶é置表åï¼ |
| | | // ç¡®ä¿"坿¥å·¥æ°é"ç任塿´¾çåæ®µå§ç»å±ç¤ºææ°å¼ |
| | | watch( |
| | | () => props.task?.id, |
| | | () => props.task, |
| | | () => { |
| | | if (props.task) { |
| | | resetForTask(props.task); |
| | |
| | | </div> |
| | | |
| | | <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> |
| | | <div class="feedback-tab__params-grid"> |
| | | <div |
| | | v-for="detail in paramDetails" |
| | | :key="detail.id" |
| | | class="feedback-tab__params-item" |
| | | > |
| | | <span class="feedback-tab__params-label"> |
| | | <span v-if="detail.required" class="feedback-tab__params-required" |
| | | >* </span |
| | | > |
| | | {{ detail.paramName || detail.paramCode |
| | | }}{{ |
| | | detail.unitMeasureName ? `ï¼${detail.unitMeasureName}ï¼` : '' |
| | | }} |
| | | </span> |
| | | <Input |
| | | v-model:value="detail.paramValue" |
| | | :disabled="!isEditable" |
| | | placeholder="请è¾å
¥åæ°å¼" |
| | | class="feedback-tab__params-input" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="feedback-tab__actions"> |
| | | <Button |
| | |
| | | border-top: 1px solid #f0f0f0; |
| | | margin-top: 16px; |
| | | } |
| | | |
| | | .feedback-tab__params { |
| | | margin-top: 16px; |
| | | padding: 14px 16px; |
| | | border: 1px solid #f0f0f0; |
| | | border-radius: 8px; |
| | | background: #fafbfc; |
| | | } |
| | | |
| | | .feedback-tab__params-title { |
| | | margin-bottom: 12px; |
| | | font-size: 15px; |
| | | font-weight: 600; |
| | | color: #1a1a1a; |
| | | } |
| | | |
| | | .feedback-tab__params-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | gap: 12px 20px; |
| | | } |
| | | |
| | | .feedback-tab__params-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .feedback-tab__params-label { |
| | | flex-shrink: 0; |
| | | width: 176px; |
| | | text-align: right; |
| | | font-size: 14px; |
| | | color: #595959; |
| | | } |
| | | |
| | | .feedback-tab__params-required { |
| | | color: #ff4d4f; |
| | | } |
| | | |
| | | .feedback-tab__params-input { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | </style> |
| | |
| | | <script lang="ts" setup> |
| | | import type { PendingTask } from '../../../types'; |
| | | import type { PendingTask } from '../../types'; |
| | | |
| | | import { Descriptions, Progress, Tag, Timeline, TimelineItem } from 'ant-design-vue'; |
| | | |
| | |
| | | checked: config?.approvalEnabled ?? false, |
| | | checkedChildren: 'å¼', |
| | | unCheckedChildren: 'å
³', |
| | | onChange: (checked: boolean) => { |
| | | handleChange(row, 'INBOUND', checked); |
| | | onChange: (checked) => { |
| | | handleChange(row, 'INBOUND', Boolean(checked)); |
| | | }, |
| | | }); |
| | | }, |
| | |
| | | checked: config?.approvalEnabled ?? false, |
| | | checkedChildren: 'å¼', |
| | | unCheckedChildren: 'å
³', |
| | | onChange: (checked: boolean) => { |
| | | handleChange(row, 'OUTBOUND', checked); |
| | | onChange: (checked) => { |
| | | handleChange(row, 'OUTBOUND', Boolean(checked)); |
| | | }, |
| | | }); |
| | | }, |
| | |
| | | checked: config?.approvalEnabled ?? false, |
| | | checkedChildren: 'å¼', |
| | | unCheckedChildren: 'å
³', |
| | | onChange: (checked: boolean) => { |
| | | handleChange(row, 'TRANSFER', checked); |
| | | onChange: (checked) => { |
| | | handleChange(row, 'TRANSFER', Boolean(checked)); |
| | | }, |
| | | }); |
| | | }, |
| | |
| | | checked: config?.approvalEnabled ?? false, |
| | | checkedChildren: 'å¼', |
| | | unCheckedChildren: 'å
³', |
| | | onChange: (checked: boolean) => { |
| | | handleChange(row, 'CHECK', checked); |
| | | onChange: (checked) => { |
| | | handleChange(row, 'CHECK', Boolean(checked)); |
| | | }, |
| | | }); |
| | | }, |
| | |
| | | } |
| | | |
| | | /** æµç¨é
置表å */ |
| | | export function useProcessFormSchema(): VbenFormSchema[] { |
| | | export function useProcessFormSchema( |
| | | onBizTypeChange?: (value?: string) => void, |
| | | ): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'warehouseId', |
| | |
| | | componentProps: { |
| | | options: BIZ_TYPE_OPTIONS, |
| | | placeholder: 'è¯·éæ©ä¸å¡ç±»å', |
| | | onChange: onBizTypeChange, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { WmsApprovalApi } from '#/api/mes/wm/approval'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { ref, unref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | |
| | |
| | | columns: useGridColumns(handleApprovalChange), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | data: warehouseConfigList, |
| | | data: unref(warehouseConfigList), |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | |
| | | <script lang="ts" setup> |
| | | import type { WmsApprovalApi } from '#/api/mes/wm/approval'; |
| | | |
| | | import { computed, ref, watch } from 'vue'; |
| | | import { ref, watch } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | |
| | | }, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useProcessFormSchema(), |
| | | schema: useProcessFormSchema((value) => { |
| | | currentBizType.value = value; |
| | | }), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | |
| | | }, |
| | | onOpenChange(isOpen) { |
| | | if (isOpen) { |
| | | currentBizType.value = undefined; |
| | | const data = modalApi.getData() as { |
| | | warehouseId: number; |
| | | warehouseName: string; |
| | |
| | | }, |
| | | }); |
| | | |
| | | /** å½åéä¸çä¸å¡ç±»å */ |
| | | const currentBizType = computed(() => { |
| | | const values = formApi.getValues(); |
| | | return values.bizType; |
| | | }); |
| | | /** å½åéä¸çä¸å¡ç±»åï¼formApi.getValues ä¸ºå¼æ¥ï¼æ¹ç±å段 onChange åæ¥ï¼ */ |
| | | const currentBizType = ref<string>(); |
| | | |
| | | /** çå¬ä¸å¡ç±»åååï¼å 载对åºçæµç¨å表 */ |
| | | watch(currentBizType, async (bizType) => { |
| | |
| | | message.warning('è¯·éæ©éè´è®¢å'); |
| | | return; |
| | | } |
| | | emit('selected', selectedRows.id); |
| | | emit('selected', selectedRows.id!); |
| | | await modalApi.close(); |
| | | }, |
| | | async onOpened() { |
| | |
| | | message.warning('è¯·éæ©å°è´§éç¥å'); |
| | | return; |
| | | } |
| | | emit('selected', selectedRows.id); |
| | | emit('selected', selectedRows.id!); |
| | | await modalApi.close(); |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | |
| | | |
| | | if (!enabled) { |
| | | formApi.updateSchema([ |
| | | { fieldName: 'batchCode', dependencies: { show: () => false } }, |
| | | { fieldName: 'manualBatchEntry', dependencies: { show: () => false } }, |
| | | { fieldName: 'produceDate', dependencies: { show: () => false } }, |
| | | { fieldName: 'expireDate', dependencies: { show: () => false } }, |
| | | { fieldName: 'receiptDate', dependencies: { show: () => false } }, |
| | | { fieldName: 'vendorId', dependencies: { show: () => true } }, |
| | | { fieldName: 'purchaseOrderCode', dependencies: { show: () => false } }, |
| | | { fieldName: 'lotNumber', dependencies: { show: () => false } }, |
| | | { fieldName: 'qualityStatus', dependencies: { show: () => false } }, |
| | | { fieldName: 'batchCode', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'manualBatchEntry', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'produceDate', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'expireDate', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'receiptDate', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'vendorId', dependencies: { show: () => true, triggerFields: [''] } }, |
| | | { fieldName: 'purchaseOrderCode', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'lotNumber', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'qualityStatus', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | ]); |
| | | return; |
| | | } |
| | |
| | | const isIn = operateType.value === 1; |
| | | |
| | | formApi.updateSchema([ |
| | | { fieldName: 'batchCode', dependencies: { show: () => manualBatchEntry.value } }, |
| | | { fieldName: 'manualBatchEntry', dependencies: { show: () => true } }, |
| | | { fieldName: 'produceDate', dependencies: { show: () => isIn && !!item?.produceDateFlag } }, |
| | | { fieldName: 'expireDate', dependencies: { show: () => isIn && !!item?.expireDateFlag } }, |
| | | { fieldName: 'receiptDate', dependencies: { show: () => isIn && !!item?.receiptDateFlag } }, |
| | | { fieldName: 'vendorId', dependencies: { show: () => !isIn || !!item?.vendorFlag } }, |
| | | { fieldName: 'purchaseOrderCode', dependencies: { show: () => isIn && !!item?.purchaseOrderCodeFlag } }, |
| | | { fieldName: 'lotNumber', dependencies: { show: () => isIn && !!item?.lotNumberFlag } }, |
| | | { fieldName: 'qualityStatus', dependencies: { show: () => isIn && !!item?.qualityStatusFlag } }, |
| | | { fieldName: 'batchCode', dependencies: { show: () => manualBatchEntry.value, triggerFields: [''] } }, |
| | | { fieldName: 'manualBatchEntry', dependencies: { show: () => true, triggerFields: [''] } }, |
| | | { fieldName: 'produceDate', dependencies: { show: () => isIn && !!item?.produceDateFlag, triggerFields: [''] } }, |
| | | { fieldName: 'expireDate', dependencies: { show: () => isIn && !!item?.expireDateFlag, triggerFields: [''] } }, |
| | | { fieldName: 'receiptDate', dependencies: { show: () => isIn && !!item?.receiptDateFlag, triggerFields: [''] } }, |
| | | { fieldName: 'vendorId', dependencies: { show: () => !isIn || !!item?.vendorFlag, triggerFields: [''] } }, |
| | | { fieldName: 'purchaseOrderCode', dependencies: { show: () => isIn && !!item?.purchaseOrderCodeFlag, triggerFields: [''] } }, |
| | | { fieldName: 'lotNumber', dependencies: { show: () => isIn && !!item?.lotNumberFlag, triggerFields: [''] } }, |
| | | { fieldName: 'qualityStatus', dependencies: { show: () => isIn && !!item?.qualityStatusFlag, triggerFields: [''] } }, |
| | | ]); |
| | | } |
| | | |
| | |
| | | async function handleOperateTypeChange(type: 1 | 2) { |
| | | operateType.value = type; |
| | | modalTitle.value = type === 1 ? 'åºåå
¥åº' : 'åºååºåº'; |
| | | modalApi.setState({ title: modalTitle.value }); |
| | | await formApi.setFieldValue('operateType', type); |
| | | // æ´æ°æ¹æ¬¡å段æ¾ç¤º |
| | | await updateBatchFields(); |
| | |
| | | batchEnabled.value = false; |
| | | operateType.value = 1; |
| | | modalTitle.value = 'åºåè°æ´'; |
| | | modalApi.setState({ title: modalTitle.value }); |
| | | return; |
| | | } |
| | | // é置表å |
| | | await formApi.resetForm(); |
| | | // é»è®¤éèæ¹æ¬¡ç¸å
³å段 |
| | | formApi.updateSchema([ |
| | | { fieldName: 'batchCode', dependencies: { show: () => false } }, |
| | | { fieldName: 'manualBatchEntry', dependencies: { show: () => false } }, |
| | | { fieldName: 'produceDate', dependencies: { show: () => false } }, |
| | | { fieldName: 'expireDate', dependencies: { show: () => false } }, |
| | | { fieldName: 'receiptDate', dependencies: { show: () => false } }, |
| | | { fieldName: 'purchaseOrderCode', dependencies: { show: () => false } }, |
| | | { fieldName: 'lotNumber', dependencies: { show: () => false } }, |
| | | { fieldName: 'qualityStatus', dependencies: { show: () => false } }, |
| | | { fieldName: 'batchCode', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'manualBatchEntry', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'produceDate', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'expireDate', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'receiptDate', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'purchaseOrderCode', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'lotNumber', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | { fieldName: 'qualityStatus', dependencies: { show: () => false, triggerFields: [''] } }, |
| | | ]); |
| | | // éç½®æå¨å¡«åæ¹æ¬¡å·ç¶æ |
| | | manualBatchEntry.value = false; |
| | |
| | | } |
| | | } |
| | | }, |
| | | title: modalTitle, |
| | | title: 'åºåè°æ´', |
| | | }); |
| | | |
| | | /** æäº¤è¡¨å */ |
| | |
| | | }, |
| | | }, |
| | | { |
| | | field: 'feedbackId', |
| | | title: 'æ¥æº', |
| | | width: 100, |
| | | slots: { default: 'source' }, |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 240, |
| | | fixed: 'right', |
| | |
| | | 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 |
| | | ? [ |
| | | { |
| | |
| | | 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 { |
| | |
| | | {{ row.code }} |
| | | </Button> |
| | | </template> |
| | | <template #source="{ row }"> |
| | | <Tag v-if="row.feedbackId" color="blue">èªå¨çæ</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | |
| | | <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> |
| | |
| | | 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'; |
| | | |
| | |
| | | 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, |
| | |
| | | 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({ |
| | |
| | | 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(); |
| | | } |
| | |
| | | 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: 'æ£å¨ä¿å䏿¶åé
...', |
| | | 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; |
| | | } |
| | | // å¾éå
¥åºçè¡å¿
须已æä¸æ¶æç»ï¼ä¸æç»æ°éä¹åçäºè¡æ¶è´§æ°é |
| | | if (!details.length) { |
| | | message.error(`ç©æã${line.itemName}ãå·²å¾éå
¥åºï¼è¯·å
æ·»å 䏿¶æç»`); |
| | | return; |
| | | } |
| | | const lineQty = Number(line.quantity ?? 0); |
| | | const sumQty = details.reduce( |
| | | (acc, detail) => acc + Number(detail.quantity ?? 0), |
| | | 0, |
| | | ); |
| | | if (lineQty !== sumQty) { |
| | | message.error( |
| | | `ç©æã${line.itemName}ã䏿¶æç»æ°éä¹åï¼${sumQty}ï¼ä¸æ¶è´§æ°éï¼${lineQty}ï¼ä¸ä¸è´`, |
| | | ); |
| | | return; |
| | | } |
| | | 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(); |
| | | } |
| | | } |
| | | |
| | | /** æå¼ä¸æ¶æç»è¡¨å */ |
| | |
| | | <LineFormModal @success="handleRefresh" /> |
| | | <DetailFormModal @success="handleDetailSuccess" /> |
| | | <Grid table-title="ç©æä¿¡æ¯"> |
| | | <template v-if="isEditable" #toolbar-tools> |
| | | <template v-if="canEditLine" #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template v-if="isStock" #toolbar-tools> |
| | | <Button type="primary" @click="handleSaveStock">ä¿å䏿¶åé
</Button> |
| | | </template> |
| | | <template #detail="{ row }"> |
| | | <DetailList |
| | | :details="getExpandedDetails(row)" |
| | |
| | | @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"> |
| | |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | ifShow: isEditable, |
| | | ifShow: canEditLine, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | |
| | | 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), |
| | |
| | | contactAddress: notice?.recipientAddress, |
| | | contactName: notice?.recipientName, |
| | | contactTelephone: notice?.recipientTelephone, |
| | | salesOrderCode: notice?.salesOrderCode, |
| | | salesOrderCode: notice?.saleOrderCode, |
| | | }); |
| | | }, |
| | | status: MesWmSalesNoticeStatusEnum.APPROVED, |
| | |
| | | try { |
| | | formData.value = await getReturnSales(data.id); |
| | | // éè¿éå®è®¢åå·åæ¥è®¢åIDï¼ç¡®ä¿ ErpSaleOrderSelect è½æ£ç¡®åæ¾ |
| | | if (formData.value?.salesOrderCode && !formData.value.saleOrderId) { |
| | | const formValues = formData.value as MesWmReturnSalesApi.ReturnSales & { |
| | | saleOrderId?: number; |
| | | }; |
| | | if (formValues.salesOrderCode && !formValues.saleOrderId) { |
| | | const orderPage = await getSaleOrderPage({ |
| | | pageNo: 1, |
| | | pageSize: 1, |
| | | no: formData.value.salesOrderCode, |
| | | } as Record<string, unknown> as never); |
| | | const list = (orderPage as Record<string, unknown>)?.list as Array<Record<string, unknown>> | undefined; |
| | | if (list?.length) { |
| | | const orderId = list[0].id as number; |
| | | (formData.value as Record<string, unknown>).saleOrderId = orderId; |
| | | no: formValues.salesOrderCode, |
| | | } as never); |
| | | const first = orderPage?.list?.[0]; |
| | | if (first) { |
| | | const orderId = Number(first.id); |
| | | formValues.saleOrderId = orderId; |
| | | const order = await getSaleOrder(orderId); |
| | | saleOrderItems.value = order?.items || []; |
| | | } |
| | |
| | | pageNo: 1, |
| | | pageSize: 1, |
| | | }); |
| | | const batchList = batchPage?.list; |
| | | if (batchList?.length) { |
| | | await formApi.setFieldValue('batchId', batchList[0].id); |
| | | const firstBatch = batchPage?.list?.[0]; |
| | | if (firstBatch) { |
| | | await formApi.setFieldValue('batchId', firstBatch.id); |
| | | } |
| | | } catch { |
| | | // æ¥è¯¢å¤±è´¥æ¶å¿½ç¥ |