4 小时以前 5372fa6dfe5c16163880de9f899578af35671e78
Merge remote-tracking branch 'origin/dev_pro2.0' into dev_pro2.0
已修改13个文件
174 ■■■■ 文件已修改
src/api/mes/dv/machinery/index.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/document-audit/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/task/index.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/customer/detail/index.vue 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/customer/pool/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/machinery/data.ts 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/detail/index.vue 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/document-audit-list.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/feedback/index.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/data.ts 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder-center/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/index.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/dv/machinery/index.ts
@@ -17,6 +17,10 @@
    status?: number; // 设备状态
    lastMaintenTime?: Date; // 最近保养时间
    lastCheckTime?: Date; // 最近点检时间
    productionEfficiency?: number; // 生产效率
    conveyorBelt?: number; // 传送履带数量
    workstationId?: number; // 工作站编号
    workstationName?: string; // 工作站名称
    remark?: string; // 备注
    createTime?: Date; // 创建时间
  }
src/api/mes/pd/document-audit/index.ts
@@ -29,6 +29,7 @@
  }
  export interface PageParams extends PageParam {
    projectId?: number;
    auditNo?: string;
    documentId?: number;
    documentName?: string;
src/api/mes/pro/task/index.ts
@@ -166,3 +166,11 @@
    { params },
  );
}
/** 根据工作站和数量计算生产时长 */
export function calculateTaskDuration(workstationId: number, quantity: number) {
  return requestClient.get<{ duration: number }>(
    '/mes/pro/task/calculate-duration',
    { params: { workstationId, quantity } },
  );
}
src/views/crm/customer/detail/index.vue
@@ -133,18 +133,18 @@
}
/** 客户放入公海 */
async function handlePutPool(): Promise<boolean | undefined> {
  try {
    await confirm(`确定将客户【${customer.value.name}】放入公海吗?`);
  } catch {
    return false;
  }
  // 放入公海
  await putCustomerPool(customerId.value);
  // 提示并返回成功
  message.success('放入公海成功');
  return true;
}
// async function handlePutPool(): Promise<boolean | undefined> {
//   try {
//     await confirm(`确定将客户【${customer.value.name}】放入公海吗?`);
//   } catch {
//     return false;
//   }
//   // 放入公海
//   await putCustomerPool(customerId.value);
//   // 提示并返回成功
//   message.success('放入公海成功');
//   return true;
// }
/** 更新成交状态操作 */
async function handleUpdateDealStatus(): Promise<boolean | undefined> {
@@ -229,13 +229,13 @@
            ifShow: !customer.ownerUserId,
            onClick: handleDistributeForm,
          },
          {
            label: '放入公海',
            type: 'default',
            ifShow:
              !!customer.ownerUserId && permissionListRef?.validateOwnerUser,
            onClick: handlePutPool,
          },
          // {
          //   label: '放入公海',
          //   type: 'default',
          //   ifShow:
          //     !!customer.ownerUserId && permissionListRef?.validateOwnerUser,
          //   onClick: handlePutPool,
          // },
        ]"
      />
    </template>
src/views/crm/customer/pool/index.vue
@@ -43,6 +43,7 @@
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            pool: true,
            sceneType: 4,
            ...formValues,
          });
        },
src/views/mes/dv/machinery/data.ts
@@ -113,6 +113,29 @@
      rules: z.number().default(MesDvMachineryStatusEnum.STOP),
    },
    {
      fieldName: 'productionEfficiency',
      label: '生产效率',
      component: 'InputNumber',
      componentProps: {
        class: '!w-full',
        min: 0,
        precision: 2,
        placeholder: '请输入生产效率(每小时)',
      },
    },
    {
      fieldName: 'conveyorBelt',
      label: '传送履带',
      component: 'InputNumber',
      componentProps: {
        class: '!w-full',
        min: 0,
        precision: 0,
        placeholder: '请输入传送履带数量',
      },
      help: '2代表双倍效率',
    },
    {
      fieldName: 'specification',
      label: '规格型号',
      component: 'Input',
@@ -233,6 +256,8 @@
        props: { type: DICT_TYPE.MES_DV_MACHINERY_STATUS },
      },
    },
    { field: 'productionEfficiency', title: '生产效率(/h)', width: 120 },
    { field: 'conveyorBelt', title: '传送履带', width: 100 },
    {
      field: 'lastCheckTime',
      title: '最近点检时间',
src/views/mes/pd/project/detail/index.vue
@@ -59,7 +59,7 @@
);
const materialFormType = computed<FormType>(() =>
  isPendingPublish.value || isPendingUpload.value || isPendingAudit.value ? 'update' : 'detail',
  isPendingPublish.value || isPendingUpload.value ? 'update' : 'detail',
);
const [Descriptions] = useDescription({
@@ -169,35 +169,30 @@
            label: $t('common.edit'),
            type: 'primary',
            icon: ACTION_ICON.EDIT,
            auth: ['mes:pd-project:update'],
            ifShow: isFormEditable,
            onClick: handleEdit,
          },
          {
            label: '发布',
            type: 'primary',
            auth: ['mes:pd-project:update'],
            ifShow: isPendingPublish,
            onClick: handlePublish,
          },
          {
            label: '确认上传',
            type: 'primary',
            auth: ['mes:pd-project:update'],
            ifShow: isPendingUpload,
            onClick: handleUpload,
          },
          {
            label: '审核通过',
            type: 'primary',
            auth: ['mes:pd-project:audit'],
            ifShow: isPendingAudit,
            onClick: handleAudit,
          },
          {
            label: '归档',
            type: 'default',
            auth: ['mes:pd-project:archive'],
            ifShow: isPendingArchive,
            onClick: handleArchive,
          },
@@ -223,7 +218,7 @@
        <Tabs.TabPane key="workflow" tab="流程进度" :force-render="true">
          <WorkflowTimeline :status="project.status ?? 0" />
        </Tabs.TabPane>
        <Tabs.TabPane v-if="!isPendingAudit" key="material" tab="设计资料" :force-render="true">
        <Tabs.TabPane v-if="!isPendingAudit" key="material" tab="设计资料">
          <p v-if="isPendingPublish" class="mb-3 text-sm text-amber-600">
            请先发布任务后再上传设计资料
          </p>
@@ -233,7 +228,7 @@
            :project-status="project.status"
          />
        </Tabs.TabPane>
        <Tabs.TabPane v-if="isPendingAudit" key="audit" tab="资料审核" :force-render="true">
        <Tabs.TabPane v-if="isPendingAudit" key="audit" tab="资料审核">
          <DocumentAuditList :project-id="projectId" @success="loadProjectDetail" />
        </Tabs.TabPane>
      </Tabs>
src/views/mes/pd/project/modules/document-audit-list.vue
@@ -63,6 +63,7 @@
          return await getPdDocumentAuditPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            projectId: props.projectId,
            ...formValues,
          });
        },
@@ -84,7 +85,7 @@
            label: '审核',
            type: 'link',
            icon: ACTION_ICON.EDIT,
            auth: ['mes:pd-document-audit:update'],
            ifShow: row.auditStatus === 0,
            onClick: handleAudit.bind(null, row),
          },
        ]"
src/views/mes/pro/feedback/index.vue
@@ -17,10 +17,13 @@
} from '#/api/mes/pro/feedback';
import { $t } from '#/locales';
import { ref } from 'vue';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
import QuickFeedbackForm from '../workorder-center/modules/quick-feedback.vue';
import BatchFeedbackForm from './modules/batch-feedback.vue';
import ArchiveDetail from '#/views/mes/pd/archive/modules/detail.vue';
const userStore = useUserStore();
const currentUserId = userStore.userInfo?.id; // 当前登录用户 ID,用于审批人权限判断
@@ -39,6 +42,8 @@
  connectedComponent: BatchFeedbackForm,
  destroyOnClose: true,
});
const archiveDetailRef = ref<InstanceType<typeof ArchiveDetail>>();
/** 刷新表格 */
function handleRefresh() {
@@ -205,9 +210,15 @@
                confirm: handleDelete.bind(null, row),
              },
            },
            {
              label: '归档',
              type: 'link',
              onClick: () => archiveDetailRef.value?.open(row.itemCode),
            },
          ]"
        />
      </template>
    </Grid>
    <ArchiveDetail ref="archiveDetailRef" />
  </Page>
</template>
src/views/mes/pro/task/data.ts
@@ -10,7 +10,10 @@
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { message } from 'ant-design-vue';
import CrmCustomerSelect from '#/components/crm-customer-select.vue';
import { calculateTaskDuration } from '#/api/mes/pro/task';
import { getRangePickerDefaultProps } from '#/utils';
import { MdItemSelect } from '#/views/mes/md/item/components';
import { MdWorkstationSelect } from '#/views/mes/md/workstation/components';
@@ -386,6 +389,7 @@
      component: markRaw(MdWorkstationSelect),
      componentProps: {
        placeholder: '请选择工作站',
        onChange: () => recalcDuration(formApi),
      },
      rules: 'selectRequired',
    },
@@ -399,6 +403,7 @@
        min: 0.01,
        placeholder: '请输入排产数量',
        precision: 2,
        onChange: () => recalcDuration(formApi),
      },
      rules: 'required',
    },
@@ -413,11 +418,17 @@
      component: 'DatePicker',
      componentProps: {
        class: '!w-full',
        placeholder: '请选择开始时间',
        placeholder: '请先选择工作站',
        showTime: true,
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
        // 开始时间变更:重新计算结束时间
        onChange: () => recalcEndTime(formApi),
      },
      dependencies: {
        triggerFields: ['workstationId'],
        componentProps: (values) => ({
          disabled: !values.workstationId,
          placeholder: values.workstationId ? '请选择开始时间' : '请先选择工作站',
        }),
      },
      rules: 'required',
    },
@@ -459,6 +470,24 @@
  ];
}
/** 选择工作站或修改数量后,自动计算生产时长 */
async function recalcDuration(formApi?: VbenFormApi) {
  if (!formApi) return;
  const values = await formApi.getValues();
  if (!values.workstationId || !values.quantity) return;
  try {
    const res = await calculateTaskDuration(values.workstationId, values.quantity);
    await formApi.setFieldValue('duration', res.duration);
    if (res.duration === 0) {
      message.warning('该工作站未绑定设备,或绑定设备未配置生产效率');
    }
    // 重新计算结束时间
    await recalcEndTime(formApi);
  } catch {
    // 计算失败不阻塞用户操作
  }
}
/** 计算结束时间:开始时间 + 生产时长(小时) */
async function recalcEndTime(formApi?: VbenFormApi) {
  if (!formApi) {
@@ -466,11 +495,12 @@
  }
  const values = await formApi.getValues();
  if (values.startTime && values.duration) {
    const start = Number(values.startTime);
    await formApi.setFieldValue(
      'endTime',
      start + values.duration * 3600 * 1000,
    );
    const start = new Date(values.startTime).getTime();
    const endTime = start + values.duration * 3600 * 1000;
    const pad = (n: number) => String(n).padStart(2, '0');
    const d = new Date(endTime);
    const formatted = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
    await formApi.setFieldValue('endTime', formatted);
  }
}
src/views/mes/pro/task/index.vue
@@ -17,6 +17,7 @@
import { getGanttTaskList, previewAutoGenerate, autoGenerateTasks } 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';
@@ -24,6 +25,7 @@
const router = useRouter();
const ganttTasks = ref<any[]>([]); // 甘特图预览数据
const archiveDetailRef = ref<InstanceType<typeof ArchiveDetail>>();
const [ScheduleModal, scheduleModalApi] = useVbenModal({
  connectedComponent: ScheduleForm,
@@ -166,9 +168,15 @@
              ifShow: row.status === MesProWorkOrderStatusEnum.CONFIRMED,
              onClick: handleSchedule.bind(null, row),
            },
            {
              label: '归档',
              type: 'link',
              onClick: () => archiveDetailRef.value?.open(row.productCode),
            },
          ]"
        />
      </template>
    </Grid>
    <ArchiveDetail ref="archiveDetailRef" />
  </Page>
</template>
src/views/mes/pro/workorder-center/index.vue
@@ -18,11 +18,13 @@
} from '#/api/mes/pro/workorder';
import { previewAutoGenerate, autoGenerateTasks } from '#/api/mes/pro/task';
import ArchiveDetail from '#/views/mes/pd/archive/modules/detail.vue';
import { useGridColumns, useGridFormSchema, useTaskGridColumns } from './data';
import QuickFeedbackForm from './modules/quick-feedback.vue';
import SchedulePreviewModal from './modules/schedule-preview.vue';
defineOptions({ name: 'WorkorderCenter' });
const archiveDetailRef = ref<InstanceType<typeof ArchiveDetail>>();
const router = useRouter();
@@ -207,6 +209,11 @@
                confirm: handleScheduleExecute.bind(null, row),
              },
            },
            {
              label: '归档',
              type: 'link',
              onClick: () => archiveDetailRef.value?.open(row.productCode),
            },
          ]"
        />
      </template>
@@ -309,5 +316,6 @@
        </Collapse.Panel>
      </Collapse>
    </Card>
    <ArchiveDetail ref="archiveDetailRef" />
  </Page>
</template>
src/views/mes/pro/workorder/index.vue
@@ -22,7 +22,8 @@
    getWorkOrderPage,
  } from "#/api/mes/pro/workorder";
  import { $t } from "#/locales";
  import { BarcodeDetail } from "#/views/wls/barcode/components";
  import ArchiveDetail from '#/views/mes/pd/archive/modules/detail.vue';
import { BarcodeDetail } from '#/views/wls/barcode/components';
  import { useGridColumns, useGridFormSchema } from "./data";
  import Form from "./modules/form.vue";
@@ -32,7 +33,8 @@
    destroyOnClose: true,
  });
  const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // 条码详情弹窗
  const archiveDetailRef = ref<InstanceType<typeof ArchiveDetail>>();
const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // 条码详情弹窗
  /** 刷新表格 */
  function handleRefresh() {
@@ -225,9 +227,15 @@
              auth: ['mes:pro-work-order:query'],
              onClick: handleBarcode.bind(null, row),
            },
            {
              label: '归档',
              type: 'link',
              onClick: () => archiveDetailRef.value?.open(row.productCode),
            },
          ]" />
      </template>
    </Grid>
    <BarcodeDetail ref="barcodeDetailRef" />
    <ArchiveDetail ref="archiveDetailRef" />
  </Page>
</template>