| | |
| | | <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'; |
| | | |
| | | import dayjs from 'dayjs'; |
| | | |
| | | import { |
| | | MesAutoCodeRuleCode, |
| | |
| | | } from '@vben/constants'; |
| | | import { useUserStore } from '@vben/stores'; |
| | | |
| | | import { Button, message, 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'; |
| | | import { |
| | | approveFeedback, |
| | | createFeedback, |
| | | createFeedback, getBatchRequirements, |
| | | getFeedback, |
| | | getFeedbackPage, |
| | | rejectFeedback, |
| | | submitFeedback, |
| | | updateFeedback, |
| | |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../../../pro/feedback/data'; |
| | | |
| | | import { |
| | | loadParamDetails, |
| | | paramDetails, |
| | | } from '../../../pro/feedback/param-state'; |
| | | import BatchInputDialog from '../../../pro/feedback/modules/batch-input-dialog.vue'; |
| | | const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>(); |
| | | const batchActionType = ref<'' | 'submit' | 'approve'>(''); |
| | | const pendingSubmitMode = ref<'saveAndSubmit' | 'submit'>('submit'); |
| | | defineOptions({ name: 'WorkbenchFeedbackTab' }); |
| | | |
| | | const props = defineProps<{ |
| | |
| | | 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; |
| | | } |
| | | 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() { |
| | | const data = (await formApi.getValues()) as any; |
| | | syncParamValues(data); |
| | | alignQuantity(data); |
| | | return data; |
| | | } |
| | | |
| | | /** 同步工艺参数值到报工数据(与提交数据一同发送给后端) */ |
| | | function syncParamValues(data: any) { |
| | | data.paramValues = paramDetails.value.map((detail) => ({ |
| | | detailId: detail.id, |
| | | value: detail.paramValue, |
| | | })); |
| | | } |
| | | |
| | | /** 加载 checkFlag */ |
| | |
| | | |
| | | const code = await generateAutoCode(MesAutoCodeRuleCode.PRO_FEEDBACK_CODE); |
| | | const checkFlag = await resolveCheckFlag(task.routeId, task.processId); |
| | | // 加载任务绑定的工艺参数(必填参数校验 + 参数值回显) |
| | | await loadParamDetails(task.id); |
| | | |
| | | await formApi.setValues({ |
| | | code, |
| | | type: 1, |
| | | feedbackTime: Date.now(), |
| | | feedbackTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), |
| | | feedbackUserId: userStore.userInfo?.id, |
| | | taskId: task.id, |
| | | workOrderId: task.workOrderId, |
| | |
| | | itemName: task.itemName, |
| | | itemSpecification: task.itemSpecification, |
| | | unitMeasureName: task.unitMeasureName, |
| | | reportableQuantity: task.reportableQuantity, |
| | | scheduledQuantity: task.quantity, |
| | | feedbackApprove: task.feedbackApprove, |
| | | checkFlag, |
| | | }); |
| | | |
| | | originalSnapshot.value = JSON.stringify(await getAlignedData()); |
| | | } |
| | | |
| | |
| | | if (!valid) return; |
| | | |
| | | const data = await getAlignedData(); |
| | | const code = data.code as string; |
| | | |
| | | if (formType.value === 'create') { |
| | | const id = await createFeedback(data); |
| | |
| | | formType.value = 'update'; |
| | | formApi.setState({ schema: useFormSchema(formType.value, formApi) }); |
| | | await formApi.setFieldValue('id', id); |
| | | message.success($t('common.createSuccess')); |
| | | } else { |
| | | await updateFeedback(data); |
| | | message.success($t('common.updateSuccess')); |
| | | } |
| | | originalSnapshot.value = JSON.stringify(data); |
| | | |
| | | // 显示显眼的成功提示 |
| | | Modal.success({ |
| | | title: '保存成功', |
| | | content: `报工单 ${code} 已保存`, |
| | | centered: true, |
| | | }); |
| | | |
| | | // 重置表单为新建模式,重新生成报订单号 |
| | | if (props.task) { |
| | | await resetForTask(props.task); |
| | | } |
| | | |
| | | emit('reported'); |
| | | } |
| | | |
| | | /** 保存并提交(先保存,再查询并提交) */ |
| | | async function handleSaveAndSubmit() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) return; |
| | | |
| | | const data = await getAlignedData(); |
| | | const code = data.code as string; |
| | | |
| | | // 1. 先保存 |
| | | if (formType.value === 'create') { |
| | | const id = await createFeedback(data); |
| | | feedbackId.value = id; |
| | | feedbackStatus.value = MesProFeedbackStatusEnum.PREPARE; |
| | | formType.value = 'update'; |
| | | formApi.setState({ schema: useFormSchema(formType.value, formApi) }); |
| | | await formApi.setFieldValue('id', id); |
| | | } else { |
| | | await updateFeedback(data); |
| | | } |
| | | originalSnapshot.value = JSON.stringify(data); |
| | | |
| | | // 2. 查询报工记录,匹配该报工单号 |
| | | const pageResult = await getFeedbackPage({ code, pageNo: 1, pageSize: 1 }); |
| | | const record = pageResult.list?.[0]; |
| | | |
| | | // 3. 检查是否可提交(草稿状态) |
| | | if (!record || record.status !== MesProFeedbackStatusEnum.PREPARE) { |
| | | message.warning('保存成功,但当前报工单状态不可提交'); |
| | | emit('reported'); |
| | | return; |
| | | } |
| | | |
| | | pendingSubmitMode.value = 'saveAndSubmit'; |
| | | |
| | | // 4. 调用提交接口 |
| | | const isDone = await handleOnSubmit(feedbackId.value!,props.task?.feedbackApprove); |
| | | if (!isDone) return; |
| | | |
| | | feedbackId.value = record.id; |
| | | feedbackStatus.value = record.status; |
| | | |
| | | // 显示显眼的成功提示 |
| | | Modal.success({ |
| | | title: '保存并提交成功', |
| | | content: `报工单 ${code} 已提交`, |
| | | centered: true, |
| | | }); |
| | | |
| | | // 重置表单为新建模式,重新生成报订单号 |
| | | if (props.task) { |
| | | await resetForTask(props.task); |
| | | } |
| | | emit('reported'); |
| | | } |
| | | |
| | |
| | | await updateFeedback(data); |
| | | originalSnapshot.value = JSON.stringify(data); |
| | | } |
| | | await submitFeedback(feedbackId.value!); |
| | | message.success('报订单已提交'); |
| | | |
| | | pendingSubmitMode.value = 'submit'; |
| | | const isDone = await handleOnSubmit(feedbackId.value!,props.task?.feedbackApprove); |
| | | if (!isDone) return; |
| | | |
| | | message.success('报工单已提交'); |
| | | // 重置表单为新建模式 |
| | | if (props.task) { |
| | | await resetForTask(props.task); |
| | |
| | | emit('reported'); |
| | | } |
| | | |
| | | |
| | | /** 提交方法 */ |
| | | async function handleOnSubmit(id:any,feedbackRate:any): Promise<boolean> { |
| | | if (feedbackRate&&feedbackRate==true){ |
| | | await submitFeedback({ id: feedbackId.value! }); |
| | | return true; |
| | | } |
| | | |
| | | const checkResult = await getBatchRequirements(id); |
| | | if (checkResult?.needBatchInput) { |
| | | batchActionType.value = 'submit'; |
| | | batchDialogRef.value?.open({ checkResult }); |
| | | return false; |
| | | } else { |
| | | await submitFeedback({ id: feedbackId.value! }); |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | /** 批次确认回调 */ |
| | | async function handleBatchConfirm(batchData: Record<string, unknown>) { |
| | | if (batchActionType.value === 'submit') { |
| | | await submitFeedback({ id: feedbackId.value!, ...batchData }); |
| | | |
| | | if (pendingSubmitMode.value === 'saveAndSubmit') { |
| | | const data = await getAlignedData(); |
| | | const code = data.code as string; |
| | | Modal.success({ |
| | | title: '保存并提交成功', |
| | | content: `报工单 ${code} 已提交`, |
| | | centered: true, |
| | | }); |
| | | } else { |
| | | message.success('报工单已提交'); |
| | | } |
| | | |
| | | if (props.task) { |
| | | await resetForTask(props.task); |
| | | } |
| | | emit('reported'); |
| | | } |
| | | } |
| | | |
| | | /** 审批通过 */ |
| | | async function handleApprove() { |
| | | if (!feedbackId.value) return; |
| | | const finished = await approveFeedback(feedbackId.value); |
| | | const finished = await approveFeedback({ id: feedbackId.value }); |
| | | message.success( |
| | | finished ? '报订单已审批完成' : '报工成功,请等待质量检验完成!', |
| | | ); |
| | |
| | | formType.value = 'update'; |
| | | formApi.setState({ schema: useFormSchema(formType.value, formApi) }); |
| | | const checkFlag = await resolveCheckFlag(data.routeId, data.processId); |
| | | await loadParamDetails(data.taskId); |
| | | await formApi.setValues({ ...data, checkFlag }); |
| | | originalSnapshot.value = JSON.stringify(await getAlignedData()); |
| | | } |
| | | |
| | | // 监听任务变更 |
| | | // 监听任务变更:任务对象变化(选择任务 / 列表刷新后同步的最新数据)时重置表单, |
| | | // 确保"可报工数量"等任务派生字段始终展示最新值 |
| | | watch( |
| | | () => props.task, |
| | | (task) => { |
| | | if (task) { |
| | | resetForTask(task); |
| | | () => { |
| | | if (props.task) { |
| | | resetForTask(props.task); |
| | | } |
| | | }, |
| | | { immediate: true }, |
| | |
| | | |
| | | <template> |
| | | <div class="feedback-tab"> |
| | | <BatchInputDialog ref="batchDialogRef" @confirm="handleBatchConfirm" /> |
| | | <div v-if="!task" class="feedback-tab__empty"> |
| | | 请从左侧工单列表选择一条任务 |
| | | </div> |
| | |
| | | |
| | | <Form class="feedback-tab__form" /> |
| | | |
| | | <!-- 工艺参数(任务绑定参数时展示,必填参数未填会被后端拦截) --> |
| | | <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 |
| | | v-if="isEditable" |
| | |
| | | @click="handleSave" |
| | | > |
| | | 保存 |
| | | </Button> |
| | | <Button |
| | | v-if="isEditable" |
| | | type="primary" |
| | | @click="handleSaveAndSubmit" |
| | | > |
| | | 保存并提交 |
| | | </Button> |
| | | <Popconfirm |
| | | v-if="canSubmit" |
| | |
| | | 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> |