云
3 天以前 e1e28c3b0a9520932d5c3e0234e00294c0233d4c
feat(product-issue): 支持按任务工序校验和选择领料物料

- 修改物料不在BOM中的错误提示信息以包含任务工序
- 新增根据任务ID获取工序投入物料的后端接口
- 领料单行保存时增加基于任务工序BOM的物料校验逻辑
- 前端领料单行表单物料选择改为基于当前工序投入物料
- 新增前端获取任务工序投入物料的API请求方法
- 移除排产表单中多余的完成订单按钮及相关逻辑
已修改3个文件
85 ■■■■■ 文件已修改
src/api/mes/pro/workorder/bom/index.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/modules/schedule-form.vue 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/workbench/components/tabs/ProductIssueLineFormModal.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/workorder/bom/index.ts
@@ -60,3 +60,11 @@
    `/mes/pro/work-order-bom/item-list-by-work-order-id?workOrderId=${workOrderId}`,
  );
}
/** 查询生产任务所在工序的投入物料 */
export function getWorkOrderBomListByTaskId(taskId: number) {
  return requestClient.get<MesProWorkOrderBomApi.WorkOrderBom[]>(
    '/mes/pro/work-order-bom/list-by-task-id',
    { params: { taskId } },
  );
}
src/views/mes/pro/task/modules/schedule-form.vue
@@ -6,17 +6,16 @@
import { useVbenModal } from '@vben/common-ui';
import { Button, Card, message, Popconfirm, Steps } from 'ant-design-vue';
import { Card, message, Steps } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { getRouteProcessListByProduct } from '#/api/mes/pro/route/process';
import { finishWorkOrder, getWorkOrder } from '#/api/mes/pro/workorder';
import { getWorkOrder } from '#/api/mes/pro/workorder';
import { getWorkOrderProcessListByWorkOrderId } from '#/api/mes/pro/workorder/process';
import { useScheduleFormSchema } from '../data';
import TaskList from './task-list.vue';
const emit = defineEmits(['success']);
const formType = ref<'detail' | 'schedule'>('schedule');
const workOrder = ref<MesProWorkOrderApi.WorkOrder>(); // 当前订单详情
/** 排产工序列表 */
@@ -63,22 +62,6 @@
  routeProcessList.value = [...woProcesses].toSorted(
    (a, b) => (a.sort ?? 0) - (b.sort ?? 0),
  );
}
/** 完成订单 */
async function handleFinish() {
  if (!workOrder.value?.id) {
    return;
  }
  modalApi.lock();
  try {
    await finishWorkOrder(workOrder.value.id);
    message.success('订单已完成');
    await modalApi.close();
    emit('success');
  } finally {
    modalApi.unlock();
  }
}
const [Modal, modalApi] = useVbenModal({
@@ -150,22 +133,6 @@
          />
        </Card>
      </template>
    </template>
    <template #prepend-footer>
      <div class="flex flex-auto items-center gap-2">
        <template v-if="formType === 'schedule'">
          <Popconfirm
            v-if="routeProcessList.length > 0"
            title="确认要完成该订单吗?完成后订单下所有任务将标记为已完成。"
            @confirm="handleFinish"
          >
            <Button type="primary">完成</Button>
          </Popconfirm>
          <span v-else class="text-gray-400 text-sm">
            当前订单未配置工序,请先在工单中维护工序信息后再完成
          </span>
        </template>
      </div>
    </template>
  </Modal>
</template>
src/views/mes/workbench/components/tabs/ProductIssueLineFormModal.vue
@@ -2,19 +2,20 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { MesWmProductIssueLineApi } from '#/api/mes/wm/productissue/line';
import { computed, markRaw, ref } from 'vue';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { getWorkOrderBomListByTaskId } from '#/api/mes/pro/workorder/bom';
import { getProductIssue } from '#/api/mes/wm/productissue';
import {
  createProductIssueLine,
  getProductIssueLine,
  updateProductIssueLine,
} from '#/api/mes/wm/productissue/line';
import { MdItemSelect } from '#/views/mes/md/item/components';
const emit = defineEmits(['success']);
const formData = ref<MesWmProductIssueLineApi.ProductIssueLine>();
@@ -25,13 +26,18 @@
});
/** 物料行表单 schema */
function buildLineFormSchema(): VbenFormSchema[] {
function buildLineFormSchema(options: { label: string; value: number }[] = []): VbenFormSchema[] {
  return [
    {
      fieldName: 'itemId',
      label: '产品物料',
      component: markRaw(MdItemSelect),
      componentProps: { placeholder: '请选择产品物料' },
      component: 'Select',
      componentProps: {
        options,
        showSearch: true,
        optionFilterProp: 'label',
        placeholder: options.length ? '请选择当前工序物料' : '请先维护当前工序投入物料',
      },
      rules: 'selectRequired',
    },
    {
@@ -91,14 +97,26 @@
    }
    const data = modalApi.getData<{ id?: number; issueId: number }>();
    issueId.value = data.issueId;
    if (!data.id) {
      await formApi.resetForm();
      return;
    }
    modalApi.lock();
    try {
      formData.value = await getProductIssueLine(data.id);
      await formApi.setValues(formData.value);
      const issue = await getProductIssue(data.issueId);
      const boms = issue.taskId ? await getWorkOrderBomListByTaskId(issue.taskId) : [];
      const optionMap = new Map<number, { label: string; value: number }>();
      for (const item of boms) {
        if (item.itemId != null) {
          optionMap.set(item.itemId, {
            value: item.itemId,
            label: `${item.itemCode ?? ''} ${item.itemName ?? ''}`.trim(),
          });
        }
      }
      const options = [...optionMap.values()];
      await formApi.updateSchema(buildLineFormSchema(options));
      await formApi.resetForm();
      if (data.id) {
        formData.value = await getProductIssueLine(data.id);
        await formApi.setValues(formData.value);
      }
    } finally {
      modalApi.unlock();
    }