spring
2026-07-03 2540d86f0dbd77f654f558c3bf9d5ddbe7422f16
fix: 完成设计项目和归档台账接口对接,功能联调
已添加4个文件
已修改84个文件
已删除6个文件
3966 ■■■■■ 文件已修改
src/api/mes/dv/repair/index.ts 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/dv/repair/line/index.ts 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/home/index.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/md/item/batchConfig/index.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/archive/index.ts 128 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/bom/index.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/document-audit/index.ts 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/document/index.ts 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/drawing-review/index.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/mock/enabled.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/mock/seed.ts 386 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/mock/store.ts 554 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/mock/utils.ts 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/project/index.ts 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/task/index.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/version-change/index.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pd/workflow/index.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/andon/record/index.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/card/index.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/feedback/index.ts 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/task/index.ts 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/task/issue/index.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/workorder/bom/index.ts 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/workorder/index.ts 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/qc/ipqc/index.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/qc/pendinginspect/index.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/batch/index.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/outsourceissue/index.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/outsourcereceipt/index.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/packages/line/index.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/productissue/index.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/productreceipt/index.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/returnissue/index.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/sn/index.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/packages/constants/src/biz-mes-enum.ts 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/packages/constants/src/dict-enum.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/repair/data.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/repair/index.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/repair/modules/form.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/home/chart-options.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/home/data.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/home/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/home/modules/alert-panel.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/home/modules/kpi-cards.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/home/modules/shortcuts.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/home/modules/work-order-chart.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/md/item/modules/item-batch-config-form.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/archive/data.ts 84 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/archive/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/archive/modules/detail.vue 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/data.ts 612 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/detail/data.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/detail/index.vue 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/index.vue 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/create-form.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/document-audit-form.vue 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/document-audit-list.vue 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/document-form.vue 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/document-list.vue 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/form.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/publish-form.vue 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/task-draft-banner.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/user-select.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/version-change/data.ts 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/workflow-action-form.vue 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/modules/workflow-timeline.vue 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/andon/record/data.ts 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/card/components/select.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/card/data.ts 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/feedback/data.ts 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/feedback/modules/form.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/components/gantt-chart.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/components/select-dialog.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/data.ts 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/modules/schedule-form.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/modules/task-form.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/components/select-dialog.vue 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/components/select.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/data.ts 84 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/index.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/modules/bom-form.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/modules/bom-list.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/modules/form.vue 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/qc/batchtrace/data.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/qc/ipqc/data.ts 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/wm/barcode/data.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/wm/batch/data.ts 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/wm/outsourceissue/data.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/wm/outsourcereceipt/data.ts 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/wm/packages/data.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/wm/productissue/data.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/wm/productreceipt/data.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/wm/returnissue/data.ts 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/dv/repair/index.ts
@@ -1,13 +1,13 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
export namespace MesDvRepairApi {
  /** MES ç»´ä¿®å·¥å• */
  /** MES ç»´ä¿®è®¢å• */
  export interface Repair {
    id?: number; // å·¥å•编号
    code?: string; // ç»´ä¿®å·¥å•编码
    name?: string; // ç»´ä¿®å·¥å•名称
    id?: number; // è®¢å•编号
    code?: string; // ç»´ä¿®è®¢å•编码
    name?: string; // ç»´ä¿®è®¢å•名称
    machineryId?: number; // è®¾å¤‡ç¼–号
    machineryCode?: string; // è®¾å¤‡ç¼–码
    machineryName?: string; // è®¾å¤‡åç§°
@@ -30,7 +30,7 @@
  }
}
/** æŸ¥è¯¢ç»´ä¿®å·¥å•分页 */
/** æŸ¥è¯¢ç»´ä¿®è®¢å•分页 */
export function getRepairPage(params: PageParam) {
  return requestClient.get<PageResult<MesDvRepairApi.Repair>>(
    '/mes/dv/repair/page',
@@ -38,34 +38,34 @@
  );
}
/** æŸ¥è¯¢ç»´ä¿®å·¥å•详情 */
/** æŸ¥è¯¢ç»´ä¿®è®¢å•详情 */
export function getRepair(id: number) {
  return requestClient.get<MesDvRepairApi.Repair>(
    `/mes/dv/repair/get?id=${id}`,
  );
}
/** æ–°å¢žç»´ä¿®å·¥å• */
/** æ–°å¢žç»´ä¿®è®¢å• */
export function createRepair(data: MesDvRepairApi.Repair) {
  return requestClient.post<number>('/mes/dv/repair/create', data);
}
/** ä¿®æ”¹ç»´ä¿®å·¥å• */
/** ä¿®æ”¹ç»´ä¿®è®¢å• */
export function updateRepair(data: MesDvRepairApi.Repair) {
  return requestClient.put('/mes/dv/repair/update', data);
}
/** åˆ é™¤ç»´ä¿®å·¥å• */
/** åˆ é™¤ç»´ä¿®è®¢å• */
export function deleteRepair(id: number) {
  return requestClient.delete(`/mes/dv/repair/delete?id=${id}`);
}
/** å¯¼å‡ºç»´ä¿®å·¥å• */
/** å¯¼å‡ºç»´ä¿®è®¢å• */
export function exportRepair(params: any) {
  return requestClient.download('/mes/dv/repair/export-excel', { params });
}
/** æäº¤ç»´ä¿®å·¥å• */
/** æäº¤ç»´ä¿®è®¢å• */
export function submitRepair(id: number) {
  return requestClient.put(`/mes/dv/repair/submit?id=${id}`);
}
src/api/mes/dv/repair/line/index.ts
@@ -1,12 +1,12 @@
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
export namespace MesDvRepairLineApi {
  /** MES ç»´ä¿®å·¥å•行 */
  /** MES ç»´ä¿®è®¢å•行 */
  export interface RepairLine {
    id?: number; // æ˜Žç»†ç¼–号
    repairId?: number; // ç»´ä¿®å·¥å•编号
    repairId?: number; // ç»´ä¿®è®¢å•编号
    subjectId?: number; // é¡¹ç›®ç¼–号
    subjectName?: string; // é¡¹ç›®åç§°
    subjectContent?: string; // é¡¹ç›®å†…容
@@ -18,7 +18,7 @@
  }
}
/** æŸ¥è¯¢ç»´ä¿®å·¥å•行分页 */
/** æŸ¥è¯¢ç»´ä¿®è®¢å•行分页 */
export function getRepairLinePage(params: PageParam) {
  return requestClient.get<PageResult<MesDvRepairLineApi.RepairLine>>(
    '/mes/dv/repair-line/page',
@@ -26,24 +26,24 @@
  );
}
/** æŸ¥è¯¢ç»´ä¿®å·¥å•行详情 */
/** æŸ¥è¯¢ç»´ä¿®è®¢å•行详情 */
export function getRepairLine(id: number) {
  return requestClient.get<MesDvRepairLineApi.RepairLine>(
    `/mes/dv/repair-line/get?id=${id}`,
  );
}
/** æ–°å¢žç»´ä¿®å·¥å•行 */
/** æ–°å¢žç»´ä¿®è®¢å•行 */
export function createRepairLine(data: MesDvRepairLineApi.RepairLine) {
  return requestClient.post('/mes/dv/repair-line/create', data);
}
/** ä¿®æ”¹ç»´ä¿®å·¥å•行 */
/** ä¿®æ”¹ç»´ä¿®è®¢å•行 */
export function updateRepairLine(data: MesDvRepairLineApi.RepairLine) {
  return requestClient.put('/mes/dv/repair-line/update', data);
}
/** åˆ é™¤ç»´ä¿®å·¥å•行 */
/** åˆ é™¤ç»´ä¿®è®¢å•行 */
export function deleteRepairLine(id: number) {
  return requestClient.delete(`/mes/dv/repair-line/delete?id=${id}`);
}
src/api/mes/home/index.ts
@@ -1,11 +1,11 @@
import { requestClient } from '#/api/request';
import { requestClient } from '#/api/request';
export namespace MesHomeApi {
  /** MES é¦–页汇总统计 */
  export interface Summary {
    workOrderActiveCount: number; // è¿›è¡Œä¸­å·¥å•æ•°
    workOrderPrepareCount: number; // å¾…排产工单数
    workOrderFinishedCount: number; // å·²å®Œæˆå·¥å•æ•°
    workOrderActiveCount: number; // è¿›è¡Œä¸­è®¢å•æ•°
    workOrderPrepareCount: number; // å¾…排产订单数
    workOrderFinishedCount: number; // å·²å®Œæˆè®¢å•æ•°
    todayOutput: number; // ä»Šæ—¥äº§é‡
    yesterdayOutput: number; // æ˜¨æ—¥äº§é‡
    todayQualifiedQuantity: number; // ä»Šæ—¥åˆæ ¼å“æ•°
@@ -15,13 +15,13 @@
    machineryStop: number; // åœæœºè®¾å¤‡æ•°
    machineryMaintenance: number; // ç»´æŠ¤ä¸­è®¾å¤‡æ•°
    andonActiveCount: number; // æœªå¤„置安灯呼叫数
    repairActiveCount: number; // å¾…处理维修工单数
    repairActiveCount: number; // å¾…处理维修订单数
  }
  /** MES å·¥å•状态分布 */
  /** MES è®¢å•状态分布 */
  export interface WorkOrderStatus {
    status: number; // å·¥å•状态
    statusName: string; // å·¥å•状态名称
    status: number; // è®¢å•状态
    statusName: string; // è®¢å•状态名称
    count: number; // æ•°é‡
  }
@@ -39,7 +39,7 @@
  return requestClient.get<MesHomeApi.Summary>('/mes/home-statistics/summary');
}
/** èŽ·å¾—å·¥å•çŠ¶æ€åˆ†å¸ƒ */
/** èŽ·å¾—è®¢å•çŠ¶æ€åˆ†å¸ƒ */
export function getWorkOrderStatusDistribution() {
  return requestClient.get<MesHomeApi.WorkOrderStatus[]>(
    '/mes/home-statistics/work-order-status',
src/api/mes/md/item/batchConfig/index.ts
@@ -1,4 +1,4 @@
import { requestClient } from '#/api/request';
import { requestClient } from '#/api/request';
export namespace MesMdItemBatchConfigApi {
  /** MES ç‰©æ–™æ‰¹æ¬¡å±žæ€§é…ç½® */
@@ -12,7 +12,7 @@
    clientFlag?: boolean; // æ‰¹æ¬¡å±žæ€§-客户
    salesOrderCodeFlag?: boolean; // æ‰¹æ¬¡å±žæ€§-销售订单编号
    purchaseOrderCodeFlag?: boolean; // æ‰¹æ¬¡å±žæ€§-采购订单编号
    workorderFlag?: boolean; // æ‰¹æ¬¡å±žæ€§-生产工单
    workorderFlag?: boolean; // æ‰¹æ¬¡å±žæ€§-生产订单
    taskFlag?: boolean; // æ‰¹æ¬¡å±žæ€§-生产任务
    workstationFlag?: boolean; // æ‰¹æ¬¡å±žæ€§-工作站
    toolFlag?: boolean; // æ‰¹æ¬¡å±žæ€§-工具
src/api/mes/pd/archive/index.ts
@@ -2,61 +2,135 @@
import { requestClient } from '#/api/request';
import { isPdMockEnabled } from '../mock/enabled';
import { pdMockStore } from '../mock/store';
export namespace MesPdArchiveApi {
  /** æ•°å­—化设计归档台账 */
  /** å½’档台账(分页聚合) */
  export interface Archive {
    id?: number;
    code?: string;
    /** æœ€æ–°å½’档编号 */
    archiveCode?: string;
    /** è®¾è®¡é¡¹ç›®ID */
    projectId?: number;
    projectCode?: string;
    projectName?: string;
    /** ä»»åŠ¡ç¼–ç  */
    taskCode?: string;
    /** ä»»åŠ¡åç§° */
    taskName?: string;
    /** äº§å“ç¼–码 */
    productCode?: string;
    /** äº§å“åç§° */
    productName?: string;
    /** è¯¥é¡¹ç›®ä¸‹å½’档记录总数 */
    archiveCount?: number;
    /** å½’档人ID */
    archiver?: number;
    /** å½’档人名称 */
    archiverNickname?: string;
    /** å½’档状态(字典 mes_pd_archive_status) */
    archiveStatus?: number;
    /** å½’档时间 */
    archiveTime?: number;
    /** å¤‡æ³¨ */
    remark?: string;
    /** åˆ›å»ºæ—¶é—´ */
    createTime?: number;
    /** æ›´æ–°äººID */
    updater?: string;
    /** æ›´æ–°äººåç§° */
    updaterNickname?: string;
    /** æ›´æ–°æ—¶é—´ */
    updateTime?: number;
  }
  export interface PageParams extends PageParam {
    archiveCode?: string;
    projectId?: number;
    archiveStatus?: number;
    productCode?: string;
    productName?: string;
  }
  /** è¯¦æƒ… - é¡¹ç›®ä¿¡æ¯ */
  export interface ProjectInfo {
    id?: number;
    taskCode?: string;
    taskName?: string;
    productCode?: string;
    productName?: string;
    version?: string;
    archiveStatus?: number;
    archiveTime?: number;
    archiverId?: number;
    archiverName?: string;
    documentCount?: number;
    bomCount?: number;
    storagePath?: string;
    status?: number;
    chiefDesigner?: number;
    chiefDesignerNickname?: string;
    reviewer?: number;
    reviewerNickname?: string;
    planFinishTime?: number;
    remark?: string;
    createTime?: number;
  }
  export interface PageParams extends PageParam {
    code?: string;
    projectCode?: string;
    productCode?: string;
  /** è¯¦æƒ… - å½’档记录 */
  export interface ArchiveInfo {
    id?: number;
    archiveCode?: string;
    archiver?: number;
    archiverNickname?: string;
    archiveStatus?: number;
    archiveTime?: number;
    remark?: string;
    createTime?: number;
  }
  /** è¯¦æƒ… - è®¾è®¡èµ„æ–™ */
  export interface DocumentInfo {
    id?: number;
    documentType?: string;
    documentName?: string;
    documentVersion?: string;
    fileName?: string;
    fileUrl?: string;
    uploadUserId?: number;
    uploadUserNickname?: string;
    uploadTime?: number;
    remark?: string;
    createTime?: number;
  }
  /** è¯¦æƒ…(按产品查询) */
  export interface DetailItem {
    project: ProjectInfo;
    archives: ArchiveInfo[];
    documents: DocumentInfo[];
  }
}
/** å½’档台账分页(按项目/产品聚合) */
export function getPdArchivePage(params: MesPdArchiveApi.PageParams) {
  if (isPdMockEnabled()) {
    return pdMockStore.getArchivePage(params);
  }
  return requestClient.get<PageResult<MesPdArchiveApi.Archive>>(
    '/mes/pd/archive/page',
    { params },
  );
}
export function getPdArchive(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.getArchive(id);
/** å½’档台账详情(按产品编码查询) */
export function getPdArchiveDetailByProduct(productCode: string) {
  return requestClient.get<MesPdArchiveApi.DetailItem[]>(
    '/mes/pd/archive/detail-by-product',
    { params: { productCode } },
  );
  }
export function getPdArchive(id: number) {
  return requestClient.get<MesPdArchiveApi.Archive>(
    `/mes/pd/archive/get?id=${id}`,
  );
}
export function exportPdArchive(params: MesPdArchiveApi.PageParams) {
  if (isPdMockEnabled()) {
    return Promise.resolve(new Blob());
export function updatePdArchive(data: MesPdArchiveApi.Archive) {
  return requestClient.put('/mes/pd/archive/update', data);
  }
export function deletePdArchive(id: number) {
  return requestClient.delete(`/mes/pd/archive/delete?id=${id}`);
}
export function exportPdArchive(params: MesPdArchiveApi.PageParams) {
  return requestClient.download('/mes/pd/archive/export-excel', { params });
}
src/api/mes/pd/bom/index.ts
@@ -2,9 +2,6 @@
import { requestClient } from '#/api/request';
import { isPdMockEnabled } from '../mock/enabled';
import { pdMockStore } from '../mock/store';
export namespace MesPdBomApi {
  /** è®¾è®¡ BOM è¡Œ */
  export interface BomLine {
@@ -28,9 +25,6 @@
}
export function getPdBomPage(params: MesPdBomApi.PageParams) {
  if (isPdMockEnabled()) {
    return pdMockStore.getBomPage(params);
  }
  return requestClient.get<PageResult<MesPdBomApi.BomLine>>(
    '/mes/pd/bom/page',
    { params },
@@ -38,22 +32,13 @@
}
export function createPdBomLine(data: MesPdBomApi.BomLine) {
  if (isPdMockEnabled()) {
    return pdMockStore.createBomLine(data);
  }
  return requestClient.post<number>('/mes/pd/bom/create', data);
}
export function updatePdBomLine(data: MesPdBomApi.BomLine) {
  if (isPdMockEnabled()) {
    return pdMockStore.updateBomLine(data);
  }
  return requestClient.put('/mes/pd/bom/update', data);
}
export function deletePdBomLine(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.deleteBomLine(id);
  }
  return requestClient.delete(`/mes/pd/bom/delete?id=${id}`);
}
src/api/mes/pd/document-audit/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
import type { PageParam, PageResult } from '#/packages/effects/request/src';
import { requestClient } from '#/api/request';
export namespace MesPdDocumentAuditApi {
  /** èµ„料审核 */
  export interface DocumentAudit {
    id?: number;
    /** å®¡æ ¸å•号 */
    auditNo?: string;
    /** èµ„æ–™ID */
    documentId?: number;
    /** èµ„料名称(冗余) */
    documentName?: string;
    /** èµ„料版本(冗余) */
    documentVersion?: string;
    /** å®¡æ ¸äººï¼ˆç”¨æˆ·ID) */
    reviewer?: number;
    /** å®¡æ ¸äººåç§° */
    reviewerNickname?: string;
    /** å®¡æ ¸çŠ¶æ€ï¼ˆå­—å…¸ mes_pd_document_audit_status) */
    auditStatus?: number;
    /** å®¡æ ¸æ—¶é—´ */
    auditTime?: number;
    /** å¤‡æ³¨ */
    remark?: string;
    /** åˆ›å»ºæ—¶é—´ */
    createTime?: number;
  }
  export interface PageParams extends PageParam {
    auditNo?: string;
    documentId?: number;
    documentName?: string;
    auditStatus?: number;
  }
}
/** èŽ·å–èµ„æ–™å®¡æ ¸åˆ†é¡µï¼ˆä¸Šä¼ èµ„æ–™æ—¶åŽç«¯è‡ªåŠ¨åˆ›å»ºï¼Œå‰ç«¯æ— éœ€è°ƒç”¨ create) */
export function getPdDocumentAuditPage(
  params: MesPdDocumentAuditApi.PageParams,
) {
  return requestClient.get<PageResult<MesPdDocumentAuditApi.DocumentAudit>>(
    '/mes/pd/document-audit/page',
    { params },
  );
}
export function getPdDocumentAudit(id: number) {
  return requestClient.get<MesPdDocumentAuditApi.DocumentAudit>(
    `/mes/pd/document-audit/get?id=${id}`,
  );
}
export function updatePdDocumentAudit(data: MesPdDocumentAuditApi.DocumentAudit) {
  return requestClient.put('/mes/pd/document-audit/update', data);
}
export function deletePdDocumentAudit(id: number) {
  return requestClient.delete(`/mes/pd/document-audit/delete?id=${id}`);
}
export function exportPdDocumentAudit(
  params: MesPdDocumentAuditApi.PageParams,
) {
  return requestClient.download('/mes/pd/document-audit/export-excel', {
    params,
  });
}
src/api/mes/pd/document/index.ts
@@ -2,38 +2,43 @@
import { requestClient } from '#/api/request';
import { isPdMockEnabled } from '../mock/enabled';
import { pdMockStore } from '../mock/store';
export namespace MesPdDocumentApi {
  /** è®¾è®¡èµ„æ–™ */
  export interface Document {
    id?: number;
    /** è®¾è®¡é¡¹ç›®ID */
    projectId?: number;
    documentType?: number;
    name?: string;
    version?: string;
    fileUrl?: string;
    /** èµ„料类型(字典 mes_pd_document_type) */
    documentType?: string;
    /** èµ„料名称 */
    documentName?: string;
    /** ç‰ˆæœ¬ */
    documentVersion?: string;
    /** æ–‡ä»¶å */
    fileName?: string;
    fileSize?: number;
    uploaderId?: number;
    uploaderName?: string;
    /** ä¸Šä¼ äººï¼ˆç”¨æˆ·ID) */
    uploadUserId?: number;
    /** ä¸Šä¼ äººåç§° */
    uploadUserNickname?: string;
    /** ä¸Šä¼ æ—¶é—´ */
    uploadTime?: number;
    /** æ–‡ä»¶URL */
    fileUrl?: string;
    /** å¤‡æ³¨ */
    remark?: string;
    /** åˆ›å»ºæ—¶é—´ */
    createTime?: number;
  }
  export interface PageParams extends PageParam {
    projectId: number;
    documentType?: number;
    name?: string;
    version?: string;
    documentType?: string;
    documentName?: string;
    documentVersion?: string;
  }
}
export function getPdDocumentPage(params: MesPdDocumentApi.PageParams) {
  if (isPdMockEnabled()) {
    return pdMockStore.getDocumentPage(params);
  }
  return requestClient.get<PageResult<MesPdDocumentApi.Document>>(
    '/mes/pd/document/page',
    { params },
@@ -41,31 +46,23 @@
}
export function getPdDocument(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.getDocument(id);
  }
  return requestClient.get<MesPdDocumentApi.Document>(
    `/mes/pd/document/get?id=${id}`,
  );
}
export function createPdDocument(data: MesPdDocumentApi.Document) {
  if (isPdMockEnabled()) {
    return pdMockStore.createDocument(data);
  }
  return requestClient.post<number>('/mes/pd/document/create', data);
}
export function updatePdDocument(data: MesPdDocumentApi.Document) {
  if (isPdMockEnabled()) {
    return pdMockStore.updateDocument(data);
  }
  return requestClient.put('/mes/pd/document/update', data);
}
export function deletePdDocument(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.deleteDocument(id);
  }
  return requestClient.delete(`/mes/pd/document/delete?id=${id}`);
}
export function exportPdDocument(params: MesPdDocumentApi.PageParams) {
  return requestClient.download('/mes/pd/document/export-excel', { params });
}
src/api/mes/pd/drawing-review/index.ts
@@ -2,9 +2,6 @@
import { requestClient } from '#/api/request';
import { isPdMockEnabled } from '../mock/enabled';
import { pdMockStore } from '../mock/store';
export namespace MesPdDrawingReviewApi {
  /** å›¾çº¸å®¡æ ¸ */
  export interface DrawingReview {
@@ -41,9 +38,6 @@
export function getPdDrawingReviewPage(
  params: MesPdDrawingReviewApi.PageParams,
) {
  if (isPdMockEnabled()) {
    return pdMockStore.getDrawingReviewPage(params);
  }
  return requestClient.get<PageResult<MesPdDrawingReviewApi.DrawingReview>>(
    '/mes/pd/drawing-review/page',
    { params },
@@ -51,24 +45,15 @@
}
export function getPdDrawingReview(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.getDrawingReview(id);
  }
  return requestClient.get<MesPdDrawingReviewApi.DrawingReview>(
    `/mes/pd/drawing-review/get?id=${id}`,
  );
}
export function createPdDrawingReview(data: MesPdDrawingReviewApi.DrawingReview) {
  if (isPdMockEnabled()) {
    return Promise.resolve(1);
  }
  return requestClient.post<number>('/mes/pd/drawing-review/create', data);
}
export function reviewPdDrawing(data: MesPdDrawingReviewApi.ReviewParams) {
  if (isPdMockEnabled()) {
    return pdMockStore.reviewDrawing(data);
  }
  return requestClient.put('/mes/pd/drawing-review/review', data);
}
src/api/mes/pd/mock/enabled.ts
ÎļþÒÑɾ³ý
src/api/mes/pd/mock/seed.ts
ÎļþÒÑɾ³ý
src/api/mes/pd/mock/store.ts
ÎļþÒÑɾ³ý
src/api/mes/pd/mock/utils.ts
ÎļþÒÑɾ³ý
src/api/mes/pd/project/index.ts
@@ -2,43 +2,49 @@
import { requestClient } from '#/api/request';
import { isPdMockEnabled } from '../mock/enabled';
import { pdMockStore } from '../mock/store';
export namespace MesPdProjectApi {
  /** äº§å“è®¾è®¡é¡¹ç›® */
  export interface Project {
    id?: number;
    code?: string;
    name?: string;
    /** ä»»åŠ¡ç¼–ç  */
    taskCode?: string;
    /** ä»»åŠ¡åç§° */
    taskName?: string;
    /** äº§å“ç¼–码 */
    productCode?: string;
    /** äº§å“åç§° */
    productName?: string;
    /** ç‰ˆæœ¬å· */
    version?: string;
    /** çŠ¶æ€ */
    status?: number;
    designerId?: number;
    designerName?: string;
    reviewerId?: number;
    reviewerName?: string;
    planStartDate?: number;
    planEndDate?: number;
    actualFinishDate?: number;
    /** ä¸»è®¾è®¡å¸ˆï¼ˆç”¨æˆ·ID) */
    chiefDesigner?: number;
    /** ä¸»è®¾è®¡å¸ˆåç§° */
    chiefDesignerNickname?: string;
    /** å®¡æ ¸äººï¼ˆç”¨æˆ·ID) */
    reviewer?: number;
    /** å®¡æ ¸äººåç§° */
    reviewerNickname?: string;
    /** è®¡åˆ’完成时间 */
    planFinishTime?: number;
    /** å¤‡æ³¨ */
    remark?: string;
    /** åˆ›å»ºæ—¶é—´ */
    createTime?: number;
  }
  export interface PageParams extends PageParam {
    code?: string;
    name?: string;
    taskCode?: string;
    taskName?: string;
    productCode?: string;
    productName?: string;
    status?: number;
    designerId?: number;
    planFinishTime?: number[];
  }
}
export function getPdProjectPage(params: MesPdProjectApi.PageParams) {
  if (isPdMockEnabled()) {
    return pdMockStore.getProjectPage(params);
  }
  return requestClient.get<PageResult<MesPdProjectApi.Project>>(
    '/mes/pd/project/page',
    { params },
@@ -46,73 +52,50 @@
}
export function getPdProject(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.getProject(id);
  }
  return requestClient.get<MesPdProjectApi.Project>(
    `/mes/pd/project/get?id=${id}`,
  );
}
export function createPdProject(data: MesPdProjectApi.Project) {
  if (isPdMockEnabled()) {
    return pdMockStore.createProject(data);
  }
  return requestClient.post<number>('/mes/pd/project/create', data);
}
export function updatePdProject(data: MesPdProjectApi.Project) {
  if (isPdMockEnabled()) {
    return pdMockStore.updateProject(data);
  }
  return requestClient.put('/mes/pd/project/update', data);
}
export function deletePdProject(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.deleteProject(id);
  }
  return requestClient.delete(`/mes/pd/project/delete?id=${id}`);
}
export function exportPdProject(params: MesPdProjectApi.PageParams) {
  if (isPdMockEnabled()) {
    return Promise.resolve(new Blob());
  }
  return requestClient.download('/mes/pd/project/export-excel', { params });
}
export function submitPdProjectReview(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.submitProjectReview(id);
  }
  return requestClient.put('/mes/pd/project/submit-review', undefined, {
    params: { id },
  });
}
/** å‘布(待发布→待上传) */
export function publishPdProject(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.publishProject(id);
  }
  return requestClient.put('/mes/pd/project/publish', undefined, {
    params: { id },
  });
}
export function approvePdProject(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.approveProject(id);
  }
  return requestClient.put('/mes/pd/project/approve', undefined, {
/** ä¸Šä¼ èµ„料(待上传→待审核),后端自动创建资料审核记录 */
export function uploadPdProject(id: number) {
  return requestClient.put('/mes/pd/project/upload', undefined, {
    params: { id },
  });
}
export function archivePdProject(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.archiveProject(id);
/** å®¡æ ¸ï¼ˆå¾…审核→待归档) */
export function auditPdProject(id: number) {
  return requestClient.put('/mes/pd/project/audit', undefined, {
    params: { id },
  });
  }
/** å½’档(待归档→已归档),后端自动创建归档台账记录 */
export function archivePdProject(id: number) {
  return requestClient.put('/mes/pd/project/archive', undefined, {
    params: { id },
  });
src/api/mes/pd/task/index.ts
@@ -2,9 +2,6 @@
import { requestClient } from '#/api/request';
import { isPdMockEnabled } from '../mock/enabled';
import { pdMockStore } from '../mock/store';
export namespace MesPdTaskApi {
  /** äº§å“è®¾è®¡ä»»åŠ¡ */
  export interface Task {
@@ -44,9 +41,6 @@
}
export function getPdTaskPage(params: MesPdTaskApi.PageParams) {
  if (isPdMockEnabled()) {
    return pdMockStore.getTaskPage(params);
  }
  return requestClient.get<PageResult<MesPdTaskApi.Task>>(
    '/mes/pd/task/page',
    { params },
@@ -54,44 +48,26 @@
}
export function getPdTask(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.getTask(id);
  }
  return requestClient.get<MesPdTaskApi.Task>(`/mes/pd/task/get?id=${id}`);
}
export function createPdTask(data: MesPdTaskApi.Task) {
  if (isPdMockEnabled()) {
    return pdMockStore.createTask(data);
  }
  return requestClient.post<number>('/mes/pd/task/create', data);
}
export function updatePdTask(data: MesPdTaskApi.Task) {
  if (isPdMockEnabled()) {
    return pdMockStore.updateTask(data);
  }
  return requestClient.put('/mes/pd/task/update', data);
}
export function deletePdTask(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.deleteTask(id);
  }
  return requestClient.delete(`/mes/pd/task/delete?id=${id}`);
}
export function dispatchPdTask(data: MesPdTaskApi.DispatchParams) {
  if (isPdMockEnabled()) {
    return pdMockStore.dispatchTask(data);
  }
  return requestClient.put('/mes/pd/task/dispatch', data);
}
export function completePdTask(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.completeTask(id);
  }
  return requestClient.put('/mes/pd/task/complete', undefined, {
    params: { id },
  });
src/api/mes/pd/version-change/index.ts
@@ -2,9 +2,6 @@
import { requestClient } from '#/api/request';
import { isPdMockEnabled } from '../mock/enabled';
import { pdMockStore } from '../mock/store';
export namespace MesPdVersionChangeApi {
  /** ç‰ˆæœ¬å˜æ›´ */
  export interface VersionChange {
@@ -34,9 +31,6 @@
export function getPdVersionChangePage(
  params: MesPdVersionChangeApi.PageParams,
) {
  if (isPdMockEnabled()) {
    return pdMockStore.getVersionChangePage(params);
  }
  return requestClient.get<PageResult<MesPdVersionChangeApi.VersionChange>>(
    '/mes/pd/version-change/page',
    { params },
@@ -44,39 +38,24 @@
}
export function getPdVersionChange(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.getVersionChange(id);
  }
  return requestClient.get<MesPdVersionChangeApi.VersionChange>(
    `/mes/pd/version-change/get?id=${id}`,
  );
}
export function createPdVersionChange(data: MesPdVersionChangeApi.VersionChange) {
  if (isPdMockEnabled()) {
    return pdMockStore.createVersionChange(data);
  }
  return requestClient.post<number>('/mes/pd/version-change/create', data);
}
export function updatePdVersionChange(data: MesPdVersionChangeApi.VersionChange) {
  if (isPdMockEnabled()) {
    return pdMockStore.updateVersionChange(data);
  }
  return requestClient.put('/mes/pd/version-change/update', data);
}
export function deletePdVersionChange(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.deleteVersionChange(id);
  }
  return requestClient.delete(`/mes/pd/version-change/delete?id=${id}`);
}
export function approvePdVersionChange(id: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.approveVersionChange(id);
  }
  return requestClient.put('/mes/pd/version-change/approve', undefined, {
    params: { id },
  });
src/api/mes/pd/workflow/index.ts
@@ -1,8 +1,5 @@
import { requestClient } from '#/api/request';
import { isPdMockEnabled } from '../mock/enabled';
import { pdMockStore } from '../mock/store';
export namespace MesPdWorkflowApi {
  /** è®¾è®¡æµç¨‹èŠ‚ç‚¹ */
  export interface WorkflowNode {
@@ -18,9 +15,6 @@
}
export function getPdWorkflowList(projectId: number) {
  if (isPdMockEnabled()) {
    return pdMockStore.getWorkflowList(projectId);
  }
  return requestClient.get<MesPdWorkflowApi.WorkflowNode[]>(
    '/mes/pd/workflow/list',
    { params: { projectId } },
src/api/mes/pro/andon/record/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -10,8 +10,8 @@
    workstationId?: number; // å·¥ä½œç«™ç¼–号
    workstationCode?: string; // å·¥ä½œç«™ç¼–码
    workstationName?: string; // å·¥ä½œç«™åç§°
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderCode?: string; // å·¥å•编码
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workOrderCode?: string; // è®¢å•编码
    processId?: number; // å·¥åºç¼–号
    processName?: string; // å·¥åºåç§°
    userId?: number; // å‘起用户编号
src/api/mes/pro/card/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -7,9 +7,9 @@
  export interface Card {
    id?: number; // ç¼–号
    code?: string; // æµè½¬å¡ç¼–码
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderCode?: string; // å·¥å•编码
    workOrderName?: string; // å·¥å•名称
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workOrderCode?: string; // è®¢å•编码
    workOrderName?: string; // è®¢å•名称
    batchCode?: string; // æ‰¹æ¬¡å·
    itemId?: number; // äº§å“ç‰©æ–™ç¼–号
    itemCode?: string; // äº§å“ç¼–码
src/api/mes/pro/feedback/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -6,7 +6,7 @@
  /** MES ç”Ÿäº§æŠ¥å·¥ */
  export interface Feedback {
    id?: number;
    code?: string; // æŠ¥å·¥å•编号
    code?: string; // æŠ¥è®¢å•编号
    type?: number; // æŠ¥å·¥ç±»åž‹
    channel?: string; // æŠ¥å·¥é€”径
    feedbackTime?: number; // æŠ¥å·¥æ—¶é—´
@@ -19,9 +19,9 @@
    processCode?: string; // å·¥åºç¼–码
    processName?: string; // å·¥åºåç§°
    checkFlag?: boolean; // æ˜¯å¦éœ€è¦æ£€éªŒ
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderCode?: string; // å·¥å•编码
    workOrderName?: string; // å·¥å•名称
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workOrderCode?: string; // è®¢å•编码
    workOrderName?: string; // è®¢å•名称
    taskId?: number; // ç”Ÿäº§ä»»åŠ¡ç¼–å·
    taskCode?: string; // ä»»åŠ¡ç¼–ç 
    itemId?: number; // äº§å“ç‰©æ–™ç¼–号
src/api/mes/pro/task/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -8,9 +8,9 @@
    id?: number;
    code?: string; // ä»»åŠ¡ç¼–ç 
    name?: string; // ä»»åŠ¡åç§°
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderCode?: string; // å·¥å•编码
    workOrderName?: string; // å·¥å•名称
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workOrderCode?: string; // è®¢å•编码
    workOrderName?: string; // è®¢å•名称
    workstationId?: number; // å·¥ä½œç«™ç¼–号
    workstationCode?: string; // å·¥ä½œç«™ç¼–码
    workstationName?: string; // å·¥ä½œç«™åç§°
@@ -33,7 +33,7 @@
    startTime?: number; // å¼€å§‹ç”Ÿäº§æ—¶é—´
    endTime?: number; // ç»“束生产时间
    duration?: number; // ç”Ÿäº§æ—¶é•¿ï¼ˆå·¥ä½œæ—¥ï¼Œ1=8小时)
    requestDate?: number; // éœ€æ±‚日期(从工单查)
    requestDate?: number; // éœ€æ±‚日期(从订单查)
    finishDate?: number; // å®Œæˆæ—¥æœŸ
    cancelDate?: number; // å–消日期
    colorCode?: string; // ç”˜ç‰¹å›¾æ˜¾ç¤ºé¢œè‰²
src/api/mes/pro/task/issue/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -7,7 +7,7 @@
  export interface TaskIssue {
    id?: number; // ç¼–号
    taskId?: number; // ç”Ÿäº§ä»»åŠ¡ç¼–å·
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workstationId?: number; // å·¥ä½œç«™ç¼–号
    sourceDocType?: string; // æ¥æºå•据类型
    sourceDocId?: number; // æ¥æºå•据编号
@@ -29,7 +29,7 @@
  /** MES ç”Ÿäº§ä»»åŠ¡æŠ•æ–™åˆ†é¡µæŸ¥è¯¢å‚æ•° */
  export interface PageParams extends PageParam {
    taskId?: number; // ç”Ÿäº§ä»»åŠ¡ç¼–å·
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workstationId?: number; // å·¥ä½œç«™ç¼–号
    itemId?: number; // äº§å“ç‰©æ–™ç¼–号
  }
src/api/mes/pro/workorder/bom/index.ts
@@ -1,12 +1,12 @@
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
export namespace MesProWorkOrderBomApi {
  /** MES ç”Ÿäº§å·¥å• BOM */
  /** MES ç”Ÿäº§è®¢å• BOM */
  export interface WorkOrderBom {
    id?: number; // ç¼–号
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    itemId?: number; // BOM ç‰©æ–™ç¼–号
    itemName?: string; // ç‰©æ–™åç§°
    itemCode?: string; // ç‰©æ–™ç¼–码
@@ -18,13 +18,13 @@
    itemOrProduct?: string; // ç‰©æ–™äº§å“æ ‡è¯†
  }
  /** MES ç”Ÿäº§å·¥å• BOM åˆ†é¡µæŸ¥è¯¢å‚æ•° */
  /** MES ç”Ÿäº§è®¢å• BOM åˆ†é¡µæŸ¥è¯¢å‚æ•° */
  export interface PageParams extends PageParam {
    workOrderId?: number;
  }
}
/** æŸ¥è¯¢å·¥å• BOM åˆ†é¡µ */
/** æŸ¥è¯¢è®¢å• BOM åˆ†é¡µ */
export function getWorkOrderBomPage(params: MesProWorkOrderBomApi.PageParams) {
  return requestClient.get<PageResult<MesProWorkOrderBomApi.WorkOrderBom>>(
    '/mes/pro/work-order-bom/page',
@@ -32,29 +32,29 @@
  );
}
/** æŸ¥è¯¢å·¥å• BOM è¯¦æƒ… */
/** æŸ¥è¯¢è®¢å• BOM è¯¦æƒ… */
export function getWorkOrderBom(id: number) {
  return requestClient.get<MesProWorkOrderBomApi.WorkOrderBom>(
    `/mes/pro/work-order-bom/get?id=${id}`,
  );
}
/** æ–°å¢žå·¥å• BOM */
/** æ–°å¢žè®¢å• BOM */
export function createWorkOrderBom(data: MesProWorkOrderBomApi.WorkOrderBom) {
  return requestClient.post('/mes/pro/work-order-bom/create', data);
}
/** ä¿®æ”¹å·¥å• BOM */
/** ä¿®æ”¹è®¢å• BOM */
export function updateWorkOrderBom(data: MesProWorkOrderBomApi.WorkOrderBom) {
  return requestClient.put('/mes/pro/work-order-bom/update', data);
}
/** åˆ é™¤å·¥å• BOM */
/** åˆ é™¤è®¢å• BOM */
export function deleteWorkOrderBom(id: number) {
  return requestClient.delete(`/mes/pro/work-order-bom/delete?id=${id}`);
}
/** æŸ¥è¯¢å·¥å•物料需求列表 */
/** æŸ¥è¯¢è®¢å•物料需求列表 */
export function getWorkOrderBomItemListByWorkOrderId(workOrderId: number) {
  return requestClient.get<MesProWorkOrderBomApi.WorkOrderBom[]>(
    `/mes/pro/work-order-bom/item-list-by-work-order-id?workOrderId=${workOrderId}`,
src/api/mes/pro/workorder/index.ts
@@ -1,14 +1,14 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
export namespace MesProWorkOrderApi {
  /** MES ç”Ÿäº§å·¥å• */
  /** MES ç”Ÿäº§è®¢å• */
  export interface WorkOrder {
    id?: number; // ç¼–号
    code?: string; // å·¥å•编码
    name?: string; // å·¥å•名称
    type?: number; // å·¥å•类型
    code?: string; // è®¢å•编码
    name?: string; // è®¢å•名称
    type?: number; // è®¢å•类型
    orderSourceType?: number; // æ¥æºç±»åž‹
    orderSourceCode?: string; // æ¥æºå•据编号
    productId?: number; // äº§å“ç¼–号
@@ -28,16 +28,16 @@
    vendorCode?: string; // ä¾›åº”商编码
    batchCode?: string; // æ‰¹æ¬¡å·
    requestDate?: number; // éœ€æ±‚日期
    parentId?: number; // çˆ¶å·¥å•编号
    parentCode?: string; // çˆ¶å·¥å•编码
    parentId?: number; // çˆ¶è®¢å•编号
    parentCode?: string; // çˆ¶è®¢å•编码
    finishDate?: number; // å®Œæˆæ—¶é—´
    cancelDate?: number; // å–消时间
    status?: number; // å·¥å•状态
    status?: number; // è®¢å•状态
    remark?: string; // å¤‡æ³¨
    createTime?: number; // åˆ›å»ºæ—¶é—´
  }
  /** MES ç”Ÿäº§å·¥å•分页查询参数 */
  /** MES ç”Ÿäº§è®¢å•分页查询参数 */
  export interface PageParams extends PageParam {
    code?: string;
    name?: string;
@@ -50,7 +50,7 @@
  }
}
/** æŸ¥è¯¢ç”Ÿäº§å·¥å•分页 */
/** æŸ¥è¯¢ç”Ÿäº§è®¢å•分页 */
export function getWorkOrderPage(params: MesProWorkOrderApi.PageParams) {
  return requestClient.get<PageResult<MesProWorkOrderApi.WorkOrder>>(
    '/mes/pro/work-order/page',
@@ -58,44 +58,44 @@
  );
}
/** æŸ¥è¯¢ç”Ÿäº§å·¥å•详情 */
/** æŸ¥è¯¢ç”Ÿäº§è®¢å•详情 */
export function getWorkOrder(id: number) {
  return requestClient.get<MesProWorkOrderApi.WorkOrder>(
    `/mes/pro/work-order/get?id=${id}`,
  );
}
/** æ–°å¢žç”Ÿäº§å·¥å• */
/** æ–°å¢žç”Ÿäº§è®¢å• */
export function createWorkOrder(data: MesProWorkOrderApi.WorkOrder) {
  return requestClient.post<number>('/mes/pro/work-order/create', data);
}
/** ä¿®æ”¹ç”Ÿäº§å·¥å• */
/** ä¿®æ”¹ç”Ÿäº§è®¢å• */
export function updateWorkOrder(data: MesProWorkOrderApi.WorkOrder) {
  return requestClient.put('/mes/pro/work-order/update', data);
}
/** åˆ é™¤ç”Ÿäº§å·¥å• */
/** åˆ é™¤ç”Ÿäº§è®¢å• */
export function deleteWorkOrder(id: number) {
  return requestClient.delete(`/mes/pro/work-order/delete?id=${id}`);
}
/** å¯¼å‡ºç”Ÿäº§å·¥å• */
/** å¯¼å‡ºç”Ÿäº§è®¢å• */
export function exportWorkOrder(params: any) {
  return requestClient.download('/mes/pro/work-order/export-excel', { params });
}
/** å®Œæˆå·¥å• */
/** å®Œæˆè®¢å• */
export function finishWorkOrder(id: number) {
  return requestClient.put(`/mes/pro/work-order/finish?id=${id}`);
}
/** å–消工单 */
/** å–消订单 */
export function cancelWorkOrder(id: number) {
  return requestClient.put(`/mes/pro/work-order/cancel?id=${id}`);
}
/** ç¡®è®¤å·¥å• */
/** ç¡®è®¤è®¢å• */
export function confirmWorkOrder(id: number) {
  return requestClient.put(`/mes/pro/work-order/confirm?id=${id}`);
}
src/api/mes/qc/ipqc/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -14,9 +14,9 @@
    sourceDocId?: number; // æ¥æºå•据 ID
    sourceLineId?: number; // æ¥æºå•据行 ID
    sourceDocCode?: string; // æ¥æºå•据编号(关联查询)
    workOrderId?: number; // ç”Ÿäº§å·¥å• ID
    workOrderCode?: string; // å·¥å•编号(关联查询)
    workOrderName?: string; // å·¥å•名称(关联查询)
    workOrderId?: number; // ç”Ÿäº§è®¢å• ID
    workOrderCode?: string; // è®¢å•编号(关联查询)
    workOrderName?: string; // è®¢å•名称(关联查询)
    taskId?: number; // ç”Ÿäº§ä»»åŠ¡ ID
    workstationId?: number; // å·¥ä½ ID
    workstationName?: string; // å·¥ä½åç§°ï¼ˆå…³è”查询)
src/api/mes/qc/pendinginspect/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -19,8 +19,8 @@
    // ä¾›åº”商(IQC åœºæ™¯ï¼‰
    vendorId?: number; // ä¾›åº”商 ID
    vendorName?: string; // ä¾›åº”商名称
    // å·¥å•/工作站/任务(IPQC/RQC åœºæ™¯ï¼‰
    workOrderId?: number; // ç”Ÿäº§å·¥å• ID
    // è®¢å•/工作站/任务(IPQC/RQC åœºæ™¯ï¼‰
    workOrderId?: number; // ç”Ÿäº§è®¢å• ID
    workstationId?: number; // å·¥ä½œç«™ ID
    workstationName?: string; // å·¥ä½œç«™åç§°
    taskId?: number; // ç”Ÿäº§ä»»åŠ¡ ID
src/api/mes/wm/batch/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -23,8 +23,8 @@
    clientName?: string; // å®¢æˆ·åç§°
    purchaseOrderCode?: string; // é‡‡è´­è®¢å•编号
    salesOrderCode?: string; // é”€å”®è®¢å•编号
    workOrderId?: number; // ç”Ÿäº§å·¥å• ID
    workOrderCode?: string; // ç”Ÿäº§å·¥å•编号
    workOrderId?: number; // ç”Ÿäº§è®¢å• ID
    workOrderCode?: string; // ç”Ÿäº§è®¢å•编号
    taskId?: number; // ç”Ÿäº§ä»»åŠ¡ ID
    taskCode?: string; // ç”Ÿäº§ä»»åŠ¡ç¼–å·
    workstationId?: number; // å·¥ä½œç«™ ID
@@ -43,7 +43,7 @@
    itemId?: number; // ç‰©æ–™ç¼–号
    vendorId?: number; // ä¾›åº”商编号
    clientId?: number; // å®¢æˆ·ç¼–号
    workOrderId?: number; // å·¥å•编号
    workOrderId?: number; // è®¢å•编号
    taskId?: number; // ç”Ÿäº§ä»»åŠ¡ç¼–å·
    workstationId?: number; // å·¥ä½œç«™ç¼–号
    toolId?: number; // å·¥å…·ç¼–号
src/api/mes/wm/outsourceissue/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -11,9 +11,9 @@
    vendorId?: number; // ä¾›åº”商编号
    vendorCode?: string; // ä¾›åº”商编码
    vendorName?: string; // ä¾›åº”商名称
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderCode?: string; // ç”Ÿäº§å·¥å•编码
    workOrderName?: string; // ç”Ÿäº§å·¥å•名称
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workOrderCode?: string; // ç”Ÿäº§è®¢å•编码
    workOrderName?: string; // ç”Ÿäº§è®¢å•名称
    issueDate?: number; // å‘料日期
    status?: number; // å•据状态
    remark?: string; // å¤‡æ³¨
src/api/mes/wm/outsourcereceipt/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -8,8 +8,8 @@
    id?: number; // å…¥åº“单编号
    code?: string; // å…¥åº“单编码
    name?: string; // å…¥åº“单名称
    workOrderId?: number; // å¤–协工单编号
    workOrderCode?: string; // å¤–协工单编码
    workOrderId?: number; // å¤–协订单编号
    workOrderCode?: string; // å¤–协订单编码
    vendorId?: number; // ä¾›åº”商编号
    vendorName?: string; // ä¾›åº”商名称
    receiptDate?: number; // å…¥åº“日期
src/api/mes/wm/packages/line/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -14,8 +14,8 @@
    specification?: string; // è§„格型号
    unitMeasureName?: string; // è®¡é‡å•位名称
    quantity?: number; // è£…箱数量
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderCode?: string; // ç”Ÿäº§å·¥å•编号
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workOrderCode?: string; // ç”Ÿäº§è®¢å•编号
    batchCode?: string; // æ‰¹æ¬¡å·
    expireDate?: number; // æœ‰æ•ˆæœŸ
    remark?: string; // å¤‡æ³¨
src/api/mes/wm/productissue/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -11,8 +11,8 @@
    workstationId?: number; // å·¥ä½œç«™ç¼–号
    workstationCode?: string; // å·¥ä½œç«™ç¼–码
    workstationName?: string; // å·¥ä½œç«™åç§°
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderCode?: string; // ç”Ÿäº§å·¥å•编码
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workOrderCode?: string; // ç”Ÿäº§è®¢å•编码
    clientCode?: string; // å®¢æˆ·ç¼–码
    clientName?: string; // å®¢æˆ·åç§°
    requiredTime?: number; // éœ€æ±‚æ—¶é—´
src/api/mes/wm/productreceipt/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -8,8 +8,8 @@
    id?: number; // å…¥åº“单编号
    code?: string; // å…¥åº“单编码
    name?: string; // å…¥åº“单名称
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderCode?: string; // ç”Ÿäº§å·¥å•编码
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workOrderCode?: string; // ç”Ÿäº§è®¢å•编码
    itemId?: number; // äº§å“ç‰©æ–™ç¼–号
    itemCode?: string; // äº§å“ç‰©æ–™ç¼–码
    itemName?: string; // äº§å“ç‰©æ–™åç§°
src/api/mes/wm/returnissue/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -10,8 +10,8 @@
    name?: string; // é€€æ–™å•名称
    workstationId?: number; // å·¥ä½œç«™ç¼–号
    workstationName?: string; // å·¥ä½œç«™åç§°
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderCode?: string; // ç”Ÿäº§å·¥å•编码
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    workOrderCode?: string; // ç”Ÿäº§è®¢å•编码
    type?: number; // é€€æ–™ç±»åž‹
    returnDate?: number; // é€€æ–™æ—¥æœŸ
    status?: number; // å•据状态
src/api/mes/wm/sn/index.ts
@@ -1,4 +1,4 @@
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import type { PageParam, PageResult } from '../../../../packages/effects/request/src';
import { requestClient } from '#/api/request';
@@ -13,7 +13,7 @@
    specification?: string; // è§„格型号
    unitName?: string; // å•位名称
    batchCode?: string; // æ‰¹æ¬¡å·
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    createTime?: Date; // ç”Ÿæˆæ—¶é—´
  }
@@ -28,7 +28,7 @@
    specification?: string; // è§„格型号
    unitName?: string; // å•位名称
    batchCode?: string; // æ‰¹æ¬¡å·
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    createTime?: Date; // ç”Ÿæˆæ—¶é—´
  }
@@ -36,7 +36,7 @@
  export interface SnGenerate {
    itemId?: number; // ç‰©æ–™ç¼–号
    batchCode?: string; // æ‰¹æ¬¡å·
    workOrderId?: number; // ç”Ÿäº§å·¥å•编号
    workOrderId?: number; // ç”Ÿäº§è®¢å•编号
    count?: number; // ç”Ÿæˆæ•°é‡
  }
src/packages/constants/src/biz-mes-enum.ts
@@ -136,6 +136,8 @@
  PD_PROJECT_CODE: 'PD_PROJECT_CODE',
  PD_TASK_CODE: 'PD_TASK_CODE',
  PD_VERSION_CODE: 'PD_VERSION_CODE',
  PD_DOCUMENT_AUDIT_CODE: 'PD_DOCUMENT_AUDIT_CODE',
  PD_ARCHIVE_CODE: 'PD_ARCHIVE_CODE',
  PRO_CARD_CODE: 'PRO_CARD_CODE',
  PRO_FEEDBACK_CODE: 'PRO_FEEDBACK_CODE',
  PRO_PROCESS_CODE: 'PRO_PROCESS_CODE',
@@ -495,14 +497,13 @@
  RIGHT: 2, // å³è¡¥é½
} as const;
/** MES äº§å“è®¾è®¡é¡¹ç›®çŠ¶æ€æžšä¸¾ */
/** MES äº§å“è®¾è®¡é¡¹ç›®çŠ¶æ€æžšä¸¾ï¼ˆçº¿æ€§æµè½¬ï¼šå¾…å‘å¸ƒâ†’å¾…ä¸Šä¼ â†’å¾…å®¡æ ¸â†’å¾…å½’æ¡£â†’å·²å½’æ¡£ï¼‰ */
export const MesPdProjectStatusEnum = {
  DRAFT: MesOrderStatusConstants.DRAFT,
  DESIGNING: 10,
  REVIEWING: MesOrderStatusConstants.APPROVING,
  APPROVED: MesOrderStatusConstants.APPROVED,
  ARCHIVED: 20,
  CANCELLED: MesOrderStatusConstants.CANCELLED,
  PENDING_PUBLISH: 0, // å¾…发布
  PENDING_UPLOAD: 1, // å¾…上传
  PENDING_AUDIT: 2, // å¾…审核
  PENDING_ARCHIVE: 3, // å¾…å½’æ¡£
  ARCHIVED: 4, // å·²å½’æ¡£
} as const;
/** MES äº§å“è®¾è®¡ä»»åŠ¡çŠ¶æ€æžšä¸¾ */
@@ -528,14 +529,14 @@
  PATCH: 3,
} as const;
/** MES äº§å“è®¾è®¡èµ„料类型枚举 */
/** MES äº§å“è®¾è®¡èµ„料类型枚举(字典 mes_pd_document_type,后端存储 string ç±»åž‹ï¼‰ */
export const MesPdDocumentTypeEnum = {
  SCHEME: 1,
  DRAWING: 2,
  MODEL: 3,
  PRODUCT_DESIGN: 4,
  OTHER: 5,
  BOM: 6,
  SCHEME: 'scheme',
  DRAWING: 'drawing',
  MODEL: 'model',
  PRODUCT_DESIGN: 'product_design',
  BOM: 'bom',
  OTHER: 'other',
} as const;
/** MES æ¡ç æ ¼å¼æžšä¸¾ */
src/packages/constants/src/dict-enum.ts
@@ -231,6 +231,7 @@
  MES_PD_PROJECT_STATUS: 'mes_pd_project_status', // MES äº§å“è®¾è®¡é¡¹ç›®çŠ¶æ€
  MES_PD_TASK_STATUS: 'mes_pd_task_status', // MES äº§å“è®¾è®¡ä»»åŠ¡çŠ¶æ€
  MES_PD_DRAWING_REVIEW_STATUS: 'mes_pd_drawing_review_status', // MES å›¾çº¸å®¡æ ¸çŠ¶æ€
  MES_PD_DOCUMENT_AUDIT_STATUS: 'mes_pd_document_audit_status', // MES èµ„料审核状态
  MES_PD_VERSION_CHANGE_TYPE: 'mes_pd_version_change_type', // MES ç‰ˆæœ¬å˜æ›´ç±»åž‹
  MES_PD_DOCUMENT_TYPE: 'mes_pd_document_type', // MES è®¾è®¡èµ„料类型
  MES_PD_ARCHIVE_STATUS: 'mes_pd_archive_status', // MES è®¾è®¡å½’档状态
src/views/mes/dv/repair/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
@@ -25,7 +25,7 @@
  return ['confirm', 'detail', 'finish'].includes(formType);
}
/** æ–°å¢ž/修改维修工单的表单 */
/** æ–°å¢ž/修改维修订单的表单 */
export function useFormSchema(
  formType: FormType,
  formApi?: VbenFormApi,
src/views/mes/dv/repair/index.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
@@ -25,17 +25,17 @@
  gridApi.query();
}
/** åˆ›å»ºç»´ä¿®å·¥å• */
/** åˆ›å»ºç»´ä¿®è®¢å• */
function handleCreate() {
  formModalApi.setData({ formType: 'create' }).open();
}
/** æŸ¥çœ‹ç»´ä¿®å·¥å• */
/** æŸ¥çœ‹ç»´ä¿®è®¢å• */
function handleDetail(row: MesDvRepairApi.Repair) {
  formModalApi.setData({ id: row.id, formType: 'detail' }).open();
}
/** ç¼–辑维修工单 */
/** ç¼–辑维修订单 */
function handleEdit(row: MesDvRepairApi.Repair) {
  formModalApi.setData({ id: row.id, formType: 'update' }).open();
}
@@ -50,7 +50,7 @@
  formModalApi.setData({ id: row.id, formType: 'finish' }).open();
}
/** åˆ é™¤ç»´ä¿®å·¥å• */
/** åˆ é™¤ç»´ä¿®è®¢å• */
async function handleDelete(row: MesDvRepairApi.Repair) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting', [row.name]),
@@ -65,10 +65,10 @@
  }
}
/** å¯¼å‡ºç»´ä¿®å·¥å• */
/** å¯¼å‡ºç»´ä¿®è®¢å• */
async function handleExport() {
  const data = await exportRepair(await gridApi.formApi.getValues());
  downloadFileFromBlobPart({ fileName: '维修工单.xls', source: data });
  downloadFileFromBlobPart({ fileName: '维修订单.xls', source: data });
}
const [Grid, gridApi] = useVbenVxeGrid({
@@ -102,12 +102,12 @@
</script>
<template>
  <Page auto-content-height><FormModal @success="handleRefresh" />
    <Grid table-title="维修工单列表">
    <Grid table-title="维修订单列表">
      <template #toolbar-tools>
        <TableAction
          :actions="[
            {
              label: $t('ui.actionTitle.create', ['维修工单']),
              label: $t('ui.actionTitle.create', ['维修订单']),
              type: 'primary',
              icon: ACTION_ICON.ADD,
              auth: ['mes:dv-repair:create'],
src/views/mes/dv/repair/modules/form.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
@@ -43,7 +43,7 @@
);
const getTitle = computed(() => {
  if (formType.value === 'detail') {
    return '查看维修工单';
    return '查看维修订单';
  }
  if (formType.value === 'confirm') {
    return '完成维修';
@@ -51,7 +51,7 @@
  if (formType.value === 'finish') {
    return '验收维修';
  }
  return formType.value === 'update' ? '修改维修工单' : '新增维修工单';
  return formType.value === 'update' ? '修改维修订单' : '新增维修订单';
});
const [Form, formApi] = useVbenForm({
@@ -68,7 +68,7 @@
  showDefaultActions: false,
});
/** æäº¤ç»´ä¿®å·¥å• */
/** æäº¤ç»´ä¿®è®¢å• */
async function handleSubmit() {
  const { valid } = await formApi.validate();
  if (!valid || !formData.value?.id) {
@@ -77,7 +77,7 @@
  await doSubmit();
}
/** æ‰§è¡Œç»´ä¿®å·¥å•提交 */
/** æ‰§è¡Œç»´ä¿®è®¢å•提交 */
async function doSubmit() {
  modalApi.lock();
  try {
@@ -214,7 +214,7 @@
      <div class="flex flex-auto items-center gap-2">
        <Popconfirm
          v-if="canSubmit"
          title="确认提交该维修工单?提交后将不能修改。"
          title="确认提交该维修订单?提交后将不能修改。"
          @confirm="handleSubmit"
        >
          <Button type="primary">提交</Button>
src/views/mes/home/chart-options.ts
@@ -1,4 +1,4 @@
import type { EChartsOption } from '../../../packages/effects/plugins/src/echarts';
import type { EChartsOption } from '../../../packages/effects/plugins/src/echarts';
/** ç”Ÿäº§è¶‹åŠ¿æŠ˜çº¿å›¾é…ç½® */
export function getProductionTrendChartOptions(
@@ -40,7 +40,7 @@
  };
}
/** å·¥å•状态分布饼图配置 */
/** è®¢å•状态分布饼图配置 */
export function getWorkOrderStatusChartOptions(
  data: Array<{ itemStyle: { color: string }; name: string; value: number }>,
): EChartsOption {
src/views/mes/home/data.ts
@@ -1,4 +1,4 @@
import type { MesHomeApi } from '#/api/mes/home';
import type { MesHomeApi } from '#/api/mes/home';
import { MesProWorkOrderStatusEnum } from '../../../packages/constants/src';
@@ -19,7 +19,7 @@
  yesterdayOutput: 0,
};
/** å·¥å•状态对应的颜色映射 */
/** è®¢å•状态对应的颜色映射 */
export const WORK_ORDER_STATUS_COLOR_MAP: Record<number, string> = {
  [MesProWorkOrderStatusEnum.PREPARE]: '#909399', // è‰ç¨¿
  [MesProWorkOrderStatusEnum.CONFIRMED]: '#409EFF', // å·²ç¡®è®¤
src/views/mes/home/index.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { MesHomeApi } from '#/api/mes/home';
import { onMounted, ref } from 'vue';
@@ -49,7 +49,7 @@
      </Col>
    </Row>
    <!-- ç¬¬ä¸‰è¡Œï¼šå·¥å•分布 + å¿«æ·å…¥å£ -->
    <!-- ç¬¬ä¸‰è¡Œï¼šè®¢å•分布 + å¿«æ·å…¥å£ -->
    <Row :gutter="16">
      <Col :lg="12" :md="24" :sm="24" :xl="12" :xs="24" class="mb-4">
        <WorkOrderChart />
src/views/mes/home/modules/alert-panel.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { MesHomeApi } from '#/api/mes/home';
import { computed } from 'vue';
@@ -29,7 +29,7 @@
  },
  {
    count: props.summary.repairActiveCount,
    desc: '待处理的维修工单',
    desc: '待处理的维修订单',
    icon: 'lucide:wrench',
    iconClass: 'bg-orange-50 text-orange-500',
    label: '设备维修',
@@ -37,10 +37,10 @@
  },
  {
    count: props.summary.workOrderPrepareCount,
    desc: '草稿状态的生产工单',
    desc: '草稿状态的生产订单',
    icon: 'lucide:clipboard-list',
    iconClass: 'bg-blue-50 text-blue-500',
    label: '待排产工单',
    label: '待排产订单',
    routeName: 'MesProWorkOrder',
  },
]);
src/views/mes/home/modules/kpi-cards.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { MesHomeApi } from '#/api/mes/home';
import { computed } from 'vue';
@@ -53,7 +53,7 @@
            <IconifyIcon class="size-7" icon="lucide:file-text" />
          </div>
          <div>
            <div class="text-muted-foreground mb-1 text-sm">生产工单</div>
            <div class="text-muted-foreground mb-1 text-sm">生产订单</div>
            <div class="flex items-baseline gap-1">
              <CountTo
                class="text-2xl font-bold leading-tight text-[#409eff]"
src/views/mes/home/modules/shortcuts.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import { IconifyIcon } from '../../../../packages/icons/src';
import { Card, Col, Row } from 'ant-design-vue';
@@ -9,12 +9,12 @@
  navigate: [name: string];
}>();
/** å¿«æ·å…¥å£åˆ—表,3×3 ç½‘格布局与工单状态分布面板等高 */
/** å¿«æ·å…¥å£åˆ—表,3×3 ç½‘格布局与订单状态分布面板等高 */
const shortcuts = [
  {
    bgColor: '#409EFF',
    icon: 'lucide:file-text',
    name: '生产工单',
    name: '生产订单',
    routeName: 'MesProWorkOrder',
  },
  {
src/views/mes/home/modules/work-order-chart.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { EchartsUIType } from '../../../../packages/effects/plugins/src/echarts';
import { onMounted, ref } from 'vue';
@@ -17,7 +17,7 @@
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
/** åŠ è½½å·¥å•çŠ¶æ€åˆ†å¸ƒæ•°æ®å¹¶æ¸²æŸ“é¥¼å›¾ */
/** åŠ è½½è®¢å•çŠ¶æ€åˆ†å¸ƒæ•°æ®å¹¶æ¸²æŸ“é¥¼å›¾ */
async function loadData() {
  const data = await getWorkOrderStatusDistribution();
  const chartData = data.map((d) => ({
@@ -34,7 +34,7 @@
</script>
<template>
  <Card title="工单状态分布" class="h-full">
  <Card title="订单状态分布" class="h-full">
    <EchartsUI ref="chartRef" class="h-[280px] w-full" />
  </Card>
</template>
src/views/mes/md/item/modules/item-batch-config-form.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesMdItemBatchConfigApi } from '#/api/mes/md/item/batchConfig';
@@ -158,7 +158,7 @@
          v-model:checked="formData.workorderFlag"
          :disabled="isReadOnly"
        >
          ç”Ÿäº§å·¥å•
          ç”Ÿäº§è®¢å•
        </Checkbox>
        <Checkbox v-model:checked="formData.taskFlag" :disabled="isReadOnly">
          ç”Ÿäº§ä»»åŠ¡
src/views/mes/pd/archive/data.ts
@@ -1,71 +1,79 @@
import type { VbenFormSchema } from "#/adapter/form";
import type { VxeTableGridOptions } from "#/adapter/vxe-table";
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE } from "#/packages/constants/src";
import { getDictOptions } from "#/packages/effects/hooks/src";
import { DICT_TYPE } from '#/packages/constants/src';
import { getDictOptions } from '#/packages/effects/hooks/src';
export function useGridFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: "code",
      label: "归档编号",
      component: "Input",
      componentProps: { placeholder: "请输入归档编号" },
      fieldName: 'archiveCode',
      label: '归档编号',
      component: 'Input',
      componentProps: { placeholder: '请输入归档编号' },
    },
    {
      fieldName: "projectCode",
      label: "任务编码",
      component: "Input",
      componentProps: { placeholder: "请输入任务编码" },
      fieldName: 'productCode',
      label: '产品编码',
      component: 'Input',
      componentProps: { placeholder: '请输入产品编码' },
    },
    {
      fieldName: "productCode",
      label: "产品编码",
      component: "Input",
      componentProps: { placeholder: "请输入产品编码" },
      fieldName: 'productName',
      label: '产品名称',
      component: 'Input',
      componentProps: { placeholder: '请输入产品名称' },
    },
    {
      fieldName: "archiveStatus",
      label: "归档状态",
      component: "Select",
      fieldName: 'archiveStatus',
      label: '归档状态',
      component: 'Select',
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_PD_ARCHIVE_STATUS, "number"),
        placeholder: "请选择归档状态",
        options: getDictOptions(DICT_TYPE.MES_PD_ARCHIVE_STATUS, 'number'),
        placeholder: '请选择归档状态',
      },
    },
  ];
}
export function useGridColumns(): VxeTableGridOptions["columns"] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
  return [
    {
      field: "code",
      title: "归档编号",
      field: 'archiveCode',
      title: '归档编号',
      minWidth: 130,
      slots: { default: "code" },
      slots: { default: 'archiveCode' },
    },
    { field: "projectCode", title: "任务编码", minWidth: 120 },
    { field: "projectName", title: "任务名称", minWidth: 140 },
    { field: "productCode", title: "产品编码", minWidth: 120 },
    { field: "productName", title: "产品名称", minWidth: 140 },
    { field: "version", title: "版本号", minWidth: 90 },
    { field: 'productCode', title: '产品编码', minWidth: 120 },
    { field: 'productName', title: '产品名称', minWidth: 140 },
    { field: 'taskCode', title: '任务编码', minWidth: 130 },
    { field: 'taskName', title: '任务名称', minWidth: 160 },
    { field: 'archiveCount', title: '归档数量', minWidth: 90 },
    {
      field: "archiveStatus",
      title: "归档状态",
      field: 'archiveStatus',
      title: '归档状态',
      minWidth: 100,
      cellRender: {
        name: "CellDict",
        name: 'CellDict',
        props: { type: DICT_TYPE.MES_PD_ARCHIVE_STATUS },
      },
    },
    { field: "documentCount", title: "资料数", minWidth: 80 },
    { field: "archiverName", title: "归档人", minWidth: 100 },
    { field: 'archiverNickname', title: '归档人', minWidth: 100 },
    {
      field: "archiveTime",
      title: "归档时间",
      field: 'archiveTime',
      title: '归档时间',
      minWidth: 160,
      formatter: "formatDateTime",
      formatter: 'formatDateTime',
    },
    { field: 'updaterNickname', title: '更新人', minWidth: 100 },
    {
      field: 'updateTime',
      title: '更新时间',
      minWidth: 160,
      formatter: 'formatDateTime',
    },
  ];
}
src/views/mes/pd/archive/index.vue
@@ -18,12 +18,12 @@
const detailRef = ref<InstanceType<typeof Detail>>();
function handleDetail(row: MesPdArchiveApi.Archive) {
  detailRef.value?.open(row.id!);
  detailRef.value?.open(row.productCode!);
}
async function handleExport() {
  const data = await exportPdArchive(await gridApi.formApi.getValues());
  downloadFileFromBlobPart({ fileName: '设计归档台账.xls', source: data });
  downloadFileFromBlobPart({ fileName: '归档台账.xls', source: data });
}
const [Grid, gridApi] = useVbenVxeGrid({
@@ -53,7 +53,7 @@
  <Page auto-content-height>
    <Detail ref="detailRef" />
    <Grid table-title="产品版本归档台账">
    <Grid table-title="归档台账">
      <template #toolbar-tools>
        <TableAction
          :actions="[
@@ -67,8 +67,8 @@
          ]"
        />
      </template>
      <template #code="{ row }">
        <Button type="link" @click="handleDetail(row)">{{ row.code }}</Button>
      <template #archiveCode="{ row }">
        <Button type="link" @click="handleDetail(row)">{{ row.archiveCode }}</Button>
      </template>
    </Grid>
  </Page>
src/views/mes/pd/archive/modules/detail.vue
@@ -1,104 +1,120 @@
<script lang="ts" setup>
import type { MesPdArchiveApi } from '#/api/mes/pd/archive';
import type { MesPdVersionChangeApi } from '#/api/mes/pd/version-change';
import { ref } from 'vue';
import { useVbenModal } from '#/packages/effects/common-ui/src';
import { DICT_TYPE } from '#/packages/constants/src';
import { Alert, Descriptions, Tabs } from 'ant-design-vue';
import { Descriptions, Table, Tabs } from 'ant-design-vue';
import { getPdArchive } from '#/api/mes/pd/archive';
import { getPdArchiveDetailByProduct } from '#/api/mes/pd/archive';
import MaterialPanel from '#/views/mes/pd/project/modules/material-panel.vue';
import VersionChangeList from '#/views/mes/pd/project/modules/version-change/version-change-list.vue';
const loading = ref(false);
const detailList = ref<MesPdArchiveApi.DetailItem[]>([]);
const activeProject = ref('0');
const detail = ref<MesPdArchiveApi.Archive>();
const activeTab = ref('version-change');
const materialVersion = ref<string>();
const materialVersionHint = ref<string>();
const archiveColumns = [
  { title: '归档编号', dataIndex: 'archiveCode', key: 'archiveCode' },
  { title: '归档人', dataIndex: 'archiverNickname', key: 'archiverNickname' },
  { title: '归档状态', dataIndex: 'archiveStatus', key: 'archiveStatus' },
  { title: '归档时间', dataIndex: 'archiveTime', key: 'archiveTime' },
  { title: '备注', dataIndex: 'remark', key: 'remark', ellipsis: true },
];
function handleViewMaterials(row: MesPdVersionChangeApi.VersionChange) {
  materialVersion.value = row.toVersion;
  materialVersionHint.value = `变更单 ${row.code}:${row.fromVersion} â†’ ${row.toVersion}`;
  activeTab.value = 'material';
}
function clearMaterialVersionFilter() {
  materialVersion.value = detail.value?.version;
  materialVersionHint.value = undefined;
}
const documentColumns = [
  { title: '资料名称', dataIndex: 'documentName', key: 'documentName' },
  { title: '资料类型', dataIndex: 'documentType', key: 'documentType' },
  { title: '版本', dataIndex: 'documentVersion', key: 'documentVersion', width: 80 },
  { title: '文件名', dataIndex: 'fileName', key: 'fileName' },
  { title: '上传人', dataIndex: 'uploadUserNickname', key: 'uploadUserNickname' },
  { title: '上传时间', dataIndex: 'uploadTime', key: 'uploadTime' },
  {
    title: '操作',
    key: 'action',
    width: 80,
  },
];
const [Modal, modalApi] = useVbenModal({
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) {
      detail.value = undefined;
      activeTab.value = 'version-change';
      materialVersion.value = undefined;
      materialVersionHint.value = undefined;
      detailList.value = [];
      activeProject.value = '0';
      return;
    }
    const data = modalApi.getData<{ id: number }>();
    modalApi.lock();
    const data = modalApi.getData<{ productCode: number }>();
    loading.value = true;
    try {
      detail.value = await getPdArchive(data.id);
      materialVersion.value = detail.value.version;
      detailList.value = await getPdArchiveDetailByProduct(data.productCode);
      activeProject.value = '0';
    } finally {
      modalApi.unlock();
      loading.value = false;
    }
  },
});
defineExpose({
  open: (id: number) => modalApi.setData({ id }).open(),
  open: (productCode: number) => modalApi.setData({ productCode }).open(),
});
</script>
<template>
  <Modal class="w-4/5" title="归档详情">
    <Descriptions v-if="detail" :column="3" bordered class="mx-4 mb-4" size="small">
      <Descriptions.Item label="归档编号">{{ detail.code }}</Descriptions.Item>
      <Descriptions.Item label="产品编码">{{ detail.productCode }}</Descriptions.Item>
      <Descriptions.Item label="产品名称">{{ detail.productName }}</Descriptions.Item>
      <Descriptions.Item label="设计版本">{{ detail.version }}</Descriptions.Item>
      <Descriptions.Item label="任务编码">{{ detail.projectCode }}</Descriptions.Item>
      <Descriptions.Item label="任务名称">{{ detail.projectName }}</Descriptions.Item>
      <Descriptions.Item label="资料数">{{ detail.documentCount ?? '-' }}</Descriptions.Item>
      <Descriptions.Item label="归档人">{{ detail.archiverName || '-' }}</Descriptions.Item>
      <Descriptions.Item label="归档路径" :span="3">
        {{ detail.storagePath || '-' }}
      </Descriptions.Item>
      <Descriptions.Item label="备注" :span="3">{{ detail.remark || '-' }}</Descriptions.Item>
  <Modal class="w-4/5" title="归档详情" :loading="loading">
    <Tabs v-if="detailList.length > 0" v-model:active-key="activeProject" type="card">
      <Tabs.TabPane
        v-for="(detail, index) in detailList"
        :key="String(index)"
        :tab="detail.project.taskName || '项目'"
      >
        <!-- é¡¹ç›®ä¿¡æ¯ -->
        <Descriptions :column="3" bordered size="small" class="mb-4">
          <Descriptions.Item label="任务编码">{{ detail.project.taskCode }}</Descriptions.Item>
          <Descriptions.Item label="任务名称">{{ detail.project.taskName }}</Descriptions.Item>
          <Descriptions.Item label="产品编码">{{ detail.project.productCode }}</Descriptions.Item>
          <Descriptions.Item label="产品名称">{{ detail.project.productName }}</Descriptions.Item>
          <Descriptions.Item label="版本号">{{ detail.project.version }}</Descriptions.Item>
          <Descriptions.Item label="主设计师">{{ detail.project.chiefDesignerNickname }}</Descriptions.Item>
          <Descriptions.Item label="审核人">{{ detail.project.reviewerNickname }}</Descriptions.Item>
          <Descriptions.Item label="计划完成">{{ detail.project.planFinishTime }}</Descriptions.Item>
    </Descriptions>
    <template v-if="detail?.projectId">
      <Tabs v-model:active-key="activeTab" class="mx-4">
        <Tabs.TabPane key="version-change" tab="版本变更记录">
          <VersionChangeList
            form-type="detail"
            :project-id="detail.projectId"
            :project-version="detail.version"
            @view-materials="handleViewMaterials"
        <!-- å½’档记录 -->
        <div class="mb-4">
          <h4 class="mb-2 text-sm font-medium">归档记录</h4>
          <Table
            :columns="archiveColumns"
            :data-source="detail.archives"
            :pagination="false"
            bordered
            row-key="id"
            size="small"
          />
        </Tabs.TabPane>
        <Tabs.TabPane key="material" tab="版本资料">
          <Alert
            v-if="materialVersionHint"
            class="mb-3"
            closable
            show-icon
            type="info"
            :message="`正在查看 ${materialVersion} ç‰ˆæœ¬èµ„料(${materialVersionHint})`"
            @close="clearMaterialVersionFilter"
          />
          <MaterialPanel
            form-type="detail"
            :project-id="detail.projectId"
            table-title="版本资料"
            :version="materialVersion"
          />
        </div>
        <!-- è®¾è®¡èµ„æ–™ -->
        <div>
          <h4 class="mb-2 text-sm font-medium">设计资料</h4>
          <Table
            :columns="documentColumns"
            :data-source="detail.documents"
            :pagination="false"
            bordered
            row-key="id"
            size="small"
          >
            <template #bodyCell="{ column, record }">
              <template v-if="column.key === 'action'">
                <a v-if="record.fileUrl" :href="record.fileUrl" target="_blank" class="text-primary">下载</a>
                <span v-else class="text-gray-400">-</span>
              </template>
            </template>
          </Table>
        </div>
        </Tabs.TabPane>
      </Tabs>
    </template>
    <div v-else class="py-8 text-center text-gray-400">
      æš‚无归档记录
    </div>
  </Modal>
</template>
src/views/mes/pd/project/data.ts
@@ -1,381 +1,340 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdItemApi } from '#/api/mes/md/item';
import type { MesPdProjectApi } from '#/api/mes/pd/project';
import type { ExtendedFormApi, VbenFormSchema } from "#/adapter/form";
import type { VxeTableGridOptions } from "#/adapter/vxe-table";
import type { MesMdItemApi } from "#/api/mes/md/item";
import type { MesPdProjectApi } from "#/api/mes/pd/project";
import { h, markRaw } from 'vue';
import { h, markRaw } from "vue";
import {
  DICT_TYPE,
  MesAutoCodeRuleCode,
  MesPdDocumentTypeEnum,
} from '#/packages/constants/src';
import { getDictOptions } from '#/packages/effects/hooks/src';
} from "#/packages/constants/src";
import { getDictOptions } from "#/packages/effects/hooks/src";
import { Button } from 'ant-design-vue';
import { Button } from "ant-design-vue";
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { isPdMockEnabled } from '#/api/mes/pd/mock/enabled';
import { mockNextCode } from '#/api/mes/pd/mock/utils';
import { getRangePickerDefaultProps } from '#/utils';
import { MdItemSelect } from '#/views/mes/md/item/components';
import { generateAutoCode } from "#/api/mes/md/autocode/record";
export type FormType = 'create' | 'detail' | 'update';
import { MdItemSelect } from "#/views/mes/md/item/components";
import PdUserSelect from "../project/modules/user-select.vue";
async function generateTaskCode(formApi?: VbenFormApi) {
  const code = isPdMockEnabled()
    ? mockNextCode('DT-2026', Math.floor(Date.now() % 10_000))
    : await generateAutoCode(MesAutoCodeRuleCode.PD_PROJECT_CODE);
  await formApi?.setFieldValue('code', code);
export type FormType = "create" | "detail" | "update";
/** å¼¹çª—内 DatePicker:挂载到 dialog å†…,避免 focus-outside æ‹¦æˆªæ—¥åŽ†ç‚¹å‡» */
function useModalDatePickerProps(options?: {
  disabled?: boolean;
  placeholder?: string;
}) {
  return {
    allowClear: true,
    class: "!w-full",
    disabled: options?.disabled,
    format: "YYYY-MM-DD",
    getPopupContainer: (node: HTMLElement) =>
      (node.closest('[data-slot="dialog-content"]') as HTMLElement) ??
      document.body,
    inputReadOnly: true,
    placeholder: options?.placeholder ?? "请选择日期",
    valueFormat: "x",
  };
}
async function generateTaskCode(formApi?: ExtendedFormApi) {
  const code = await generateAutoCode(MesAutoCodeRuleCode.PD_PROJECT_CODE);
  await formApi?.setFieldValue("taskCode", code);
}
async function syncTaskNameFromProduct(
  formApi: VbenFormApi | undefined,
  productName?: string,
  formApi: ExtendedFormApi | undefined,
  productName?: string
) {
  const version = (await formApi?.getFieldValue('version')) as
    | string
    | undefined;
  const values = await formApi?.getValues();
  const version = values?.version as string | undefined;
  const name = productName?.trim();
  if (name && version?.trim()) {
    await formApi?.setFieldValue('name', `${name} ${version.trim()} è®¾è®¡`);
    await formApi?.setFieldValue("taskName", `${name} ${version.trim()} è®¾è®¡`);
  }
}
/** äº§å“é€‰æ‹©ï¼šé€‰æ‹©åŽè‡ªåŠ¨å¸¦å‡ºäº§å“ç¼–ç  */
function useProductSelectSchema(
  formApi: VbenFormApi | undefined,
  readonly: boolean,
  formApi: ExtendedFormApi | undefined,
  readonly: boolean
): VbenFormSchema[] {
  if (readonly) {
    return [
      {
        fieldName: 'productCode',
        label: '产品编码',
        component: 'Input',
        fieldName: "productCode",
        label: "产品编码",
        component: "Input",
        componentProps: { disabled: true },
      },
      {
        fieldName: 'productName',
        label: '产品名称',
        component: 'Input',
        fieldName: "productName",
        label: "产品名称",
        component: "Input",
        componentProps: { disabled: true },
      },
    ];
  }
  return [
    {
      fieldName: 'productId',
      label: '产品',
      fieldName: "productId",
      label: "产品",
      component: markRaw(MdItemSelect),
      componentProps: {
        placeholder: '请选择产品',
        placeholder: "请选择产品",
        onChange: async (item: MesMdItemApi.Item | undefined) => {
          await formApi?.setFieldValue('productCode', item?.code ?? '');
          await formApi?.setFieldValue('productName', item?.name ?? '');
          await formApi?.setFieldValue("productCode", item?.code ?? "");
          await formApi?.setFieldValue("productName", item?.name ?? "");
          await syncTaskNameFromProduct(formApi, item?.name);
        },
      },
      rules: 'selectRequired',
      rules: "selectRequired",
    },
    {
      fieldName: 'productCode',
      label: '产品编码',
      component: 'Input',
      fieldName: "productCode",
      label: "产品编码",
      component: "Input",
      componentProps: {
        disabled: true,
        placeholder: '选择产品后自动带出',
        placeholder: "选择产品后自动带出",
      },
      rules: 'required',
      rules: "required",
    },
    {
      fieldName: 'productName',
      component: 'Input',
      dependencies: { triggerFields: [''], show: () => false },
      fieldName: "productName",
      component: "Input",
      dependencies: { triggerFields: [""], show: () => false },
    },
  ];
}
/** å‘起设计任务表单 */
export function useCreateTaskSchema(formApi?: VbenFormApi): VbenFormSchema[] {
/** å‘起设计任务 / åˆ›å»ºè¡¨å• */
export function useCreateFormSchema(
  formApi?: ExtendedFormApi
): VbenFormSchema[] {
  return [
    {
      fieldName: 'id',
      component: 'Input',
      dependencies: { triggerFields: [''], show: () => false },
      fieldName: "id",
      component: "Input",
      dependencies: { triggerFields: [""], show: () => false },
    },
    {
      fieldName: 'code',
      label: '任务编码',
      component: 'Input',
      componentProps: { placeholder: '请点击生成任务编码' },
      rules: 'required',
      fieldName: "taskCode",
      label: "任务编码",
      component: "Input",
      componentProps: { placeholder: "请点击生成任务编码" },
      rules: "required",
      suffix: () =>
        h(
          Button,
          { type: 'default', onClick: () => generateTaskCode(formApi) },
          { default: () => '生成' },
          { type: "default", onClick: () => generateTaskCode(formApi) },
          { default: () => "生成" }
        ),
    },
    ...useProductSelectSchema(formApi, false),
    {
      fieldName: 'version',
      label: '设计版本',
      component: 'Input',
      componentProps: { placeholder: '如 V1.0、V2.1' },
      rules: 'required',
      fieldName: "version",
      label: "版本号",
      component: "Input",
      componentProps: { placeholder: "如 V1.0、V2.1" },
      rules: "required",
    },
    {
      fieldName: 'name',
      label: '任务名称',
      component: 'Input',
      componentProps: { placeholder: '将根据产品与版本自动生成,可修改' },
      rules: 'required',
      fieldName: "taskName",
      label: "任务名称",
      component: "Input",
      componentProps: { placeholder: "将根据产品与版本自动生成,可修改" },
      rules: "required",
      dependencies: {
        triggerFields: ['productName', 'version'],
        triggerFields: ["productName", "version"],
        trigger(values: Record<string, string>, form) {
          const productName = values.productName?.trim();
          const version = values.version?.trim();
          if (productName && version) {
            form.setFieldValue('name', `${productName} ${version} è®¾è®¡`);
            form.setFieldValue("taskName", `${productName} ${version} è®¾è®¡`);
          }
        },
      },
    },
    {
      fieldName: 'designerName',
      label: '主设计师',
      component: 'Input',
      componentProps: { placeholder: '请输入主设计师' },
      rules: 'required',
      fieldName: "chiefDesigner",
      label: "主设计师",
      component: markRaw(PdUserSelect),
      componentProps: { placeholder: "请选择主设计师" },
      rules: "selectRequired",
    },
    {
      fieldName: 'reviewerName',
      label: '审核人',
      component: 'Input',
      componentProps: { placeholder: '资料审核负责人' },
      rules: 'required',
      fieldName: "reviewer",
      label: "审核人",
      component: markRaw(PdUserSelect),
      componentProps: { placeholder: "请选择审核人" },
      rules: "selectRequired",
    },
    {
      fieldName: 'planDateRange',
      label: '计划周期',
      component: 'RangePicker',
      componentProps: getRangePickerDefaultProps(),
      rules: 'required',
      fieldName: "planFinishTime",
      label: "计划完成时间",
      component: "DatePicker",
      componentProps: useModalDatePickerProps({
        placeholder: "请选择计划完成时间",
      }),
    },
    {
      fieldName: 'remark',
      label: '任务说明',
      component: 'Textarea',
      fieldName: "remark",
      label: "备注",
      component: "Textarea",
      componentProps: {
        placeholder: '简述本次设计目标、变更背景等',
        placeholder: "简述本次设计目标、变更背景等",
        rows: 3,
      },
      formItemClass: 'col-span-2',
      formItemClass: "col-span-2",
    },
  ];
}
/** å‘布任务确认表单 */
export function usePublishFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'publishRemark',
      label: '发布说明',
      component: 'Textarea',
      componentProps: {
        placeholder: '可选,填写发布要求或注意事项',
        rows: 3,
      },
      formItemClass: 'col-span-2',
    },
  ];
}
export type WorkflowActionType = 'approve' | 'archive' | 'submit-review';
export interface WorkflowActionConfig {
  title: string;
  alertMessage: string;
  alertDescription: string;
  remarkLabel: string;
  remarkTag: string;
  remarkPlaceholder: string;
  successMessage: string;
  alertType: 'info' | 'success' | 'warning';
}
export const WORKFLOW_ACTION_CONFIG: Record<
  WorkflowActionType,
  WorkflowActionConfig
> = {
  'submit-review': {
    title: '提交资料审核',
    alertMessage: '确认提交审核',
    alertDescription:
      '提交后将进入资料审核环节,请确认设计资料已上传完整。提交后设计师将无法继续修改资料。',
    remarkLabel: '提交说明',
    remarkTag: '【提交说明】',
    remarkPlaceholder: '可选,填写提交审核的补充说明',
    successMessage: '已提交资料审核',
    alertType: 'info',
  },
  approve: {
    title: '审核通过',
    alertMessage: '确认审核通过',
    alertDescription:
      '审核通过后任务进入「已通过」状态,可进行归档操作。请确认设计资料符合要求。',
    remarkLabel: '审核意见',
    remarkTag: '【审核意见】',
    remarkPlaceholder: '可选,填写审核通过意见',
    successMessage: '资料审核通过',
    alertType: 'success',
  },
  archive: {
    title: '归档设计任务',
    alertMessage: '确认归档',
    alertDescription:
      '归档后将生成本版本归档台账记录,任务状态变为已归档,设计资料将锁定不可修改。',
    remarkLabel: '归档说明',
    remarkTag: '【归档说明】',
    remarkPlaceholder: '可选,填写归档备注',
    successMessage: '任务已归档',
    alertType: 'warning',
  },
};
/** æµç¨‹æ“ä½œç¡®è®¤è¡¨å• */
export function useWorkflowActionFormSchema(
  action: WorkflowActionType,
): VbenFormSchema[] {
  const config = WORKFLOW_ACTION_CONFIG[action];
  return [
    {
      fieldName: 'actionRemark',
      label: config.remarkLabel,
      component: 'Textarea',
      componentProps: {
        placeholder: config.remarkPlaceholder,
        rows: 3,
      },
      formItemClass: 'col-span-2',
    },
  ];
}
/** ç½‘格搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'code',
      label: '任务编码',
      component: 'Input',
      componentProps: { placeholder: '请输入任务编码' },
      fieldName: "taskCode",
      label: "任务编码",
      component: "Input",
      componentProps: { placeholder: "请输入任务编码" },
    },
    {
      fieldName: 'name',
      label: '任务名称',
      component: 'Input',
      componentProps: { placeholder: '请输入任务名称' },
      fieldName: "taskName",
      label: "任务名称",
      component: "Input",
      componentProps: { placeholder: "请输入任务名称" },
    },
    {
      fieldName: 'productCode',
      label: '产品编码',
      component: 'Input',
      componentProps: { placeholder: '请输入产品编码' },
      fieldName: "productCode",
      label: "产品编码",
      component: "Input",
      componentProps: { placeholder: "请输入产品编码" },
    },
    {
      fieldName: 'status',
      label: '任务状态',
      component: 'Select',
      fieldName: "productName",
      label: "产品名称",
      component: "Input",
      componentProps: { placeholder: "请输入产品名称" },
    },
    {
      fieldName: "status",
      label: "任务状态",
      component: "Select",
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_PD_PROJECT_STATUS, 'number'),
        placeholder: '请选择项目状态',
        options: getDictOptions(DICT_TYPE.MES_PD_PROJECT_STATUS, "number"),
        placeholder: "请选择项目状态",
      },
    },
  ];
}
export function useGridColumns(): VxeTableGridOptions['columns'] {
/** ç½‘格列定义 */
export function useGridColumns(): VxeTableGridOptions["columns"] {
  return [
    { field: 'code', title: '任务编码', minWidth: 140, slots: { default: 'code' } },
    { field: 'name', title: '任务名称', minWidth: 160 },
    { field: 'productCode', title: '产品编码', minWidth: 120 },
    { field: 'productName', title: '产品名称', minWidth: 140 },
    { field: 'version', title: '版本号', minWidth: 90 },
    {
      field: 'status',
      title: '状态',
      field: "taskCode",
      title: "任务编码",
      minWidth: 140,
      slots: { default: "taskCode" },
    },
    { field: "taskName", title: "任务名称", minWidth: 160 },
    { field: "productCode", title: "产品编码", minWidth: 120 },
    { field: "productName", title: "产品名称", minWidth: 140 },
    { field: "version", title: "版本号", minWidth: 90 },
    {
      field: "status",
      title: "状态",
      minWidth: 100,
      cellRender: {
        name: 'CellDict',
        name: "CellDict",
        props: { type: DICT_TYPE.MES_PD_PROJECT_STATUS },
      },
    },
    { field: 'designerName', title: '主设计师', minWidth: 100 },
    { field: 'reviewerName', title: '审核人', minWidth: 100 },
    { field: 'planEndDate', title: '计划完成', minWidth: 120, formatter: 'formatDate' },
    { field: 'createTime', title: '创建时间', minWidth: 160, formatter: 'formatDateTime' },
    { field: 'action', title: '操作', width: 220, fixed: 'right', slots: { default: 'actions' } },
    { field: "chiefDesignerNickname", title: "主设计师", minWidth: 100 },
    { field: "reviewerNickname", title: "审核人", minWidth: 100 },
    {
      field: "planFinishTime",
      title: "计划完成",
      minWidth: 120,
      formatter: "formatDate",
    },
    {
      field: "createTime",
      title: "创建时间",
      minWidth: 160,
      formatter: "formatDateTime",
    },
    {
      field: "action",
      title: "操作",
      width: 260,
      fixed: "right",
      slots: { default: "actions" },
    },
  ];
}
/** ç¼–辑/详情表单 */
export function useFormSchema(
  formType: FormType,
  formApi?: VbenFormApi,
  formApi?: ExtendedFormApi
): VbenFormSchema[] {
  const readonly = formType === 'detail';
  const readonly = formType === "detail";
  return [
    {
      fieldName: 'id',
      component: 'Input',
      dependencies: { triggerFields: [''], show: () => false },
      fieldName: "id",
      component: "Input",
      dependencies: { triggerFields: [""], show: () => false },
    },
    {
      fieldName: 'code',
      label: '任务编码',
      component: 'Input',
      componentProps: { disabled: readonly, placeholder: '请输入任务编码' },
      rules: 'required',
      fieldName: "taskCode",
      label: "任务编码",
      component: "Input",
      componentProps: { disabled: readonly, placeholder: "请输入任务编码" },
      rules: "required",
      suffix:
        formType === 'detail'
        formType === "detail"
          ? undefined
          : () =>
              h(
                Button,
                {
                  type: 'default',
                  type: "default",
                  onClick: async () => {
                    const code = isPdMockEnabled()
                      ? mockNextCode(
                          'DT-2026',
                          Math.floor(Date.now() % 10_000),
                        )
                      : await generateAutoCode(
                          MesAutoCodeRuleCode.PD_PROJECT_CODE,
                    const code = await generateAutoCode(
                      MesAutoCodeRuleCode.PD_PROJECT_CODE
                        );
                    await formApi?.setFieldValue('code', code);
                    await formApi?.setFieldValue("taskCode", code);
                  },
                },
                { default: () => '生成' },
                { default: () => "生成" }
              ),
    },
    ...useProductSelectSchema(formApi, readonly),
    {
      fieldName: 'version',
      label: '版本号',
      component: 'Input',
      componentProps: { disabled: readonly, placeholder: '如 V1.0' },
      rules: 'required',
      fieldName: "version",
      label: "版本号",
      component: "Input",
      componentProps: { disabled: readonly, placeholder: "如 V1.0" },
      rules: "required",
    },
    {
      fieldName: 'name',
      label: '任务名称',
      component: 'Input',
      componentProps: { disabled: readonly, placeholder: '如:智能温控模组 V1.0 è®¾è®¡' },
      rules: 'required',
      fieldName: "taskName",
      label: "任务名称",
      component: "Input",
      componentProps: {
        disabled: readonly,
        placeholder: "如:智能温控模组 V1.0 è®¾è®¡",
      },
      rules: "required",
      dependencies: {
        triggerFields: ['productName', 'version'],
        triggerFields: ["productName", "version"],
        trigger(values: Record<string, string>, form) {
          if (readonly) {
            return;
@@ -383,152 +342,145 @@
          const productName = values.productName?.trim();
          const version = values.version?.trim();
          if (productName && version) {
            form.setFieldValue('name', `${productName} ${version} è®¾è®¡`);
            form.setFieldValue("taskName", `${productName} ${version} è®¾è®¡`);
          }
        },
      },
    },
    {
      fieldName: 'status',
      label: '任务状态',
      component: 'Select',
      fieldName: "status",
      label: "任务状态",
      component: "Select",
      componentProps: {
        disabled: true,
        options: getDictOptions(DICT_TYPE.MES_PD_PROJECT_STATUS, 'number'),
        options: getDictOptions(DICT_TYPE.MES_PD_PROJECT_STATUS, "number"),
      },
      dependencies: { triggerFields: [''], show: () => formType !== 'create' },
      dependencies: { triggerFields: [""], show: () => formType !== "create" },
    },
    {
      fieldName: 'designerName',
      label: '主设计师',
      component: 'Input',
      componentProps: { disabled: readonly, placeholder: '请输入主设计师' },
      fieldName: "chiefDesigner",
      label: "主设计师",
      component: markRaw(PdUserSelect),
      componentProps: { disabled: readonly, placeholder: "请选择主设计师" },
    },
    {
      fieldName: 'reviewerName',
      label: '审核人',
      component: 'Input',
      componentProps: { disabled: readonly, placeholder: '请输入审核人' },
      fieldName: "reviewer",
      label: "审核人",
      component: markRaw(PdUserSelect),
      componentProps: { disabled: readonly, placeholder: "请选择审核人" },
    },
    {
      fieldName: 'planDateRange',
      label: '计划周期',
      component: 'RangePicker',
      componentProps: {
        ...getRangePickerDefaultProps(),
      fieldName: "planFinishTime",
      label: "计划完成时间",
      component: "DatePicker",
      componentProps: useModalDatePickerProps({
        disabled: readonly,
      },
        placeholder: "请选择计划完成时间",
      }),
    },
    {
      fieldName: 'remark',
      label: '备注',
      component: 'Textarea',
      componentProps: { disabled: readonly, placeholder: '请输入备注', rows: 3 },
      formItemClass: 'col-span-3',
      fieldName: "remark",
      label: "备注",
      component: "Textarea",
      componentProps: {
        disabled: readonly,
        placeholder: "请输入备注",
        rows: 3,
      },
      formItemClass: "col-span-3",
    },
  ];
}
/** è®¾è®¡èµ„料类型选项(上传时选择) */
/** è®¾è®¡èµ„料类型选项 */
export function getPdDocumentTypeOptions() {
  return [
    { label: '设计方案', value: MesPdDocumentTypeEnum.SCHEME },
    { label: '设计图纸', value: MesPdDocumentTypeEnum.DRAWING },
    { label: '设计模型', value: MesPdDocumentTypeEnum.MODEL },
    { label: '产品设计', value: MesPdDocumentTypeEnum.PRODUCT_DESIGN },
    { label: 'BOM æ¸…单', value: MesPdDocumentTypeEnum.BOM },
    { label: '其他附件', value: MesPdDocumentTypeEnum.OTHER },
    { label: "设计方案", value: MesPdDocumentTypeEnum.SCHEME },
    { label: "设计图纸", value: MesPdDocumentTypeEnum.DRAWING },
    { label: "设计模型", value: MesPdDocumentTypeEnum.MODEL },
    { label: "产品设计", value: MesPdDocumentTypeEnum.PRODUCT_DESIGN },
    { label: "BOM æ¸…单", value: MesPdDocumentTypeEnum.BOM },
    { label: "其他附件", value: MesPdDocumentTypeEnum.OTHER },
  ];
}
/** ç»Ÿä¸€è®¾è®¡èµ„料列表列 */
/** èµ„料列表搜索表单 */
export function useMaterialListGridFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'name',
      label: '资料名称',
      component: 'Input',
      componentProps: { placeholder: '请输入资料名称' },
      fieldName: "documentName",
      label: "资料名称",
      component: "Input",
      componentProps: { placeholder: "请输入资料名称" },
    },
    {
      fieldName: 'documentType',
      label: '资料类型',
      component: 'Select',
      fieldName: "documentType",
      label: "资料类型",
      component: "Select",
      componentProps: {
        allowClear: true,
        options: getPdDocumentTypeOptions(),
        placeholder: '请选择资料类型',
        placeholder: "请选择资料类型",
      },
    },
  ];
}
export function useMaterialListGridColumns(): VxeTableGridOptions['columns'] {
/** èµ„料列表列定义 */
export function useMaterialListGridColumns(): VxeTableGridOptions["columns"] {
  return [
    {
      field: 'documentType',
      title: '资料类型',
      field: "documentType",
      title: "资料类型",
      minWidth: 110,
      formatter: ({ cellValue }: { cellValue: number }) => {
      formatter: ({ cellValue }: { cellValue: string }) => {
        const option = getPdDocumentTypeOptions().find(
          (item) => item.value === cellValue,
          (item) => item.value === cellValue
        );
        return option?.label ?? String(cellValue ?? '');
        return option?.label ?? String(cellValue ?? "");
      },
    },
    { field: 'name', title: '资料名称', minWidth: 180 },
    { field: 'version', title: '版本', minWidth: 90 },
    { field: 'fileName', title: '文件名', minWidth: 200 },
    { field: 'uploaderName', title: '上传人', minWidth: 100 },
    { field: "documentName", title: "资料名称", minWidth: 180 },
    { field: "documentVersion", title: "版本", minWidth: 90 },
    {
      field: 'createTime',
      title: '上传时间',
      field: "fileUrl",
      title: "文件",
      minWidth: 140,
      slots: { default: "file" },
    },
    { field: "fileName", title: "文件名", minWidth: 160 },
    { field: "uploadUserNickname", title: "上传人", minWidth: 100 },
    {
      field: "uploadTime",
      title: "上传时间",
      minWidth: 160,
      formatter: 'formatDateTime',
      formatter: "formatDateTime",
    },
    {
      field: 'action',
      title: '操作',
      field: "action",
      title: "操作",
      width: 140,
      fixed: 'right',
      slots: { default: 'actions' },
      fixed: "right",
      slots: { default: "actions" },
    },
  ];
}
/** @deprecated æŒ‰ç±»åž‹åˆ† Tab æ—¶ä½¿ç”¨ï¼Œè¯·æ”¹ç”¨ useMaterialListGridColumns */
export function useDocumentGridColumns(documentType: number): VxeTableGridOptions['columns'] {
  const typeLabel =
    documentType === MesPdDocumentTypeEnum.SCHEME
      ? '方案'
      : documentType === MesPdDocumentTypeEnum.DRAWING
        ? '图纸'
        : documentType === MesPdDocumentTypeEnum.MODEL
          ? '模型'
          : documentType === MesPdDocumentTypeEnum.PRODUCT_DESIGN
            ? '产品设计'
            : '资料';
  return [
    { field: 'name', title: `${typeLabel}名称`, minWidth: 180 },
    { field: 'version', title: '版本', minWidth: 90 },
    { field: 'fileName', title: '文件名', minWidth: 200 },
    { field: 'uploaderName', title: '上传人', minWidth: 100 },
    { field: 'createTime', title: '上传时间', minWidth: 160, formatter: 'formatDateTime' },
    { field: 'action', title: '操作', width: 140, slots: { default: 'actions' } },
  ];
}
/** BOM è¡¨æ ¼åˆ— */
export function useBomGridColumns(): VxeTableGridOptions['columns'] {
export function useBomGridColumns(): VxeTableGridOptions["columns"] {
  return [
    { field: 'itemCode', title: '物料编码', minWidth: 120 },
    { field: 'itemName', title: '物料名称', minWidth: 160 },
    { field: 'specification', title: '规格型号', minWidth: 140 },
    { field: 'unitMeasureName', title: '单位', minWidth: 80 },
    { field: 'quantity', title: '用量', minWidth: 90 },
    { field: 'remark', title: '备注', minWidth: 140 },
    { field: 'action', title: '操作', width: 140, slots: { default: 'actions' } },
    { field: "itemCode", title: "物料编码", minWidth: 120 },
    { field: "itemName", title: "物料名称", minWidth: 160 },
    { field: "specification", title: "规格型号", minWidth: 140 },
    { field: "unitMeasureName", title: "单位", minWidth: 80 },
    { field: "quantity", title: "用量", minWidth: 90 },
    { field: "remark", title: "备注", minWidth: 140 },
    {
      field: "action",
      title: "操作",
      width: 140,
      slots: { default: "actions" },
    },
  ];
}
src/views/mes/pd/project/detail/data.ts
@@ -10,8 +10,8 @@
/** è®¾è®¡ä»»åŠ¡è¯¦æƒ…æ‘˜è¦ */
export function useDetailSchema(): DescriptionItemSchema[] {
  return [
    { field: 'code', label: '任务编码' },
    { field: 'name', label: '任务名称' },
    { field: 'taskCode', label: '任务编码' },
    { field: 'taskName', label: '任务名称' },
    { field: 'productCode', label: '产品编码' },
    { field: 'productName', label: '产品名称' },
    { field: 'version', label: '设计版本' },
@@ -21,21 +21,16 @@
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.MES_PD_PROJECT_STATUS, value: val }),
    },
    { field: 'designerName', label: '设计师' },
    { field: 'reviewerName', label: '审核人' },
    { field: 'chiefDesignerNickname', label: '主设计师' },
    { field: 'reviewerNickname', label: '审核人' },
    {
      field: 'planStartDate',
      label: '计划开始',
      render: (val) => (val ? formatDate(val) : '-'),
    },
    {
      field: 'planEndDate',
      field: 'planFinishTime',
      label: '计划完成',
      render: (val) => (val ? formatDate(val) : '-'),
    },
    {
      field: 'createTime',
      label: '发起时间',
      label: '创建时间',
      render: (val) => (val ? formatDateTime(val) : '-'),
    },
    { field: 'remark', label: '备注' },
src/views/mes/pd/project/detail/index.vue
@@ -9,22 +9,24 @@
import { useTabs } from '#/packages/effects/hooks/src';
import { MesPdProjectStatusEnum } from '#/packages/constants/src';
import { Card, Tabs } from 'ant-design-vue';
import { Card, message, Tabs } from 'ant-design-vue';
import {
  archivePdProject,
  auditPdProject,
  getPdProject,
  publishPdProject,
  uploadPdProject,
} from '#/api/mes/pd/project';
import { useDescription } from '#/components/description';
import { ACTION_ICON, TableAction } from '#/adapter/vxe-table';
import { $t } from '#/locales';
import Form from '../modules/form.vue';
import PublishForm from '../modules/publish-form.vue';
import WorkflowActionForm from '../modules/workflow-action-form.vue';
import ReviewList from '../modules/drawing-review/review-list.vue';
import MaterialPanel from '../modules/material-panel.vue';
import TaskDraftBanner from '../modules/task-draft-banner.vue';
import DocumentAuditList from '../modules/document-audit-list.vue';
import DocumentList from '../modules/document-list.vue';
import WorkflowTimeline from '../modules/workflow-timeline.vue';
import TaskDraftBanner from '../modules/task-draft-banner.vue';
import { useDetailSchema } from './data';
const route = useRoute();
@@ -35,37 +37,29 @@
const projectId = ref(0);
const project = ref<MesPdProjectApi.Project>({});
const activeTab = ref('workflow');
const workflowKey = ref(0);
const isDraft = computed(
  () => project.value.status === MesPdProjectStatusEnum.DRAFT,
const isPendingPublish = computed(
  () => project.value.status === MesPdProjectStatusEnum.PENDING_PUBLISH,
);
const isDesigning = computed(
  () => project.value.status === MesPdProjectStatusEnum.DESIGNING,
const isPendingUpload = computed(
  () => project.value.status === MesPdProjectStatusEnum.PENDING_UPLOAD,
);
const isReviewing = computed(
  () => project.value.status === MesPdProjectStatusEnum.REVIEWING,
const isPendingAudit = computed(
  () => project.value.status === MesPdProjectStatusEnum.PENDING_AUDIT,
);
const isApproved = computed(
  () => project.value.status === MesPdProjectStatusEnum.APPROVED,
const isPendingArchive = computed(
  () => project.value.status === MesPdProjectStatusEnum.PENDING_ARCHIVE,
);
const isArchived = computed(
  () => project.value.status === MesPdProjectStatusEnum.ARCHIVED,
);
const isFormEditable = computed(
  () =>
    !isArchived.value &&
    project.value.status !== MesPdProjectStatusEnum.CANCELLED &&
    (isDraft.value || isDesigning.value),
  () => isPendingPublish.value,
);
const materialFormType = computed<FormType>(() =>
  isDesigning.value ? 'update' : 'detail',
);
const showReviewTab = computed(
  () => isReviewing.value || isApproved.value || isArchived.value,
  isPendingPublish.value || isPendingUpload.value || isPendingAudit.value ? 'update' : 'detail',
);
const [Descriptions] = useDescription({
@@ -79,25 +73,14 @@
  destroyOnClose: true,
});
const [PublishModal, publishModalApi] = useVbenModal({
  connectedComponent: PublishForm,
  destroyOnClose: true,
});
const [WorkflowActionModal, workflowActionModalApi] = useVbenModal({
  connectedComponent: WorkflowActionForm,
  destroyOnClose: true,
});
async function loadProjectDetail() {
  loading.value = true;
  try {
    project.value = await getPdProject(projectId.value);
    workflowKey.value += 1;
    if (isDesigning.value) {
    if (isPendingUpload.value) {
      activeTab.value = 'material';
    } else if (isReviewing.value) {
      activeTab.value = 'review';
    } else if (isPendingAudit.value) {
      activeTab.value = 'audit';
    }
  } finally {
    loading.value = false;
@@ -113,12 +96,50 @@
  formModalApi.setData({ formType: 'update', id: projectId.value }).open();
}
function handlePublish() {
  publishModalApi.setData({ id: projectId.value }).open();
async function handlePublish() {
  const hideLoading = message.loading({ content: '发布中…', duration: 0 });
  try {
    await publishPdProject(projectId.value);
    message.success('任务已发布,进入待上传状态');
    loadProjectDetail();
  } finally {
    hideLoading();
  }
}
function handleWorkflowAction(action: 'approve' | 'archive' | 'submit-review') {
  workflowActionModalApi.setData({ id: projectId.value, action }).open();
async function handleUpload() {
  const hideLoading = message.loading({ content: '提交审核中…', duration: 0 });
  try {
    await uploadPdProject(projectId.value);
    message.success('已提交资料审核');
    tabs.closeCurrentTab();
    router.back();
  } finally {
    hideLoading();
  }
}
async function handleAudit() {
  const hideLoading = message.loading({ content: '审核中…', duration: 0 });
  try {
    await auditPdProject(projectId.value);
    message.success('审核通过,进入待归档状态');
    tabs.closeCurrentTab();
    router.back();
  } finally {
    hideLoading();
  }
}
async function handleArchive() {
  const hideLoading = message.loading({ content: '归档中…', duration: 0 });
  try {
    await archivePdProject(projectId.value);
    message.success('已归档');
    loadProjectDetail();
  } finally {
    hideLoading();
  }
}
onMounted(() => {
@@ -131,11 +152,9 @@
  <Page
    auto-content-height
    :loading="loading"
    :title="project.name || '设计任务详情'"
    :title="project.taskName || '设计任务详情'"
  >
    <FormModal @success="loadProjectDetail" />
    <PublishModal @success="loadProjectDetail" />
    <WorkflowActionModal @success="loadProjectDetail" />
    <template #extra>
      <TableAction
@@ -155,40 +174,40 @@
            onClick: handleEdit,
          },
          {
            label: '发布任务',
            label: '发布',
            type: 'primary',
            auth: ['mes:pd-project:update'],
            ifShow: isDraft,
            ifShow: isPendingPublish,
            onClick: handlePublish,
          },
          {
            label: '提交审核',
            type: 'default',
            auth: ['mes:pd-project:submit-review'],
            ifShow: isDesigning,
            onClick: () => handleWorkflowAction('submit-review'),
            label: '确认上传',
            type: 'primary',
            auth: ['mes:pd-project:update'],
            ifShow: isPendingUpload,
            onClick: handleUpload,
          },
          {
            label: '审核通过',
            type: 'primary',
            auth: ['mes:pd-drawing-review:review'],
            ifShow: isReviewing,
            onClick: () => handleWorkflowAction('approve'),
            auth: ['mes:pd-project:audit'],
            ifShow: isPendingAudit,
            onClick: handleAudit,
          },
          {
            label: '归档',
            type: 'default',
            auth: ['mes:pd-project:archive'],
            ifShow: isApproved,
            onClick: () => handleWorkflowAction('archive'),
            ifShow: isPendingArchive,
            onClick: handleArchive,
          },
        ]"
      />
    </template>
    <TaskDraftBanner
      v-if="isDraft"
      :designer-name="project.designerName"
      v-if="isPendingPublish"
      :designer-name="project.chiefDesignerNickname"
      :product-name="project.productName"
      :version="project.version"
      @edit="handleEdit"
@@ -202,21 +221,20 @@
    <Card class="mt-4 min-h-[60%]">
      <Tabs v-model:active-key="activeTab">
        <Tabs.TabPane key="workflow" tab="流程进度" :force-render="true">
          <WorkflowTimeline :key="workflowKey" :project-id="projectId" />
          <WorkflowTimeline :status="project.status ?? 0" />
        </Tabs.TabPane>
        <Tabs.TabPane key="material" tab="设计资料" :force-render="true">
          <p v-if="isDraft" class="mb-3 text-sm text-amber-600">
        <Tabs.TabPane v-if="!isPendingAudit" key="material" tab="设计资料" :force-render="true">
          <p v-if="isPendingPublish" class="mb-3 text-sm text-amber-600">
            è¯·å…ˆå‘布任务后再上传设计资料
          </p>
          <MaterialPanel :form-type="materialFormType" :project-id="projectId" />
          <DocumentList
            :form-type="materialFormType"
            :project-id="projectId"
            :project-status="project.status"
          />
        </Tabs.TabPane>
        <Tabs.TabPane
          v-if="showReviewTab"
          key="review"
          tab="资料审核"
          :force-render="true"
        >
          <ReviewList form-type="detail" :project-id="projectId" />
        <Tabs.TabPane v-if="isPendingAudit" key="audit" tab="资料审核" :force-render="true">
          <DocumentAuditList :project-id="projectId" @success="loadProjectDetail" />
        </Tabs.TabPane>
      </Tabs>
    </Card>
src/views/mes/pd/project/index.vue
@@ -2,6 +2,7 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesPdProjectApi } from '#/api/mes/pd/project';
import { onActivated } from 'vue';
import { useRouter } from 'vue-router';
import { Page, useVbenModal } from '#/packages/effects/common-ui/src';
@@ -12,17 +13,19 @@
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
  archivePdProject,
  deletePdProject,
  exportPdProject,
  getPdProjectPage,
  publishPdProject,
} from '#/api/mes/pd/project';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
import CreateForm from './modules/create-form.vue';
import Form from './modules/form.vue';
import PublishForm from './modules/publish-form.vue';
import WorkflowActionForm from './modules/workflow-action-form.vue';
const router = useRouter();
@@ -36,23 +39,12 @@
  destroyOnClose: true,
});
const [PublishModal, publishModalApi] = useVbenModal({
  connectedComponent: PublishForm,
  destroyOnClose: true,
});
const [WorkflowActionModal, workflowActionModalApi] = useVbenModal({
  connectedComponent: WorkflowActionForm,
  destroyOnClose: true,
});
function handleRefresh() {
  gridApi.query();
}
function handleCreateSuccess(projectId: number) {
function handleCreateSuccess() {
  handleRefresh();
  router.push({ name: 'MesPdProjectDetail', params: { id: projectId } });
}
function handleCreate() {
@@ -67,25 +59,42 @@
  formModalApi.setData({ formType: 'update', id: row.id }).open();
}
function handlePublish(row: MesPdProjectApi.Project) {
  publishModalApi.setData({ id: row.id }).open();
async function handlePublish(row: MesPdProjectApi.Project) {
  const hideLoading = message.loading({
    content: '发布中…',
    duration: 0,
  });
  try {
    await publishPdProject(row.id!);
    message.success('任务已发布,进入待上传状态');
    handleRefresh();
  } finally {
    hideLoading();
  }
}
function handleWorkflowAction(
  row: MesPdProjectApi.Project,
  action: 'approve' | 'archive' | 'submit-review',
) {
  workflowActionModalApi.setData({ id: row.id, action }).open();
async function handleArchive(row: MesPdProjectApi.Project) {
  const hideLoading = message.loading({
    content: '归档中…',
    duration: 0,
  });
  try {
    await archivePdProject(row.id!);
    message.success('已归档');
    handleRefresh();
  } finally {
    hideLoading();
  }
}
async function handleDelete(row: MesPdProjectApi.Project) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting', [row.name]),
    content: $t('ui.actionMessage.deleting', [row.taskName]),
    duration: 0,
  });
  try {
    await deletePdProject(row.id!);
    message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
    message.success($t('ui.actionMessage.deleteSuccess', [row.taskName]));
    handleRefresh();
  } finally {
    hideLoading();
@@ -118,21 +127,23 @@
    toolbarConfig: { refresh: true, search: true },
  } as VxeTableGridOptions<MesPdProjectApi.Project>,
});
onActivated(() => {
  handleRefresh();
});
</script>
<template>
  <Page auto-content-height>
    <CreateFormModal @success="handleCreateSuccess" />
    <FormModal @success="handleRefresh" />
    <PublishModal @success="handleRefresh" />
    <WorkflowActionModal @success="handleRefresh" />
    <Grid table-title="设计任务">
      <template #toolbar-tools>
        <TableAction
          :actions="[
            {
              label: '发起设计任务',
              label: '创建设计任务',
              type: 'primary',
              icon: ACTION_ICON.ADD,
              auth: ['mes:pd-project:create'],
@@ -148,49 +159,53 @@
          ]"
        />
      </template>
      <template #code="{ row }">
        <Button type="link" @click="handleDetail(row)">{{ row.code }}</Button>
      <template #taskCode="{ row }">
        <Button type="link" @click="handleDetail(row)">{{ row.taskCode }}</Button>
      </template>
      <template #actions="{ row }">
        <TableAction
          :actions="[
            {
              label: '发布任务',
              label: '发布',
              type: 'link',
              auth: ['mes:pd-project:update'],
              ifShow: row.status === MesPdProjectStatusEnum.DRAFT,
              onClick: handlePublish.bind(null, row),
              ifShow: row.status === MesPdProjectStatusEnum.PENDING_PUBLISH,
              popConfirm: {
                title: '确认发布该任务?发布后进入待上传状态。',
                confirm: handlePublish.bind(null, row),
              },
            },
            {
              label: '上传资料',
              type: 'link',
              auth: ['mes:pd-project:update'],
              ifShow: row.status === MesPdProjectStatusEnum.PENDING_UPLOAD,
              onClick: () => handleDetail(row),
            },
            {
              label: '审核',
              type: 'link',
              auth: ['mes:pd-project:audit'],
              ifShow: row.status === MesPdProjectStatusEnum.PENDING_AUDIT,
              onClick: () => handleDetail(row),
            },
            {
              label: '归档',
              type: 'link',
              auth: ['mes:pd-project:archive'],
              ifShow: row.status === MesPdProjectStatusEnum.PENDING_ARCHIVE,
              popConfirm: {
                title: '确认归档?归档后将生成台账记录,任务不可再修改。',
                confirm: handleArchive.bind(null, row),
              },
            },
            {
              label: $t('common.edit'),
              type: 'link',
              icon: ACTION_ICON.EDIT,
              auth: ['mes:pd-project:update'],
              ifShow:
                row.status === MesPdProjectStatusEnum.DRAFT ||
                row.status === MesPdProjectStatusEnum.DESIGNING,
              ifShow: row.status === MesPdProjectStatusEnum.PENDING_PUBLISH,
              onClick: handleEdit.bind(null, row),
            },
            {
              label: '提交审核',
              type: 'link',
              auth: ['mes:pd-project:submit-review'],
              ifShow: row.status === MesPdProjectStatusEnum.DESIGNING,
              onClick: () => handleWorkflowAction(row, 'submit-review'),
            },
            {
              label: '审核通过',
              type: 'link',
              auth: ['mes:pd-drawing-review:review'],
              ifShow: row.status === MesPdProjectStatusEnum.REVIEWING,
              onClick: () => handleWorkflowAction(row, 'approve'),
            },
            {
              label: '归档',
              type: 'link',
              auth: ['mes:pd-project:archive'],
              ifShow: row.status === MesPdProjectStatusEnum.APPROVED,
              onClick: () => handleWorkflowAction(row, 'archive'),
            },
            {
              label: $t('common.delete'),
@@ -198,9 +213,9 @@
              danger: true,
              icon: ACTION_ICON.DELETE,
              auth: ['mes:pd-project:delete'],
              ifShow: row.status === MesPdProjectStatusEnum.DRAFT,
              ifShow: row.status === MesPdProjectStatusEnum.PENDING_PUBLISH,
              popConfirm: {
                title: $t('ui.actionMessage.deleteConfirm', [row.name]),
                title: $t('ui.actionMessage.deleteConfirm', [row.taskName]),
                confirm: handleDelete.bind(null, row),
              },
            },
src/views/mes/pd/project/modules/create-form.vue
@@ -9,10 +9,10 @@
import { createPdProject } from '#/api/mes/pd/project';
import { $t } from '#/locales';
import { useCreateTaskSchema } from '../data';
import { useCreateFormSchema } from '../data';
const emit = defineEmits<{
  success: [projectId: number];
  success: [];
}>();
const [Form, formApi] = useVbenForm({
@@ -35,18 +35,16 @@
    }
    modalApi.lock();
    const raw = (await formApi.getValues()) as MesPdProjectApi.Project & {
      planDateRange?: [string, string];
      productId?: number;
    };
    const { productId: _productId, planDateRange, ...data } = raw;
    if (planDateRange?.length === 2) {
      data.planStartDate = new Date(planDateRange[0]).getTime();
      data.planEndDate = new Date(planDateRange[1]).getTime();
    const { productId: _productId, ...data } = raw;
    if (data.planFinishTime) {
      data.planFinishTime = Number(data.planFinishTime);
    }
    try {
      const id = await createPdProject(data);
      emit('success', id);
      message.success('设计任务已发起,请发布任务后开始上传资料');
      await createPdProject(data);
      emit('success');
      message.success('设计任务已创建,请发布任务后开始上传资料');
      await modalApi.close();
    } finally {
      modalApi.unlock();
@@ -57,21 +55,14 @@
      await formApi.resetForm();
      return;
    }
    formApi.setState({ schema: useCreateTaskSchema(formApi) });
    modalApi.setState({ title: '发起设计任务' });
    formApi.setState({ schema: useCreateFormSchema(formApi) });
    modalApi.setState({ title: '创建设计任务' });
  },
});
</script>
<template>
  <Modal class="w-[720px]">
    <Alert
      class="mx-4 mb-4"
      message="发起任务"
      description="为指定产品的某一设计版本创建设计任务。任务保存为草稿,发布后方可上传设计资料。"
      show-icon
      type="info"
    />
    <Form class="mx-4" />
  </Modal>
</template>
src/views/mes/pd/project/modules/document-audit-form.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
<script lang="ts" setup>
import type { MesPdDocumentAuditApi } from '#/api/mes/pd/document-audit';
import { useVbenModal } from '#/packages/effects/common-ui/src';
import { DICT_TYPE } from '#/packages/constants/src';
import { getDictOptions } from '#/packages/effects/hooks/src';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { updatePdDocumentAudit } from '#/api/mes/pd/document-audit';
import { $t } from '#/locales';
const emit = defineEmits(['success']);
const [Form, formApi] = useVbenForm({
  commonConfig: { componentProps: { class: 'w-full' }, labelWidth: 100 },
  layout: 'horizontal',
  schema: [
    { fieldName: 'id', component: 'Input', dependencies: { triggerFields: [''], show: () => false } },
    { fieldName: 'auditNo', label: '审核单号', component: 'Input', componentProps: { disabled: true } },
    { fieldName: 'documentName', label: '资料名称', component: 'Input', componentProps: { disabled: true } },
    { fieldName: 'documentVersion', label: '资料版本', component: 'Input', componentProps: { disabled: true } },
    { fieldName: 'reviewerNickname', label: '审核人', component: 'Input', componentProps: { disabled: true } },
    {
      fieldName: 'auditStatus',
      label: '审核结果',
      component: 'Select',
      componentProps: { options: getDictOptions(DICT_TYPE.MES_PD_DOCUMENT_AUDIT_STATUS, 'number'), placeholder: '请选择审核结果' },
      rules: 'selectRequired',
    },
    { fieldName: 'remark', label: '审核意见', component: 'Textarea', componentProps: { placeholder: '请输入审核意见', rows: 3 } },
  ],
  showDefaultActions: false,
});
const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    const { valid } = await formApi.validate();
    if (!valid) return;
    modalApi.lock();
    try {
      const raw = await formApi.getValues();
      await updatePdDocumentAudit({ id: raw.id, auditStatus: raw.auditStatus, remark: raw.remark, auditTime: Date.now() });
      emit('success');
      message.success($t('ui.actionMessage.operationSuccess'));
      await modalApi.close();
    } finally {
      modalApi.unlock();
    }
  },
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) return;
    const data = modalApi.getData<MesPdDocumentAuditApi.DocumentAudit>();
    modalApi.setState({ title: '资料审核' });
    await formApi.setValues(data);
  },
});
</script>
<template>
  <Modal class="w-[520px]">
    <Form class="mx-4" />
  </Modal>
</template>
src/views/mes/pd/project/modules/document-audit-list.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesPdDocumentAuditApi } from '#/api/mes/pd/document-audit';
import { useVbenModal } from '#/packages/effects/common-ui/src';
import { DICT_TYPE } from '#/packages/constants/src';
import { getDictOptions } from '#/packages/effects/hooks/src';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getPdDocumentAuditPage } from '#/api/mes/pd/document-audit';
import AuditForm from './document-audit-form.vue';
const props = defineProps<{
  projectId: number;
}>();
const emit = defineEmits(['success']);
const [AuditFormModal, auditFormModalApi] = useVbenModal({
  connectedComponent: AuditForm,
  destroyOnClose: true,
});
function handleRefresh() {
  gridApi.query();
  emit('success');
}
function handleAudit(row: MesPdDocumentAuditApi.DocumentAudit) {
  auditFormModalApi.setData(row).open();
}
const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: [
      { fieldName: 'auditNo', label: '审核单号', component: 'Input', componentProps: { placeholder: '请输入审核单号' } },
      { fieldName: 'documentName', label: '资料名称', component: 'Input', componentProps: { placeholder: '请输入资料名称' } },
      {
        fieldName: 'auditStatus',
        label: '审核状态',
        component: 'Select',
        componentProps: { allowClear: true, options: getDictOptions(DICT_TYPE.MES_PD_DOCUMENT_AUDIT_STATUS, 'number'), placeholder: '请选择审核状态' },
      },
    ],
  },
  gridOptions: {
    columns: [
      { field: 'auditNo', title: '审核单号', minWidth: 140 },
      { field: 'documentName', title: '资料名称', minWidth: 160 },
      { field: 'documentVersion', title: '资料版本', minWidth: 90 },
      { field: 'reviewerNickname', title: '审核人', minWidth: 100 },
      { field: 'auditStatus', title: '审核状态', minWidth: 100, cellRender: { name: 'CellDict', props: { type: DICT_TYPE.MES_PD_DOCUMENT_AUDIT_STATUS } } },
      { field: 'auditTime', title: '审核时间', minWidth: 160, formatter: 'formatDateTime' },
      { field: 'createTime', title: '创建时间', minWidth: 160, formatter: 'formatDateTime' },
      { field: 'action', title: '操作', width: 100, fixed: 'right', slots: { default: 'actions' } },
    ],
    height: 400,
    keepSource: true,
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          return await getPdDocumentAuditPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            ...formValues,
          });
        },
      },
    },
    rowConfig: { keyField: 'id', isHover: true },
    toolbarConfig: { refresh: true, search: true },
  } as VxeTableGridOptions<MesPdDocumentAuditApi.DocumentAudit>,
});
</script>
<template>
  <AuditFormModal @success="handleRefresh" />
  <Grid table-title="资料审核">
    <template #actions="{ row }">
      <TableAction
        :actions="[
          {
            label: '审核',
            type: 'link',
            icon: ACTION_ICON.EDIT,
            auth: ['mes:pd-document-audit:update'],
            onClick: handleAudit.bind(null, row),
          },
        ]"
      />
    </template>
  </Grid>
</template>
src/views/mes/pd/project/modules/document-form.vue
@@ -1,7 +1,7 @@
<script lang="ts" setup>
import type { MesPdDocumentApi } from '#/api/mes/pd/document';
import { ref } from 'vue';
import { markRaw, ref } from 'vue';
import { useVbenModal } from '#/packages/effects/common-ui/src';
import { MesPdDocumentTypeEnum } from '#/packages/constants/src';
@@ -14,6 +14,7 @@
  getPdDocument,
  updatePdDocument,
} from '#/api/mes/pd/document';
import { FileUpload } from '#/components/upload';
import { $t } from '#/locales';
import { getPdDocumentTypeOptions } from '../data';
@@ -25,9 +26,11 @@
const [Form, formApi] = useVbenForm({
  commonConfig: {
    componentProps: { class: 'w-full' },
    formItemClass: 'col-span-1',
    labelWidth: 100,
  },
  layout: 'horizontal',
  wrapperClass: 'grid-cols-1',
  schema: [
    {
      fieldName: 'id',
@@ -50,14 +53,14 @@
      rules: 'selectRequired',
    },
    {
      fieldName: 'name',
      fieldName: 'documentName',
      label: '资料名称',
      component: 'Input',
      componentProps: { placeholder: '请输入资料名称' },
      rules: 'required',
    },
    {
      fieldName: 'version',
      fieldName: 'documentVersion',
      label: '版本号',
      component: 'Input',
      componentProps: { placeholder: '如 V1.0' },
@@ -65,21 +68,23 @@
    },
    {
      fieldName: 'fileUrl',
      label: '文件地址',
      component: 'Input',
      componentProps: { placeholder: '请输入文件 URL æˆ–上传后填入' },
      label: '文件',
      component: markRaw(FileUpload),
      componentProps: {
        directory: 'mes/pd',
        maxNumber: 1,
        onChange: async (url: string) => {
          if (url) {
            await formApi?.setFieldValue('fileUrl', url);
          }
        },
      },
    },
    {
      fieldName: 'fileName',
      label: '文件名',
      component: 'Input',
      componentProps: { placeholder: '如 bom-list.xlsx' },
    },
    {
      fieldName: 'remark',
      label: '备注',
      component: 'Textarea',
      componentProps: { placeholder: '请输入备注', rows: 3 },
      componentProps: { placeholder: '上传后自动填入,也可手动输入' },
    },
  ],
  showDefaultActions: false,
@@ -113,10 +118,10 @@
    const data = modalApi.getData<{
      id?: number;
      projectId: number;
      documentType?: number;
      documentType?: string;
    }>();
    isUpdate.value = !!data.id;
    modalApi.setState({ title: isUpdate.value ? '编辑附件' : '上传附件' });
    modalApi.setState({ title: isUpdate.value ? '编辑资料' : '上传资料' });
    if (data.id) {
      modalApi.lock();
      try {
@@ -136,7 +141,7 @@
</script>
<template>
  <Modal class="w-[560px]">
  <Modal class="w-[720px]">
    <Form class="mx-4" />
  </Modal>
</template>
src/views/mes/pd/project/modules/document-list.vue
@@ -7,7 +7,7 @@
import { computed, watch } from 'vue';
import { useVbenModal } from '#/packages/effects/common-ui/src';
import { MesPdDocumentTypeEnum } from '#/packages/constants/src';
import { MesPdDocumentTypeEnum, MesPdProjectStatusEnum } from '#/packages/constants/src';
import { message } from 'ant-design-vue';
@@ -29,6 +29,7 @@
    formType: FormType;
    gridHeight?: number | string;
    projectId: number;
    projectStatus?: number;
    tableTitle?: string;
    version?: string;
  }>(),
@@ -38,8 +39,11 @@
  },
);
const isEditable = computed(() =>
  ['create', 'update'].includes(props.formType),
/** æ˜¯å¦å…è®¸æ–°å¢ž/编辑/删除资料 */
const canEdit = computed(() =>
  props.projectStatus === undefined ||
  props.projectStatus === MesPdProjectStatusEnum.PENDING_PUBLISH ||
  props.projectStatus === MesPdProjectStatusEnum.PENDING_UPLOAD,
);
const [DocumentFormModal, documentFormModalApi] = useVbenModal({
@@ -66,21 +70,15 @@
async function handleDelete(row: MesPdDocumentApi.Document) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting', [row.name]),
    content: $t('ui.actionMessage.deleting', [row.documentName]),
    duration: 0,
  });
  try {
    await deletePdDocument(row.id!);
    message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
    message.success($t('ui.actionMessage.deleteSuccess', [row.documentName]));
    handleRefresh();
  } finally {
    hideLoading();
  }
}
function handlePreview(row: MesPdDocumentApi.Document) {
  if (row.fileUrl) {
    window.open(row.fileUrl, '_blank');
  }
}
@@ -97,7 +95,7 @@
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            projectId: props.projectId,
            version: props.version,
            documentVersion: props.version,
            ...formValues,
          });
        },
@@ -119,11 +117,11 @@
<template>
  <DocumentFormModal @success="handleRefresh" />
  <Grid :table-title="tableTitle">
    <template v-if="isEditable" #toolbar-tools>
    <template v-if="canEdit" #toolbar-tools>
      <TableAction
        :actions="[
          {
            label: '上传附件',
            label: '上传资料',
            type: 'primary',
            icon: ACTION_ICON.ADD,
            onClick: handleCreate,
@@ -131,20 +129,20 @@
        ]"
      />
    </template>
    <template #file="{ row }">
      <a v-if="row.fileUrl" :href="row.fileUrl" target="_blank" class="text-primary">
        {{ row.fileName || '查看文件' }}
      </a>
      <span v-else class="text-gray-400">-</span>
    </template>
    <template #actions="{ row }">
      <TableAction
        :actions="[
          {
            label: '预览',
            type: 'link',
            ifShow: !!row.fileUrl,
            onClick: handlePreview.bind(null, row),
          },
          {
            label: $t('common.edit'),
            type: 'link',
            icon: ACTION_ICON.EDIT,
            ifShow: isEditable,
            ifShow: canEdit,
            onClick: handleEdit.bind(null, row),
          },
          {
@@ -152,9 +150,9 @@
            type: 'link',
            danger: true,
            icon: ACTION_ICON.DELETE,
            ifShow: isEditable,
            ifShow: canEdit,
            popConfirm: {
              title: $t('ui.actionMessage.deleteConfirm', [row.name]),
              title: $t('ui.actionMessage.deleteConfirm', [row.documentName]),
              confirm: handleDelete.bind(null, row),
            },
          },
src/views/mes/pd/project/modules/form.vue
@@ -51,13 +51,11 @@
    }
    modalApi.lock();
    const raw = (await formApi.getValues()) as MesPdProjectApi.Project & {
      planDateRange?: [string, string];
      productId?: number;
    };
    const { productId: _productId, planDateRange, ...data } = raw;
    if (planDateRange?.length === 2) {
      data.planStartDate = new Date(planDateRange[0]).getTime();
      data.planEndDate = new Date(planDateRange[1]).getTime();
    const { productId: _productId, ...data } = raw;
    if (data.planFinishTime) {
      data.planFinishTime = Number(data.planFinishTime);
    }
    try {
      await updatePdProject(data);
@@ -85,12 +83,10 @@
    modalApi.lock();
    try {
      formData.value = await getPdProject(data.id);
      const { planFinishTime, ...rest } = formData.value;
      await formApi.setValues({
        ...formData.value,
        planDateRange:
          formData.value.planStartDate && formData.value.planEndDate
            ? [formData.value.planStartDate, formData.value.planEndDate]
            : undefined,
        ...rest,
        planFinishTime: planFinishTime ? String(planFinishTime) : undefined,
      });
    } finally {
      modalApi.unlock();
src/views/mes/pd/project/modules/publish-form.vue
ÎļþÒÑɾ³ý
src/views/mes/pd/project/modules/task-draft-banner.vue
@@ -18,7 +18,7 @@
    class="mb-4 flex items-center justify-between rounded-lg border border-amber-200 bg-amber-50 px-4 py-3"
  >
    <div class="text-sm text-amber-900">
      <span class="font-medium">任务已发起(草稿)</span>
      <span class="font-medium">任务待发布</span>
      <span class="ml-2 text-amber-700">
        {{ productName }} {{ version }} Â· è®¾è®¡å¸ˆ {{ designerName || '未指定' }}
      </span>
src/views/mes/pd/project/modules/user-select.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
<script lang="ts" setup>
import type { SystemUserApi } from '#/api/system/user';
import { onMounted, ref, watch } from 'vue';
import { Select } from 'ant-design-vue';
import { getSimpleUserList } from '#/api/system/user';
defineOptions({ name: 'PdUserSelect', inheritAttrs: false });
const props = withDefaults(
  defineProps<{
    disabled?: boolean;
    modelValue?: number;
    placeholder?: string;
  }>(),
  {
    disabled: false,
    modelValue: undefined,
    placeholder: '请选择',
  },
);
const emit = defineEmits<{
  'update:modelValue': [value: number | undefined];
}>();
const options = ref<{ label: string; value: number }[]>([]);
onMounted(async () => {
  const users = await getSimpleUserList();
  options.value = users
    .filter((u): u is SystemUserApi.User & { id: number } => u.id !== undefined)
    .map((u) => ({ label: u.nickname || u.username, value: u.id }));
});
watch(
  () => props.modelValue,
  (val) => {
    emit('update:modelValue', val);
  },
);
</script>
<template>
  <Select
    v-bind="$attrs"
    :disabled="disabled"
    :options="options"
    :placeholder="placeholder"
    :value="modelValue"
    allow-clear
    show-search
    :filter-option="
      (input: string, option: any) =>
        option?.label?.toLowerCase().includes(input.toLowerCase())
    "
    @change="(val: number) => emit('update:modelValue', val)"
  />
</template>
src/views/mes/pd/project/modules/version-change/data.ts
@@ -9,8 +9,6 @@
import { Button } from 'ant-design-vue';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { isPdMockEnabled } from '#/api/mes/pd/mock/enabled';
import { mockNextCode } from '#/api/mes/pd/mock/utils';
export type VersionChangeFormType = 'create' | 'detail' | 'update';
@@ -104,12 +102,7 @@
                {
                  type: 'default',
                  onClick: async () => {
                    const code = isPdMockEnabled()
                      ? mockNextCode(
                          'VC-2026',
                          Math.floor(Date.now() % 10_000),
                        )
                      : await generateAutoCode(
                    const code = await generateAutoCode(
                          MesAutoCodeRuleCode.PD_VERSION_CODE,
                        );
                    await formApi?.setFieldValue('code', code);
src/views/mes/pd/project/modules/workflow-action-form.vue
ÎļþÒÑɾ³ý
src/views/mes/pd/project/modules/workflow-timeline.vue
@@ -1,53 +1,35 @@
<script lang="ts" setup>
import type { MesPdWorkflowApi } from '#/api/mes/pd/workflow';
import { onMounted, ref, watch } from 'vue';
import { computed } from 'vue';
import { Steps } from 'ant-design-vue';
import { getPdWorkflowList } from '#/api/mes/pd/workflow';
const STEPS = ['待发布', '待上传', '待审核', '待归档', '已归档'] as const;
const props = defineProps<{
  projectId: number;
  /** é¡¹ç›®å½“前状态 (0-4) */
  status: number;
}>();
const loading = ref(false);
const nodes = ref<MesPdWorkflowApi.WorkflowNode[]>([]);
const current = ref(0);
const current = computed(() => {
  // çŠ¶æ€å€¼å³å½“å‰å·²å®Œæˆåˆ°ç¬¬å‡ æ­¥ï¼š0=第一步进行中,4=全部完成
  return props.status;
});
async function loadData() {
  loading.value = true;
  try {
    nodes.value = await getPdWorkflowList(props.projectId);
    const activeIndex = nodes.value.findIndex((item) => item.status !== 1);
    current.value = activeIndex === -1 ? nodes.value.length : activeIndex;
  } finally {
    loading.value = false;
  }
}
watch(
  () => props.projectId,
  () => loadData(),
  { immediate: true },
const nodes = computed(() =>
  STEPS.map((name, index) => ({
    title: name,
    status: index < props.status ? 'finish' : index === props.status ? 'process' : 'wait',
  })),
);
onMounted(loadData);
</script>
<template>
  <Steps :current="current" direction="vertical" size="small">
    <Steps.Step
      v-for="node in nodes"
      :key="node.id"
      :description="node.remark"
      :sub-title="node.operatorName"
      :title="node.nodeName"
    />
    <Steps.Step
      v-if="!loading && nodes.length === 0"
      description="暂无流程记录,保存项目后将自动生成"
      title="待启动"
      v-for="(node, index) in nodes"
      :key="index"
      :status="node.status as any"
      :title="node.title"
    />
  </Steps>
</template>
src/views/mes/pro/andon/record/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProAndonConfigApi } from '#/api/mes/pro/andon/config';
import type { MesProAndonRecordApi } from '#/api/mes/pro/andon/record';
@@ -82,7 +82,7 @@
  return [
    { field: 'workstationCode', title: '工作站编码', width: 140 },
    { field: 'workstationName', title: '工作站名称', minWidth: 140 },
    { field: 'workOrderCode', title: '工单编码', width: 140 },
    { field: 'workOrderCode', title: '订单编码', width: 140 },
    { field: 'processName', title: '工序名称', width: 140 },
    { field: 'userNickname', title: '发起人', width: 110 },
    {
@@ -129,7 +129,7 @@
/**
 * æ–°å¢ž/处置/详情安灯呼叫记录的表单
 *
 * - create:录入呼叫主体信息(工作站/发起人/工单/工序/呼叫原因/备注)
 * - create:录入呼叫主体信息(工作站/发起人/订单/工序/呼叫原因/备注)
 * - update:呼叫主体只读展示创建时的快照字段(workstationName/workOrderCode/processName/reason),编辑处置时间/处置人/备注
 * - detail:所有字段只读
 */
@@ -187,16 +187,16 @@
    isCreate
      ? {
          fieldName: 'workOrderId',
          label: '生产工单',
          label: '生产订单',
          component: markRaw(ProWorkOrderSelect),
          componentProps: {
            placeholder: '请选择工单(可选)',
            placeholder: '请选择订单(可选)',
            status: MesProWorkOrderStatusEnum.CONFIRMED,
          },
        }
      : {
          fieldName: 'workOrderCode',
          label: '生产工单',
          label: '生产订单',
          component: 'Input',
          componentProps: {
            disabled: true,
src/views/mes/pro/card/components/select.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { MesProCardApi } from '#/api/mes/pro/card';
import { computed, ref, useAttrs, watch } from 'vue';
@@ -111,7 +111,7 @@
      <template #title>
        <div v-if="selectedItem" class="leading-6">
          <div>编号:{{ selectedItem.code || '-' }}</div>
          <div>工单:{{ selectedItem.workOrderCode || '-' }}</div>
          <div>订单:{{ selectedItem.workOrderCode || '-' }}</div>
          <div>批次:{{ selectedItem.batchCode || '-' }}</div>
          <div>产品:{{ selectedItem.itemName || '-' }}</div>
        </div>
src/views/mes/pro/card/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProCardApi } from '#/api/mes/pro/card';
import type { MesProCardProcessApi } from '#/api/mes/pro/card/process';
@@ -80,11 +80,11 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        disabled: headerReadonly,
        placeholder: '请选择生产工单',
        placeholder: '请选择生产订单',
        status: MesProWorkOrderStatusEnum.CONFIRMED,
      },
      rules: 'selectRequired',
@@ -149,10 +149,10 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        placeholder: '请选择生产工单',
        placeholder: '请选择生产订单',
      },
    },
    {
@@ -186,12 +186,12 @@
    },
    {
      field: 'workOrderCode',
      title: '生产工单编号',
      title: '生产订单编号',
      width: 160,
    },
    {
      field: 'workOrderName',
      title: '工单名称',
      title: '订单名称',
      minWidth: 150,
    },
    {
@@ -437,10 +437,10 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        placeholder: '请选择生产工单',
        placeholder: '请选择生产订单',
      },
    },
    {
@@ -479,7 +479,7 @@
    },
    {
      field: 'workOrderCode',
      title: '生产工单编号',
      title: '生产订单编号',
      width: 160,
    },
    {
src/views/mes/pro/feedback/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProFeedbackApi } from '#/api/mes/pro/feedback';
import type { MesProTaskApi } from '#/api/mes/pro/task';
@@ -32,11 +32,11 @@
  return [
    {
      fieldName: 'code',
      label: '报工单号',
      label: '报订单号',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入报工单号',
        placeholder: '请输入报订单号',
      },
    },
    {
@@ -51,11 +51,11 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        allowClear: true,
        placeholder: '请选择工单',
        placeholder: '请选择订单',
      },
    },
    {
@@ -118,7 +118,7 @@
  return [
    {
      field: 'code',
      title: '报工单号',
      title: '报订单号',
      width: 160,
      slots: { default: 'code' },
    },
@@ -133,7 +133,7 @@
    },
    { field: 'workstationName', title: '工作站', width: 120 },
    { field: 'processName', title: '工序', width: 100 },
    { field: 'workOrderCode', title: '生产工单编码', width: 160 },
    { field: 'workOrderCode', title: '生产订单编码', width: 160 },
    { field: 'itemCode', title: '产品物料编码', width: 120 },
    { field: 'itemName', title: '产品物料名称', minWidth: 140 },
    { field: 'itemSpecification', title: '规格型号', width: 120 },
@@ -209,11 +209,11 @@
    },
    {
      fieldName: 'code',
      label: '报工单号',
      label: '报订单号',
      component: 'Input',
      componentProps: {
        disabled: isHeaderReadonly,
        placeholder: '请输入报工单号',
        placeholder: '请输入报订单号',
      },
      rules: 'required',
      suffix: () =>
@@ -245,13 +245,13 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        disabled: isHeaderReadonly,
        placeholder: '请选择工单',
        placeholder: '请选择订单',
        status: MesProWorkOrderStatusEnum.CONFIRMED,
        // å·¥å•变更:清空任务及任务带出的产品信息、数量区域控制位
        // è®¢å•变更:清空任务及任务带出的产品信息、数量区域控制位
        onChange: async () => {
          await formApi?.setValues({
            checkFlag: true,
@@ -277,7 +277,7 @@
        triggerFields: ['workOrderId', 'workstationId'],
        componentProps: (values) => ({
          disabled: isHeaderReadonly || !values.workOrderId,
          placeholder: values.workOrderId ? '请选择任务' : '请先选择工单',
          placeholder: values.workOrderId ? '请选择任务' : '请先选择订单',
          statuses: [MesProTaskStatusEnum.PREPARE],
          workOrderId: values.workOrderId,
          workstationId: values.workstationId,
src/views/mes/pro/feedback/modules/form.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesProFeedbackApi } from '#/api/mes/pro/feedback';
@@ -152,7 +152,7 @@
    await submitFeedback(formData.value!.id!);
    await modalApi.close();
    emit('success');
    message.success('报工单已提交');
    message.success('报订单已提交');
  } finally {
    modalApi.unlock();
  }
@@ -169,7 +169,7 @@
    await modalApi.close();
    emit('success');
    message.success(
      finished ? '报工单已审批完成' : '报工成功,请等待质量检验完成!',
      finished ? '报订单已审批完成' : '报工成功,请等待质量检验完成!',
    );
  } finally {
    modalApi.unlock();
@@ -186,7 +186,7 @@
    await rejectFeedback(formData.value.id);
    await modalApi.close();
    emit('success');
    message.success('报工单已驳回');
    message.success('报订单已驳回');
  } finally {
    modalApi.unlock();
  }
@@ -236,7 +236,7 @@
        formType.value !== 'detail' && formType.value !== 'approve',
    });
    if (!data?.id) {
      // æ–°å¢žï¼šé»˜è®¤æŠ¥å·¥äººå’ŒæŠ¥å·¥æ—¶é—´ï¼Œå¹¶è‡ªåŠ¨ç”ŸæˆæŠ¥å·¥å•å·
      // æ–°å¢žï¼šé»˜è®¤æŠ¥å·¥äººå’ŒæŠ¥å·¥æ—¶é—´ï¼Œå¹¶è‡ªåŠ¨ç”ŸæˆæŠ¥è®¢å•å·
      const code = await generateAutoCode(
        MesAutoCodeRuleCode.PRO_FEEDBACK_CODE,
      );
@@ -287,21 +287,21 @@
      <div class="flex flex-auto items-center justify-end gap-2">
        <Popconfirm
          v-if="canSubmit"
          title="确认提交该报工单?提交后将不能修改。"
          title="确认提交该报订单?提交后将不能修改。"
          @confirm="handleSubmit"
        >
          <Button type="primary">{{ $t('common.submit') }}</Button>
        </Popconfirm>
        <Popconfirm
          v-if="canApprove"
          title="确认审批通过该报工单?"
          title="确认审批通过该报订单?"
          @confirm="handleApprove"
        >
          <Button type="primary">通过</Button>
        </Popconfirm>
        <Popconfirm
          v-if="canApprove"
          title="确认驳回该报工单?"
          title="确认驳回该报订单?"
          @confirm="handleReject"
        >
          <Button danger>不通过</Button>
src/views/mes/pro/task/components/gantt-chart.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { BarcodeBizTypeEnum } from '../../../../../packages/constants/src';
@@ -10,7 +10,7 @@
/**
 * ç”˜ç‰¹å›¾ç»„件(基于 dhtmlx-gantt)
 *
 * 1. æŒ‰å·¥å•分组展示生产任务,工单为 project è¡Œï¼Œä»»åŠ¡ä¸ºå­è¡Œ
 * 1. æŒ‰è®¢å•分组展示生产任务,订单为 project è¡Œï¼Œä»»åŠ¡ä¸ºå­è¡Œ
 * 2. æ”¯æŒåªè¯»é¢„览和拖拽编辑两种模式
 * 3. æ‹–拽后触发 taskUpdate äº‹ä»¶ï¼Œé€šçŸ¥çˆ¶ç»„件批量保存
 * 4. æ—¶é—´åˆ»åº¦ï¼šå‘¨ â†’ æ—¥ â†’ 8 å°æ—¶ï¼ˆ1 å·¥ä½œæ—¥ = 8 å°æ—¶ï¼‰
@@ -113,7 +113,7 @@
  gantt.templates.task_text = (_start: any, _end: any, task: any) => {
    const percent = Math.round((task.progress || 0) * 100);
    if (task.type === 'project') {
      return `<b>生产工单:</b> ${task.text} <span>完成比例:${percent}%</span>`;
      return `<b>生产订单:</b> ${task.text} <span>完成比例:${percent}%</span>`;
    }
    return `<b>生产任务:</b> ${task.process || ''} ${task.text} <span>完成比例:${percent}%</span>`;
  };
@@ -121,7 +121,7 @@
  gantt.templates.tooltip_text = (_start: any, _end: any, task: any) => {
    const percent = Math.round((task.progress || 0) * 100);
    if (task.type === 'project') {
      return `<b>生产工单:</b> ${task.text} <span>完成比例:${percent}%</span>`;
      return `<b>生产订单:</b> ${task.text} <span>完成比例:${percent}%</span>`;
    }
    return `<b>生产任务:</b> ${task.process || ''} ${task.text} <span>完成比例:${percent}%</span>`;
  };
@@ -135,7 +135,7 @@
  if (!props.readonly) {
    gantt.attachEvent('onAfterTaskUpdate', (id: number | string) => {
      const task = gantt.getTask(id);
      // åªå›žå†™ task èŠ‚ç‚¹ï¼Œé¿å…æŠŠå·¥å•(project)节点当成任务保存
      // åªå›žå†™ task èŠ‚ç‚¹ï¼Œé¿å…æŠŠè®¢å•(project)节点当成任务保存
      if (task.type !== gantt.config.types.task || !task.originalId) {
        return;
      }
@@ -227,7 +227,7 @@
  color: #f44;
}
/* å·¥å•(project)行样式 */
/* è®¢å•(project)行样式 */
.gantt-project-bar .gantt_task_progress {
  background: #7b68ee;
}
src/views/mes/pro/task/components/select-dialog.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProTaskApi } from '#/api/mes/pro/task';
@@ -30,7 +30,7 @@
const multiple = ref(false); // æ˜¯å¦å¤šé€‰ï¼›é»˜è®¤æŒ‰å•选选择器使用
const selectedRows = ref<MesProTaskApi.Task[]>([]); // å·²é€‰ä»»åŠ¡åˆ—è¡¨
const preSelectedIds = ref<number[]>([]); // é¢„选任务编号列表
const externalWorkOrderId = ref<number>(); // å¤–部传入的默认工单过滤
const externalWorkOrderId = ref<number>(); // å¤–部传入的默认订单过滤
const externalWorkstationId = ref<number>(); // å¤–部传入的默认工位过滤
const statusTip = computed(() => {
@@ -163,7 +163,7 @@
  },
});
/** é‡ç½®æŸ¥è¯¢å’Œé€‰æ‹©çŠ¶æ€ï¼Œä¿ç•™å¤–éƒ¨ä¼ å…¥çš„å·¥å•/工位默认过滤 */
/** é‡ç½®æŸ¥è¯¢å’Œé€‰æ‹©çŠ¶æ€ï¼Œä¿ç•™å¤–éƒ¨ä¼ å…¥çš„è®¢å•/工位默认过滤 */
async function resetQueryState() {
  selectedRows.value = [];
  await gridApi.grid.clearCheckboxRow();
src/views/mes/pro/task/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProTaskApi } from '#/api/mes/pro/task';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
@@ -16,25 +16,25 @@
import { RouteColorPicker } from '#/views/mes/process-design/route/components';
import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components';
/** å¾…排产工单列表的搜索表单 */
/** å¾…排产订单列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'code',
      label: '工单编码',
      label: '订单编码',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入工单编码',
        placeholder: '请输入订单编码',
      },
    },
    {
      fieldName: 'name',
      label: '工单名称',
      label: '订单名称',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入工单名称',
        placeholder: '请输入订单名称',
      },
    },
    {
@@ -74,12 +74,12 @@
  ];
}
/** å¾…排产工单列表的字段 */
/** å¾…排产订单列表的字段 */
export function useGridColumns(): VxeTableGridOptions<MesProWorkOrderApi.WorkOrder>['columns'] {
  return [
    {
      field: 'code',
      title: '工单编码',
      title: '订单编码',
      fixed: 'left',
      width: 200,
      treeNode: true,
@@ -87,12 +87,12 @@
    },
    {
      field: 'name',
      title: '工单名称',
      title: '订单名称',
      minWidth: 150,
    },
    {
      field: 'orderSourceType',
      title: '工单来源',
      title: '订单来源',
      width: 100,
      cellRender: {
        name: 'CellDict',
@@ -126,7 +126,7 @@
    },
    {
      field: 'quantity',
      title: '工单数量',
      title: '订单数量',
      width: 100,
    },
    {
@@ -173,12 +173,12 @@
  ];
}
/** æŽ’产对话框只读工单信息的表单 */
/** æŽ’产对话框只读订单信息的表单 */
export function useScheduleFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'code',
      label: '工单编码',
      label: '订单编码',
      component: 'Input',
      componentProps: {
        disabled: true,
@@ -186,7 +186,7 @@
    },
    {
      fieldName: 'name',
      label: '工单名称',
      label: '订单名称',
      component: 'Input',
      componentProps: {
        disabled: true,
@@ -194,7 +194,7 @@
    },
    {
      fieldName: 'orderSourceType',
      label: '工单来源',
      label: '订单来源',
      component: 'Select',
      componentProps: {
        disabled: true,
@@ -214,7 +214,7 @@
    },
    {
      fieldName: 'type',
      label: '工单类型',
      label: '订单类型',
      component: 'Select',
      componentProps: {
        disabled: true,
@@ -247,7 +247,7 @@
    },
    {
      fieldName: 'quantity',
      label: '工单数量',
      label: '订单数量',
      component: 'InputNumber',
      componentProps: {
        class: '!w-full',
@@ -284,7 +284,7 @@
    },
    {
      fieldName: 'status',
      label: '工单状态',
      label: '订单状态',
      component: 'Select',
      componentProps: {
        disabled: true,
@@ -476,11 +476,11 @@
  return [
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        allowClear: true,
        placeholder: '请选择生产工单',
        placeholder: '请选择生产订单',
      },
    },
    {
src/views/mes/pro/task/index.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
@@ -43,7 +43,7 @@
  });
}
/** æŸ¥çœ‹å·¥å•详情 */
/** æŸ¥çœ‹è®¢å•详情 */
function handleDetail(row: MesProWorkOrderApi.WorkOrder) {
  scheduleModalApi.setData({ formType: 'detail', id: row.id }).open();
}
@@ -107,7 +107,7 @@
      <GanttChart :height="350" :readonly="true" :tasks="ganttTasks" />
    </Card>
    <Grid table-title="待排产工单">
    <Grid table-title="待排产订单">
      <template #toolbar-tools>
        <TableAction
          :actions="[
src/views/mes/pro/task/modules/schedule-form.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { MesProRouteProcessApi } from '#/api/mes/pro/route/process';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
@@ -17,14 +17,14 @@
const emit = defineEmits(['success']);
const formType = ref<'detail' | 'schedule'>('schedule');
const workOrder = ref<MesProWorkOrderApi.WorkOrder>(); // å½“前工单详情
const workOrder = ref<MesProWorkOrderApi.WorkOrder>(); // å½“前订单详情
const routeProcessList = ref<MesProRouteProcessApi.RouteProcess[]>([]); // å·¥è‰ºè·¯çº¿å·¥åºåˆ—表
const activeProcessStep = ref(0); // å½“前工序步骤索引
const currentRouteId = ref(0); // å½“前工艺路线编号
const isReadonly = computed(() => formType.value === 'detail'); // è¯¦æƒ…态只读
const getTitle = computed(() =>
  formType.value === 'detail' ? '工单详情' : '生产排产',
  formType.value === 'detail' ? '订单详情' : '生产排产',
);
const [Form, formApi] = useVbenForm({
@@ -54,7 +54,7 @@
  );
}
/** å®Œæˆå·¥å• */
/** å®Œæˆè®¢å• */
async function handleFinish() {
  if (!workOrder.value?.id) {
    return;
@@ -62,7 +62,7 @@
  modalApi.lock();
  try {
    await finishWorkOrder(workOrder.value.id);
    message.success('工单已完成');
    message.success('订单已完成');
    await modalApi.close();
    emit('success');
  } finally {
@@ -140,7 +140,7 @@
      <div class="flex flex-auto items-center gap-2">
        <Popconfirm
          v-if="formType === 'schedule'"
          title="确认要完成该工单吗?完成后工单下所有任务将标记为已完成。"
          title="确认要完成该订单吗?完成后订单下所有任务将标记为已完成。"
          @confirm="handleFinish"
        >
          <Button type="primary">完成</Button>
src/views/mes/pro/task/modules/task-form.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { MesProTaskApi } from '#/api/mes/pro/task';
import { computed, ref } from 'vue';
@@ -21,7 +21,7 @@
  processId?: number;
  routeId?: number;
  workOrderId?: number;
}>({}); // æ–°å¢žä»»åŠ¡æ—¶çš„å·¥å• / å·¥åºä¸Šä¸‹æ–‡
}>({}); // æ–°å¢žä»»åŠ¡æ—¶çš„è®¢å• / å·¥åºä¸Šä¸‹æ–‡
const getTitle = computed(() => {
  return formData.value?.id
src/views/mes/pro/workorder/components/select-dialog.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
@@ -33,14 +33,14 @@
const open = ref(false); // å¼¹çª—是否打开
const multiple = ref(false); // æ˜¯å¦å¤šé€‰ï¼›é»˜è®¤æŒ‰å•选选择器使用
const selectedRows = ref<MesProWorkOrderApi.WorkOrder[]>([]); // å·²é€‰å·¥å•列表
const preSelectedIds = ref<number[]>([]); // é¢„选工单编号列表
const selectedRows = ref<MesProWorkOrderApi.WorkOrder[]>([]); // å·²é€‰è®¢å•列表
const preSelectedIds = ref<number[]>([]); // é¢„选订单编号列表
const typeTip = computed(() => {
  if (props.type === undefined) {
    return '';
  }
  return `仅展示【${getDictLabel(DICT_TYPE.MES_PRO_WORK_ORDER_TYPE, props.type)}】类型的工单`;
  return `仅展示【${getDictLabel(DICT_TYPE.MES_PRO_WORK_ORDER_TYPE, props.type)}】类型的订单`;
});
/** èŽ·å–å¤šé€‰è®°å½•ï¼ŒåŒ…å« VXE reserve è·¨é¡µè®°å½• */
@@ -94,7 +94,7 @@
  handleConfirm();
}
/** å›žæ˜¾é¢„选工单 */
/** å›žæ˜¾é¢„选订单 */
async function applyPreSelection() {
  if (preSelectedIds.value.length === 0) {
    return;
@@ -182,7 +182,7 @@
  }
}
/** æ‰“开工单选择弹窗 */
/** æ‰“开订单选择弹窗 */
async function openModal(
  selectedIds?: number[],
  options?: { multiple?: boolean },
@@ -200,12 +200,12 @@
  await applyPreSelection();
}
/** å…³é—­å·¥å•选择弹窗 */
/** å…³é—­è®¢å•选择弹窗 */
function closeModal() {
  open.value = false;
}
/** ç¡®è®¤é€‰æ‹©å·¥å• */
/** ç¡®è®¤é€‰æ‹©è®¢å• */
function handleConfirm() {
  const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
  if (rows.length === 0) {
@@ -222,7 +222,7 @@
<template>
  <Modal
    v-model:open="open"
    title="生产工单选择"
    title="生产订单选择"
    width="80%"
    :destroy-on-close="true"
    @ok="handleConfirm"
@@ -235,7 +235,7 @@
      show-icon
      class="!mb-3"
    />
    <Grid table-title="生产工单列表" />
    <Grid table-title="生产订单列表" />
    <template #footer>
      <Button @click="closeModal">取消</Button>
      <Button type="primary" @click="handleConfirm">确定</Button>
src/views/mes/pro/workorder/components/select.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
import { computed, ref, useAttrs, watch } from 'vue';
@@ -26,7 +26,7 @@
    allowClear: true,
    disabled: false,
    modelValue: undefined,
    placeholder: '请选择工单',
    placeholder: '请选择订单',
    status: undefined,
    type: undefined,
  },
@@ -38,9 +38,9 @@
}>();
const attrs = useAttrs(); // é€ä¼ å±žæ€§
const dialogRef = ref<InstanceType<typeof ProWorkOrderSelectDialog>>(); // å·¥å•选择弹窗
const dialogRef = ref<InstanceType<typeof ProWorkOrderSelectDialog>>(); // è®¢å•选择弹窗
const hovering = ref(false); // æ˜¯å¦æ‚¬åœ
const selectedItem = ref<MesProWorkOrderApi.WorkOrder>(); // å½“前选中工单
const selectedItem = ref<MesProWorkOrderApi.WorkOrder>(); // å½“前选中订单
const displayLabel = computed(() => selectedItem.value?.code ?? ''); // é€‰æ‹©å™¨å±•示编码
const showClear = computed(
@@ -53,7 +53,7 @@
    props.modelValue !== null,
);
/** æ ¹æ®ç¼–号单条查询工单信息(用于编辑回显) */
/** æ ¹æ®ç¼–号单条查询订单信息(用于编辑回显) */
async function resolveItemById(id: number | undefined) {
  if (!id) {
    selectedItem.value = undefined;
@@ -67,14 +67,14 @@
watch(() => props.modelValue, resolveItemById, { immediate: true });
/** æ¸…空已选工单 */
/** æ¸…空已选订单 */
function clearSelected() {
  selectedItem.value = undefined;
  emit('update:modelValue', undefined);
  emit('change', undefined);
}
/** æ‰“开工单选择弹窗 */
/** æ‰“开订单选择弹窗 */
function handleClick(event: MouseEvent) {
  if (props.disabled) {
    return;
src/views/mes/pro/workorder/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdProductBomApi } from '#/api/mes/md/item/productBom';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
@@ -58,7 +58,7 @@
    },
    {
      fieldName: 'status',
      label: '工单状态',
      label: '订单状态',
      component: 'Select',
      componentProps: {
        disabled: true,
@@ -71,11 +71,11 @@
    },
    {
      fieldName: 'code',
      label: '工单编码',
      label: '订单编码',
      component: 'Input',
      componentProps: {
        disabled: headerReadonly,
        placeholder: '请输入工单编码',
        placeholder: '请输入订单编码',
      },
      rules: 'required',
      suffix:
@@ -98,17 +98,17 @@
    },
    {
      fieldName: 'name',
      label: '工单名称',
      label: '订单名称',
      component: 'Input',
      componentProps: {
        disabled: headerReadonly,
        placeholder: '请输入工单名称',
        placeholder: '请输入订单名称',
      },
      rules: 'required',
    },
    {
      fieldName: 'orderSourceType',
      label: '工单来源',
      label: '订单来源',
      component: 'Select',
      componentProps: {
        disabled: headerReadonly,
@@ -116,8 +116,8 @@
          DICT_TYPE.MES_PRO_WORK_ORDER_SOURCE_TYPE,
          'number',
        ),
        placeholder: '请选择工单来源',
        // å·¥å•来源变更:非客户订单时清空来源单据编号和客户
        placeholder: '请选择订单来源',
        // è®¢å•来源变更:非客户订单时清空来源单据编号和客户
        onChange: async (value: number) => {
          if (value !== MesProWorkOrderSourceTypeEnum.ORDER) {
            await formApi?.setValues({
@@ -145,13 +145,13 @@
    },
    {
      fieldName: 'type',
      label: '工单类型',
      label: '订单类型',
      component: 'Select',
      componentProps: {
        disabled: headerReadonly,
        options: getDictOptions(DICT_TYPE.MES_PRO_WORK_ORDER_TYPE, 'number'),
        placeholder: '请选择工单类型',
        // å·¥å•类型变更:非代工/采购时清空供应商
        placeholder: '请选择订单类型',
        // è®¢å•类型变更:非代工/采购时清空供应商
        onChange: async (value: number) => {
          if (
            value !== MesProWorkOrderTypeEnum.OUTSOURCE &&
@@ -175,13 +175,13 @@
    },
    {
      fieldName: 'quantity',
      label: '工单数量',
      label: '订单数量',
      component: 'InputNumber',
      componentProps: {
        class: '!w-full',
        disabled: headerReadonly,
        min: 1,
        placeholder: '请输入工单数量',
        placeholder: '请输入订单数量',
        precision: 2,
      },
      rules: 'required',
@@ -256,20 +256,20 @@
  return [
    {
      fieldName: 'code',
      label: '工单编码',
      label: '订单编码',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入工单编码',
        placeholder: '请输入订单编码',
      },
    },
    {
      fieldName: 'name',
      label: '工单名称',
      label: '订单名称',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入工单名称',
        placeholder: '请输入订单名称',
      },
    },
    {
@@ -299,12 +299,12 @@
    },
    {
      fieldName: 'type',
      label: '工单类型',
      label: '订单类型',
      component: 'Select',
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_PRO_WORK_ORDER_TYPE, 'number'),
        placeholder: '请选择工单类型',
        placeholder: '请选择订单类型',
      },
    },
    {
@@ -324,7 +324,7 @@
  return [
    {
      field: 'code',
      title: '工单编码',
      title: '订单编码',
      fixed: 'left',
      width: 200,
      treeNode: true,
@@ -332,12 +332,12 @@
    },
    {
      field: 'name',
      title: '工单名称',
      title: '订单名称',
      minWidth: 150,
    },
    {
      field: 'type',
      title: '工单类型',
      title: '订单类型',
      width: 100,
      cellRender: {
        name: 'CellDict',
@@ -346,7 +346,7 @@
    },
    {
      field: 'orderSourceType',
      title: '工单来源',
      title: '订单来源',
      width: 100,
      cellRender: {
        name: 'CellDict',
@@ -380,7 +380,7 @@
    },
    {
      field: 'quantity',
      title: '工单数量',
      title: '订单数量',
      width: 100,
    },
    {
@@ -406,7 +406,7 @@
    },
    {
      field: 'status',
      title: '工单状态',
      title: '订单状态',
      width: 100,
      cellRender: {
        name: 'CellDict',
@@ -428,7 +428,7 @@
  ];
}
/** å·¥å• BOM å­è¡¨çš„字段 */
/** è®¢å• BOM å­è¡¨çš„字段 */
export function useBomGridColumns(
  editable: boolean,
  generatable: boolean,
@@ -486,7 +486,7 @@
  ];
}
/** å·¥å• BOM æ–°å¢ž/修改的表单 */
/** è®¢å• BOM æ–°å¢ž/修改的表单 */
export function useBomFormSchema(
  isCreate: boolean,
  productId?: number,
@@ -559,25 +559,25 @@
  ];
}
/** å·¥å•选择弹窗的搜索表单 */
/** è®¢å•选择弹窗的搜索表单 */
export function useWorkOrderSelectGridFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'code',
      label: '工单编码',
      label: '订单编码',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入工单编码',
        placeholder: '请输入订单编码',
      },
    },
    {
      fieldName: 'name',
      label: '工单名称',
      label: '订单名称',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入工单名称',
        placeholder: '请输入订单名称',
      },
    },
    {
@@ -598,18 +598,18 @@
    },
    {
      fieldName: 'status',
      label: '工单状态',
      label: '订单状态',
      component: 'Select',
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_PRO_WORK_ORDER_STATUS, 'number'),
        placeholder: '请选择工单状态',
        placeholder: '请选择订单状态',
      },
    },
  ];
}
/** å·¥å•选择弹窗的字段 */
/** è®¢å•选择弹窗的字段 */
export function useWorkOrderSelectGridColumns(
  multiple = false,
): VxeTableGridOptions<MesProWorkOrderApi.WorkOrder>['columns'] {
@@ -620,17 +620,17 @@
    },
    {
      field: 'code',
      title: '工单编码',
      title: '订单编码',
      width: 180,
    },
    {
      field: 'name',
      title: '工单名称',
      title: '订单名称',
      minWidth: 180,
    },
    {
      field: 'orderSourceType',
      title: '工单来源',
      title: '订单来源',
      width: 100,
      cellRender: {
        name: 'CellDict',
@@ -664,7 +664,7 @@
    },
    {
      field: 'quantity',
      title: '工单数量',
      title: '订单数量',
      width: 100,
    },
    {
@@ -679,7 +679,7 @@
    },
    {
      field: 'status',
      title: '工单状态',
      title: '订单状态',
      width: 100,
      cellRender: {
        name: 'CellDict',
@@ -695,7 +695,7 @@
  ];
}
/** å·¥å•物料需求子表的字段(只读) */
/** è®¢å•物料需求子表的字段(只读) */
export function useItemGridColumns(): VxeTableGridOptions<MesProWorkOrderBomApi.WorkOrderBom>['columns'] {
  return [
    {
src/views/mes/pro/workorder/index.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
@@ -39,32 +39,32 @@
  gridApi.query();
}
/** åˆ›å»ºå·¥å• */
/** åˆ›å»ºè®¢å• */
function handleCreate() {
  formModalApi.setData({ formType: 'create' }).open();
}
/** æ–°å¢žå­å·¥å• */
/** æ–°å¢žå­è®¢å• */
function handleAddChild(row: MesProWorkOrderApi.WorkOrder) {
  formModalApi.setData({ formType: 'create', parentRow: row }).open();
}
/** æŸ¥çœ‹å·¥å•详情 */
/** æŸ¥çœ‹è®¢å•详情 */
function handleDetail(row: MesProWorkOrderApi.WorkOrder) {
  formModalApi.setData({ formType: 'detail', id: row.id }).open();
}
/** ç¼–辑工单 */
/** ç¼–辑订单 */
function handleEdit(row: MesProWorkOrderApi.WorkOrder) {
  formModalApi.setData({ formType: 'update', id: row.id }).open();
}
/** å®Œæˆå·¥å• */
/** å®Œæˆè®¢å• */
function handleFinish(row: MesProWorkOrderApi.WorkOrder) {
  formModalApi.setData({ formType: 'finish', id: row.id }).open();
}
/** åˆ é™¤å·¥å• */
/** åˆ é™¤è®¢å• */
async function handleDelete(row: MesProWorkOrderApi.WorkOrder) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting', [row.code]),
@@ -79,14 +79,14 @@
  }
}
/** å–消工单 */
/** å–消订单 */
async function handleCancel(row: MesProWorkOrderApi.WorkOrder) {
  await cancelWorkOrder(row.id!);
  message.success('工单已取消');
  message.success('订单已取消');
  handleRefresh();
}
/** æŸ¥çœ‹å·¥å•条码 */
/** æŸ¥çœ‹è®¢å•条码 */
function handleBarcode(row: MesProWorkOrderApi.WorkOrder) {
  barcodeDetailRef.value?.openByBusiness(
    row.id!,
@@ -99,7 +99,7 @@
/** å¯¼å‡ºè¡¨æ ¼ */
async function handleExport() {
  const data = await exportWorkOrder(await gridApi.formApi.getValues());
  downloadFileFromBlobPart({ fileName: '生产工单.xls', source: data });
  downloadFileFromBlobPart({ fileName: '生产订单.xls', source: data });
}
const [Grid, gridApi] = useVbenVxeGrid({
@@ -143,12 +143,12 @@
<template>
  <Page auto-content-height><FormModal @success="handleRefresh" />
    <Grid table-title="生产工单列表">
    <Grid table-title="生产订单列表">
      <template #toolbar-tools>
        <TableAction
          :actions="[
            {
              label: $t('ui.actionTitle.create', ['工单']),
              label: $t('ui.actionTitle.create', ['订单']),
              type: 'primary',
              icon: ACTION_ICON.ADD,
              auth: ['mes:pro-work-order:create'],
@@ -215,7 +215,7 @@
              auth: ['mes:pro-work-order:update'],
              ifShow: row.status === MesProWorkOrderStatusEnum.CONFIRMED,
              popConfirm: {
                title: '确认要取消该工单吗?取消后不可恢复。',
                title: '确认要取消该订单吗?取消后不可恢复。',
                confirm: handleCancel.bind(null, row),
              },
            },
src/views/mes/pro/workorder/modules/bom-form.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { MesProWorkOrderBomApi } from '#/api/mes/pro/workorder/bom';
import { computed, ref } from 'vue';
@@ -19,7 +19,7 @@
const emit = defineEmits(['success']);
const formData = ref<MesProWorkOrderBomApi.WorkOrderBom>();
const workOrderId = ref<number>(); // æ‰€å±žå·¥å•编号
const workOrderId = ref<number>(); // æ‰€å±žè®¢å•编号
const getTitle = computed(() => {
  return formData.value?.id
src/views/mes/pro/workorder/modules/bom-list.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { FormType } from '../data';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
@@ -43,7 +43,7 @@
);
const isConfirmed = computed(
  () =>
    // å·²ç¡®è®¤æ€å¯ç”Ÿæˆå­å·¥å•
    // å·²ç¡®è®¤æ€å¯ç”Ÿæˆå­è®¢å•
    props.workOrder.status === MesProWorkOrderStatusEnum.CONFIRMED,
);
@@ -93,12 +93,12 @@
  }
}
/** ä»Ž BOM è¡Œç”Ÿæˆå­å·¥å•(通知父组件) */
/** ä»Ž BOM è¡Œç”Ÿæˆå­è®¢å•(通知父组件) */
function handleGenerateWorkOrder(row: MesProWorkOrderBomApi.WorkOrderBom) {
  emit('generateWorkOrder', row);
}
/** æ˜¯å¦å±•示生成子工单按钮:已确认 + è‡ªåˆ¶ + äº§å“ç±»åž‹ BOM è¡Œ */
/** æ˜¯å¦å±•示生成子订单按钮:已确认 + è‡ªåˆ¶ + äº§å“ç±»åž‹ BOM è¡Œ */
function showGenerate(row: MesProWorkOrderBomApi.WorkOrderBom) {
  return (
    isConfirmed.value &&
@@ -140,7 +140,7 @@
<template>
  <div>
    <BomFormModal @success="handleRefresh" />
    <Grid table-title="工单 BOM">
    <Grid table-title="订单 BOM">
      <template v-if="isEditable" #toolbar-tools>
        <TableAction
          :actions="[
@@ -175,7 +175,7 @@
              },
            },
            {
              label: '生成工单',
              label: '生成订单',
              type: 'link',
              ifShow: showGenerate(row),
              onClick: handleGenerateWorkOrder.bind(null, row),
src/views/mes/pro/workorder/modules/form.vue
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
@@ -49,19 +49,19 @@
  const isChild = !!formData.value?.parentId;
  switch (formType.value) {
    case 'confirm': {
      return '确认工单';
      return '确认订单';
    }
    case 'detail': {
      return $t('ui.actionTitle.view', ['工单']);
      return $t('ui.actionTitle.view', ['订单']);
    }
    case 'finish': {
      return '完成工单';
      return '完成订单';
    }
    case 'update': {
      return isChild ? '编辑子工单' : $t('ui.actionTitle.edit', ['工单']);
      return isChild ? '编辑子订单' : $t('ui.actionTitle.edit', ['订单']);
    }
    default: {
      return isChild ? '新增子工单' : $t('ui.actionTitle.create', ['工单']);
      return isChild ? '新增子订单' : $t('ui.actionTitle.create', ['订单']);
    }
  }
});
@@ -86,7 +86,7 @@
  modalApi.setState({ showConfirmButton: isEditable.value });
}
/** æŸ¥çœ‹å·¥å•条码 */
/** æŸ¥çœ‹è®¢å•条码 */
function handleBarcode() {
  if (!formData.value?.id) {
    return;
@@ -99,7 +99,7 @@
  );
}
/** ç¡®è®¤å·¥å•:编辑态有修改时先保存,再确认 */
/** ç¡®è®¤è®¢å•:编辑态有修改时先保存,再确认 */
async function handleConfirm() {
  const { valid } = await formApi.validate();
  if (!valid || !formData.value?.id) {
@@ -117,7 +117,7 @@
      }
    }
    await confirmWorkOrder(formData.value.id);
    message.success('工单已确认');
    message.success('订单已确认');
    await modalApi.close();
    emit('success');
  } finally {
@@ -125,7 +125,7 @@
  }
}
/** å®Œæˆå·¥å• */
/** å®Œæˆè®¢å• */
async function handleFinish() {
  if (!formData.value?.id) {
    return;
@@ -133,7 +133,7 @@
  modalApi.lock();
  try {
    await finishWorkOrder(formData.value.id);
    message.success('工单已完成');
    message.success('订单已完成');
    await modalApi.close();
    emit('success');
  } finally {
@@ -141,7 +141,7 @@
  }
}
/** ä»Ž BOM ç‰©æ–™è¡Œé¢„填子工单:复用当前工单上下文,进入新增态 */
/** ä»Ž BOM ç‰©æ–™è¡Œé¢„填子订单:复用当前订单上下文,进入新增态 */
async function handleGenerateWorkOrder(
  bomRow: MesProWorkOrderBomApi.WorkOrderBom,
) {
@@ -167,7 +167,7 @@
    vendorId: current.vendorId,
  });
  originalSnapshot.value = JSON.stringify(await formApi.getValues());
  message.info('已从 BOM ç‰©æ–™é¢„填子工单,请补充工单编码等信息后保存');
  message.info('已从 BOM ç‰©æ–™é¢„填子订单,请补充订单编码等信息后保存');
}
const [Modal, modalApi] = useVbenModal({
@@ -195,7 +195,7 @@
          status: MesProWorkOrderStatusEnum.PREPARE,
        };
        formType.value = 'update';
        // åˆ›å»ºæˆåŠŸåŽåˆ‡æ¢ç¼–è¾‘æ€ï¼Œé‡æŒ‚ schema è®©ã€Œå·¥å•状态」字段显现
        // åˆ›å»ºæˆåŠŸåŽåˆ‡æ¢ç¼–è¾‘æ€ï¼Œé‡æŒ‚ schema è®©ã€Œè®¢å•状态」字段显现
        applySchema();
        await formApi.setValues(formData.value);
      }
@@ -231,7 +231,7 @@
        modalApi.unlock();
      }
    } else if (data?.parentRow) {
      // æ–°å¢žå­å·¥å•时,预填父工单信息
      // æ–°å¢žå­è®¢å•时,预填父订单信息
      const parent = data.parentRow;
      await formApi.setValues({
        clientId: parent.clientId,
@@ -254,7 +254,7 @@
    <!-- BOM / ç‰©æ–™éœ€æ±‚ Tab:非新建态展示 -->
    <template v-if="formData?.id">
      <Tabs v-model:active-key="subTabsName" class="mx-4">
        <Tabs.TabPane key="bom" tab="工单 BOM">
        <Tabs.TabPane key="bom" tab="订单 BOM">
          <BomList
            :form-type="formType"
            :work-order="formData"
@@ -271,14 +271,14 @@
      <div class="flex flex-auto items-center gap-2">
        <Popconfirm
          v-if="canConfirm || isConfirm"
          title="确认要完成工单编制吗?确认后将不能更改。"
          title="确认要完成订单编制吗?确认后将不能更改。"
          @confirm="handleConfirm"
        >
          <Button type="primary">确认</Button>
        </Popconfirm>
        <Popconfirm
          v-if="isFinish"
          title="确认要完成该工单吗?"
          title="确认要完成该订单吗?"
          @confirm="handleFinish"
        >
          <Button type="primary">完成</Button>
src/views/mes/qc/batchtrace/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmBatchApi } from '#/api/mes/wm/batch';
import type { DescriptionItemSchema } from '#/components/description';
@@ -138,7 +138,7 @@
  return [
    {
      field: 'workOrderCode',
      title: '生产工单号',
      title: '生产订单号',
      width: 160,
    },
    {
@@ -219,7 +219,7 @@
    },
    {
      field: 'workOrderCode',
      label: '生产工单',
      label: '生产订单',
    },
    {
      field: 'workstationCode',
src/views/mes/qc/ipqc/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesQcIpqcApi } from '#/api/mes/qc/ipqc';
import type { MesQcIpqcLineApi } from '#/api/mes/qc/ipqc/line';
@@ -151,12 +151,12 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        placeholder: '请选择生产工单',
        placeholder: '请选择生产订单',
        status: MesProWorkOrderStatusEnum.CONFIRMED,
        // å·¥å•变更时清空生产任务
        // è®¢å•变更时清空生产任务
        onChange: () => formApi?.setFieldValue('taskId', undefined),
      },
      rules: 'selectRequired',
@@ -164,7 +164,7 @@
        triggerFields: ['sourceDocId'],
        componentProps: (values) => ({
          disabled: !!values.sourceDocId,
          placeholder: '请选择生产工单',
          placeholder: '请选择生产订单',
          status: MesProWorkOrderStatusEnum.CONFIRMED,
          onChange: () => formApi?.setFieldValue('taskId', undefined),
        }),
@@ -379,10 +379,10 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        placeholder: '请选择生产工单',
        placeholder: '请选择生产订单',
      },
    },
    {
@@ -431,7 +431,7 @@
    },
    {
      field: 'workOrderCode',
      title: '生产工单编号',
      title: '生产订单编号',
      width: 140,
    },
    {
src/views/mes/wm/barcode/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmBarcodeApi } from '#/api/mes/wm/barcode';
import type { MesWmBarcodeConfigApi } from '#/api/mes/wm/barcode/config';
@@ -221,7 +221,7 @@
    },
    {
      fieldName: 'bizId',
      label: '工单',
      label: '订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        status: MesProWorkOrderStatusEnum.CONFIRMED,
src/views/mes/wm/batch/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmBatchApi } from '#/api/mes/wm/batch';
import type { DescriptionItemSchema } from '#/components/description';
@@ -57,10 +57,10 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        placeholder: '请选择生产工单',
        placeholder: '请选择生产订单',
      },
    },
    {
@@ -229,7 +229,7 @@
    },
    {
      field: 'workOrderCode',
      title: '工单编码',
      title: '订单编码',
      width: 140,
    },
    {
@@ -351,7 +351,7 @@
    },
    {
      field: 'workOrderCode',
      label: '生产工单',
      label: '生产订单',
      render: (value) => value || '-',
    },
    {
src/views/mes/wm/outsourceissue/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
@@ -105,10 +105,10 @@
    },
    {
      fieldName: 'workOrderId',
      label: '外协工单',
      label: '外协订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        // é€‰æ‹©å¤–协工单后,自动回填供应商
        // é€‰æ‹©å¤–协订单后,自动回填供应商
        onChange: async (workOrder?: MesProWorkOrderApi.WorkOrder) => {
          await formApi?.setFieldValue('vendorId', workOrder?.vendorId);
        },
@@ -195,7 +195,7 @@
    },
    {
      field: 'workOrderCode',
      title: '生产工单号',
      title: '生产订单号',
      minWidth: 140,
    },
    {
src/views/mes/wm/outsourcereceipt/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
import type { MesWmOutsourceReceiptApi } from '#/api/mes/wm/outsourcereceipt';
@@ -95,10 +95,10 @@
    },
    {
      fieldName: 'workOrderId',
      label: '外协工单',
      label: '外协订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        // é€‰æ‹©å¤–协工单后,自动回填供应商
        // é€‰æ‹©å¤–协订单后,自动回填供应商
        onChange: async (workOrder?: MesProWorkOrderApi.WorkOrder) => {
          await formApi?.setFieldValue('vendorId', workOrder?.vendorId);
        },
@@ -162,11 +162,11 @@
    },
    {
      fieldName: 'workOrderCode',
      label: '外协工单号',
      label: '外协订单号',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入外协工单号',
        placeholder: '请输入外协订单号',
      },
    },
    {
@@ -218,7 +218,7 @@
    },
    {
      field: 'workOrderCode',
      title: '外协工单号',
      title: '外协订单号',
      minWidth: 140,
    },
    {
src/views/mes/wm/packages/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmPackageApi } from '#/api/mes/wm/packages';
import type { MesWmPackageLineApi } from '#/api/mes/wm/packages/line';
@@ -419,10 +419,10 @@
  return [
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        placeholder: '请选择生产工单',
        placeholder: '请选择生产订单',
        status: MesProWorkOrderStatusEnum.CONFIRMED,
      },
      rules: 'selectRequired',
@@ -501,7 +501,7 @@
    },
    {
      field: 'workOrderCode',
      title: '生产工单编号',
      title: '生产订单编号',
      minWidth: 140,
    },
    {
src/views/mes/wm/productissue/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
import type { MesWmProductIssueApi } from '#/api/mes/wm/productissue';
@@ -107,7 +107,7 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        status: MesProWorkOrderStatusEnum.CONFIRMED,
@@ -197,7 +197,7 @@
    },
    {
      field: 'workOrderCode',
      title: '生产工单',
      title: '生产订单',
      minWidth: 140,
    },
    {
src/views/mes/wm/productreceipt/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
import type { MesWmProductReceiptApi } from '#/api/mes/wm/productreceipt';
@@ -104,7 +104,7 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        status: MesProWorkOrderStatusEnum.CONFIRMED,
@@ -172,7 +172,7 @@
    },
    {
      field: 'workOrderCode',
      title: '生产工单',
      title: '生产订单',
      minWidth: 140,
    },
    {
src/views/mes/wm/returnissue/data.ts
@@ -1,4 +1,4 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
import type { MesWmReturnIssueApi } from '#/api/mes/wm/returnissue';
@@ -105,7 +105,7 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        status: MesProWorkOrderStatusEnum.CONFIRMED,
@@ -168,10 +168,10 @@
    },
    {
      fieldName: 'workOrderId',
      label: '生产工单',
      label: '生产订单',
      component: markRaw(ProWorkOrderSelect),
      componentProps: {
        placeholder: '请选择生产工单',
        placeholder: '请选择生产订单',
      },
    },
    {
@@ -212,7 +212,7 @@
    },
    {
      field: 'workOrderCode',
      title: '生产工单',
      title: '生产订单',
      minWidth: 140,
    },
    {