<script lang="ts" setup>
|
import type { PendingTask } from '../../../types';
|
import type { FormType } from '../../../pro/feedback/data';
|
|
import { computed, ref, watch } from 'vue';
|
|
import {
|
MesAutoCodeRuleCode,
|
MesProFeedbackStatusEnum,
|
} from '@vben/constants';
|
import { useUserStore } from '@vben/stores';
|
|
import { Button, message, Popconfirm } from 'ant-design-vue';
|
|
import { useVbenForm } from '#/adapter/form';
|
import { generateAutoCode } from '#/api/mes/md/autocode/record';
|
import {
|
approveFeedback,
|
createFeedback,
|
getFeedback,
|
rejectFeedback,
|
submitFeedback,
|
updateFeedback,
|
} from '#/api/mes/pro/feedback';
|
import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process';
|
import { $t } from '#/locales';
|
|
import { useFormSchema } from '../../../pro/feedback/data';
|
|
defineOptions({ name: 'WorkbenchFeedbackTab' });
|
|
const props = defineProps<{
|
task?: PendingTask | null;
|
/** 外部触发刷新(如报工成功后刷新列表) */
|
refreshKey?: number;
|
}>();
|
|
const emit = defineEmits<{
|
reported: [];
|
}>();
|
|
const userStore = useUserStore();
|
const formType = ref<FormType>('create');
|
const feedbackId = ref<number>();
|
const feedbackStatus = ref<number>();
|
const originalSnapshot = ref('');
|
|
const isEditable = computed(() =>
|
['create', 'submit', 'update'].includes(formType.value),
|
);
|
const canSubmit = computed(
|
() =>
|
isEditable.value &&
|
feedbackStatus.value === MesProFeedbackStatusEnum.PREPARE,
|
);
|
const canApprove = computed(() => formType.value === 'approve');
|
|
const [Form, formApi] = useVbenForm({
|
commonConfig: {
|
componentProps: { class: 'w-full' },
|
formItemClass: 'col-span-1',
|
labelWidth: 110,
|
},
|
layout: 'horizontal',
|
schema: [],
|
showDefaultActions: false,
|
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;
|
}
|
}
|
|
async function getAlignedData() {
|
const data = (await formApi.getValues()) as any;
|
alignQuantity(data);
|
return data;
|
}
|
|
/** 加载 checkFlag */
|
async function resolveCheckFlag(routeId?: number, processId?: number) {
|
if (!routeId || !processId) return true;
|
try {
|
const rp = await getRouteProcessByRouteAndProcess(routeId, processId);
|
return rp?.checkFlag ?? false;
|
} catch {
|
return true;
|
}
|
}
|
|
/** 任务变更时重置表单 */
|
async function resetForTask(task: PendingTask) {
|
formType.value = 'create';
|
feedbackId.value = undefined;
|
feedbackStatus.value = undefined;
|
originalSnapshot.value = '';
|
|
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
|
|
const code = await generateAutoCode(MesAutoCodeRuleCode.PRO_FEEDBACK_CODE);
|
const checkFlag = await resolveCheckFlag(task.routeId, task.processId);
|
|
await formApi.setValues({
|
code,
|
type: 1,
|
feedbackTime: Date.now(),
|
feedbackUserId: userStore.userInfo?.id,
|
taskId: task.id,
|
workOrderId: task.workOrderId,
|
routeId: task.routeId,
|
processId: task.processId,
|
workstationId: task.workstationId,
|
itemId: task.itemId,
|
itemCode: task.itemCode,
|
itemName: task.itemName,
|
itemSpecification: task.itemSpecification,
|
unitMeasureName: task.unitMeasureName,
|
scheduledQuantity: task.quantity,
|
checkFlag,
|
});
|
|
originalSnapshot.value = JSON.stringify(await getAlignedData());
|
}
|
|
/** 保存 */
|
async function handleSave() {
|
const { valid } = await formApi.validate();
|
if (!valid) return;
|
|
const data = await getAlignedData();
|
|
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);
|
message.success($t('common.createSuccess'));
|
} else {
|
await updateFeedback(data);
|
message.success($t('common.updateSuccess'));
|
}
|
originalSnapshot.value = JSON.stringify(data);
|
emit('reported');
|
}
|
|
/** 提交 */
|
async function handleSubmit() {
|
const { valid } = await formApi.validate();
|
if (!valid) return;
|
|
const data = await getAlignedData();
|
if (JSON.stringify(data) !== originalSnapshot.value) {
|
await updateFeedback(data);
|
originalSnapshot.value = JSON.stringify(data);
|
}
|
await submitFeedback(feedbackId.value!);
|
message.success('报订单已提交');
|
// 重置表单为新建模式
|
if (props.task) {
|
await resetForTask(props.task);
|
}
|
emit('reported');
|
}
|
|
/** 审批通过 */
|
async function handleApprove() {
|
if (!feedbackId.value) return;
|
const finished = await approveFeedback(feedbackId.value);
|
message.success(
|
finished ? '报订单已审批完成' : '报工成功,请等待质量检验完成!',
|
);
|
if (props.task) {
|
await resetForTask(props.task);
|
}
|
emit('reported');
|
}
|
|
/** 审批驳回 */
|
async function handleReject() {
|
if (!feedbackId.value) return;
|
await rejectFeedback(feedbackId.value);
|
message.success('报订单已驳回');
|
if (props.task) {
|
await resetForTask(props.task);
|
}
|
emit('reported');
|
}
|
|
/** 加载已有报工单进行编辑 */
|
async function loadFeedback(id: number) {
|
const data = await getFeedback(id);
|
feedbackId.value = data.id;
|
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 });
|
originalSnapshot.value = JSON.stringify(await getAlignedData());
|
}
|
|
// 监听任务变更(仅当任务 ID 真正变化时才重置表单)
|
watch(
|
() => props.task?.id,
|
() => {
|
if (props.task) {
|
resetForTask(props.task);
|
}
|
},
|
{ immediate: true },
|
);
|
|
// 暴露方法供外部调用
|
defineExpose({ loadFeedback, handleSave });
|
</script>
|
|
<template>
|
<div class="feedback-tab">
|
<div v-if="!task" class="feedback-tab__empty">
|
请从左侧工单列表选择一条任务
|
</div>
|
<template v-else>
|
<div class="feedback-tab__header">
|
<h3 class="feedback-tab__title">
|
{{ formType === 'create' ? '新建报工' : '编辑报工' }}
|
</h3>
|
<span v-if="task.workOrderCode" class="feedback-tab__sub">
|
工单: {{ task.workOrderCode }} {{ task.workOrderName }}
|
</span>
|
</div>
|
|
<Form class="feedback-tab__form" />
|
|
<div class="feedback-tab__actions">
|
<Button
|
v-if="isEditable"
|
type="primary"
|
@click="handleSave"
|
>
|
保存
|
</Button>
|
<Popconfirm
|
v-if="canSubmit"
|
title="确认提交该报订单?提交后将不能修改。"
|
@confirm="handleSubmit"
|
>
|
<Button type="primary">{{ $t('common.submit') }}</Button>
|
</Popconfirm>
|
<Popconfirm
|
v-if="canApprove"
|
title="确认审批通过该报订单?"
|
@confirm="handleApprove"
|
>
|
<Button type="primary">通过</Button>
|
</Popconfirm>
|
<Popconfirm
|
v-if="canApprove"
|
title="确认驳回该报订单?"
|
@confirm="handleReject"
|
>
|
<Button danger>不通过</Button>
|
</Popconfirm>
|
</div>
|
</template>
|
</div>
|
</template>
|
|
<style lang="scss" scoped>
|
.feedback-tab {
|
padding: 20px 24px;
|
min-height: 100%;
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
overflow: auto;
|
}
|
|
.feedback-tab__empty {
|
padding: 64px 0;
|
text-align: center;
|
color: #8c8c8c;
|
font-size: 16px;
|
}
|
|
.feedback-tab__header {
|
display: flex;
|
align-items: baseline;
|
gap: 16px;
|
margin-bottom: 20px;
|
padding-bottom: 16px;
|
border-bottom: 1px solid #f0f0f0;
|
}
|
|
.feedback-tab__title {
|
margin: 0;
|
font-size: 18px;
|
font-weight: 600;
|
color: #1a1a1a;
|
}
|
|
.feedback-tab__sub {
|
font-size: 14px;
|
color: #8c8c8c;
|
}
|
|
.feedback-tab__form {
|
flex: 1;
|
}
|
|
.feedback-tab__actions {
|
display: flex;
|
gap: 12px;
|
justify-content: flex-end;
|
padding-top: 16px;
|
border-top: 1px solid #f0f0f0;
|
margin-top: 16px;
|
}
|
</style>
|