13 小时以前 c77086ad2d7949ab36dd0c4e0b59907e672dd64f
src/views/mes/pro/task/index.vue
@@ -1,42 +1,40 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
import {getWorkOrderSchedulePage, type MesProWorkOrderApi} from '#/api/mes/pro/workorder';
import { onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui';
import {
  MesProWorkOrderProductionStatusEnum,
  MesProWorkOrderStatusEnum,
  MesProWorkOrderTypeEnum,
} from '@vben/constants';
import { Button, Card, message } from 'ant-design-vue';
import { Button, Card, Progress, Tabs } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getGanttTaskList, previewAutoGenerate, autoGenerateTasks } from '#/api/mes/pro/task';
import { getGanttTaskList } from '#/api/mes/pro/task';
import { getWorkOrderPage } from '#/api/mes/pro/workorder';
import ArchiveDetail from '#/views/mes/pd/archive/modules/detail.vue';
import { GanttChart } from './components';
import { useGridColumns, useGridFormSchema } from './data';
import ScheduleForm from './modules/schedule-form.vue';
import SchedulePreview from '../workorder-center/modules/schedule-preview.vue';
const router = useRouter();
const ganttTasks = ref<any[]>([]); // 甘特图预览数据
const archiveVisible = ref(false);
const archiveProductCode = ref('');
const activeTab = ref('PENDING'); // 默认选中待排产
const [ScheduleModal, scheduleModalApi] = useVbenModal({
  connectedComponent: ScheduleForm,
  destroyOnClose: true,
});
const [SchedulePreviewModal, schedulePreviewModalApi] = useVbenModal({
  connectedComponent: SchedulePreview,
  destroyOnClose: true,
});
/** 刷新表格 */
function handleRefresh() {
@@ -63,28 +61,6 @@
}
/** 一键排产预览 */
async function handleAutoSchedulePreview(row: MesProWorkOrderApi.WorkOrder) {
  try {
    const preview = await previewAutoGenerate({ workOrderId: row.id! });
    schedulePreviewModalApi
      .setData({ workOrder: row, preview })
      .open();
  } catch {
    message.error('获取排产预览失败');
  }
}
/** 一键排产执行 */
async function handleAutoSchedule(row: MesProWorkOrderApi.WorkOrder) {
  try {
    const result = await autoGenerateTasks({ workOrderId: row.id! });
    message.success(`排产成功,生成 ${result.taskIds?.length || 0} 个任务`);
    handleRefresh();
  } catch {
    message.error('排产失败');
  }
}
/** 打开归档详情 */
function handleArchive(row: MesProWorkOrderApi.WorkOrder) {
  archiveProductCode.value = row.productCode || '';
@@ -94,6 +70,30 @@
/** 打开甘特图编辑页面 */
function handleGanttEdit() {
  router.push({ name: 'MesProTaskGanttEdit' });
}
/** 判断订单是否为“已完成类型”:订单状态=已完成 或 生产状态=生产完成 */
function isCompletedWorkOrder(row: MesProWorkOrderApi.WorkOrder): boolean {
  return (
    row.status === MesProWorkOrderStatusEnum.FINISHED ||
    row.productionStatus === MesProWorkOrderProductionStatusEnum.FINISHED
  );
}
/**
 * 已完成类型行的“完成态”兜底展示:
 * 已生产数量按订单数量展示(若低于订单数量),使完成进度显示为 100%。
 * 仅作用于本页列表展示,不修改后端数据,不影响库存/报工/导出等真实口径。
 */
function normalizeCompletionDisplay(row: MesProWorkOrderApi.WorkOrder): void {
  if (!isCompletedWorkOrder(row)) {
    return;
  }
  const quantity = row.quantity ?? 0;
  const produced = row.quantityProduced ?? 0;
  if (produced < quantity) {
    row.quantityProduced = quantity;
  }
}
const [Grid, gridApi] = useVbenVxeGrid({
@@ -106,13 +106,27 @@
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          return await getWorkOrderPage({
          let scheduleStatus: number | undefined = undefined;
          if (activeTab.value === 'PENDING') {
            scheduleStatus = 0; // 未完成
          } else if (activeTab.value === 'FINISHED') {
            scheduleStatus = 1; // 已完成
          }
          // activeTab === 'ALL' 时不传
          const res = await getWorkOrderSchedulePage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            status: MesProWorkOrderStatusEnum.CONFIRMED,
            scheduleStatus,
            type: MesProWorkOrderTypeEnum.SELF,
            ...formValues,
          });
          // 已完成类型行:已生产数量/完成进度做“完成态”兜底展示(仅本页展示,不改库)
          if (res?.list) {
            res.list.forEach(normalizeCompletionDisplay);
          }
          return res;
        },
      },
    },
@@ -140,14 +154,25 @@
<template>
  <Page auto-content-height>
    <ScheduleModal @success="handleRefresh" />
    <SchedulePreviewModal @success="handleRefresh" />
    <!-- 排产甘特图预览 -->
    <Card title="排产甘特图" class="mb-4">
      <template #extra>
        <span class="text-xs text-gray-400">展示已确认自制订单的任务计划</span>
      </template>
      <GanttChart :height="350" :readonly="true" :tasks="ganttTasks" />
    </Card>
    <Grid table-title="待排产订单">
    <Grid>
      <template #table-title>
        <div class="flex flex-col gap-2">
          <span class="text-base font-medium">生产订单列表</span>
          <Tabs v-model:activeKey="activeTab" @change="handleRefresh" :tabBarStyle="{ margin: 0, borderBottom: 0 }">
            <Tabs.TabPane key="ALL" tab="全部" />
            <Tabs.TabPane key="PENDING" tab="未完成" />
            <Tabs.TabPane key="FINISHED" tab="已完成" />
          </Tabs>
        </div>
      </template>
      <template #toolbar-tools>
        <TableAction
          :actions="[
@@ -165,14 +190,22 @@
          {{ row.code }}
        </Button>
      </template>
      <template #progressPercent="{ row }">
        <Progress
          :percent="Math.min(100, Math.round((row.quantity ? ((row.quantityProduced || 0) / row.quantity) * 100 : 0)))"
          size="small"
        />
      </template>
      <template #actions="{ row }">
        <TableAction
          :actions="[
            {
              label: '排产',
              label: '排产维护',
              type: 'link',
              auth: ['mes:pro-task:create'],
              ifShow: row.status === MesProWorkOrderStatusEnum.CONFIRMED,
              ifShow:
                row.status === MesProWorkOrderStatusEnum.CONFIRMED &&
                !isCompletedWorkOrder(row),
              onClick: handleSchedule.bind(null, row),
            },
            {