已添加2个文件
已修改50个文件
4054 ■■■■■ 文件已修改
.idea/mom-pro2-before.iml 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mdm/unit/index.ts 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/processdesign/parameter/index.ts 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/processdesign/template/index.ts 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/arrivalnotice/index.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/itemreceipt/index.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/returnvendor/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/packages/constants/src/biz-wms-enum.ts 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes/modules/mes.ts 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/itemreceipt/modules/line-list.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/machinery/modules/form.vue 230 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/md/item/components/select-dialog.vue 388 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/md/item/index.vue 282 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/md/item/modules/form.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/md/vendor/components/select.vue 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/md/workstation/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/md/workstation/modules/form.vue 249 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/md/workstation/workshop/modules/form.vue 198 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/card/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/card/modules/form.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/index.vue 271 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/modules/form.vue 481 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/process-design/parameter/data.ts 189 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/process-design/parameter/index.vue 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/process-design/template/data.ts 131 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/process-design/template/index.vue 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/process-design/template/modules/form.vue 317 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/process-design/template/modules/parameter-select-dialog.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/tm/tool/modules/form.vue 199 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/outsourcereceipt/modules/line-list.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productreceipt/modules/line-list.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/returnissue/modules/line-list.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/returnsales/modules/line-list.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/returnvendor/modules/line-list.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sn/modules/detail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/transfer/modules/line-list.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/warehouse/area/modules/form.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/warehouse/location/modules/form.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/warehouse/modules/form.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/arrivalnotice/data.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/arrivalnotice/index.vue 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/arrivalnotice/modules/po-select-dialog.vue 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/barcode/components/detail.vue 139 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/barcode/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/itemreceipt/index.vue 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/itemreceipt/modules/an-select-dialog.vue 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/itemreceipt/modules/form.vue 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/returnvendor/data.ts 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/warehouse/area/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/warehouse/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/warehouse/location/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/mom-pro2-before.iml
@@ -1,7 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
  <component name="NewModuleRootManager">
    <content url="file://$MODULE_DIR$" />
    <content url="file://$MODULE_DIR$">
      <excludeFolder url="file://$MODULE_DIR$/node_modules" />
      <excludeFolder url="file://$MODULE_DIR$/.pnpm" />
      <excludeFolder url="file://$MODULE_DIR$/.vite" />
      <excludeFolder url="file://$MODULE_DIR$/.cache" />
      <excludeFolder url="file://$MODULE_DIR$/dist" />
      <excludeFolder url="file://$MODULE_DIR$/build" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
src/api/mdm/unit/index.ts
@@ -1,6 +1,6 @@
import type { PageParam, PageResult } from '#/packages/effects/request/src';
import type { PageParam, PageResult } from "#/packages/effects/request/src";
import { requestClient } from '#/api/request';
import { requestClient } from "#/api/request";
export namespace MdmUnitApi {
  /** è®¡é‡å•位信息 */
@@ -18,7 +18,7 @@
/** æŸ¥è¯¢è®¡é‡å•位分页 */
export function getUnitPage(params: PageParam) {
  return requestClient.get<PageResult<MdmUnitApi.Unit>>('/mdm/unit/page', {
  return requestClient.get<PageResult<MdmUnitApi.Unit>>("/mdm/unit/page", {
    params,
  });
}
@@ -35,12 +35,12 @@
/** æ–°å¢žè®¡é‡å•位 */
export function createUnit(data: MdmUnitApi.Unit) {
  return requestClient.post('/mdm/unit/create', data);
  return requestClient.post("/mdm/unit/create", data);
}
/** ä¿®æ”¹è®¡é‡å•位 */
export function updateUnit(data: MdmUnitApi.Unit) {
  return requestClient.put('/mdm/unit/update', data);
  return requestClient.put("/mdm/unit/update", data);
}
/** åˆ é™¤è®¡é‡å•位 */
src/api/mes/pro/processdesign/parameter/index.ts
@@ -6,22 +6,25 @@
  /** å·¥è‰ºå‚æ•° */
  export interface Parameter {
    id?: number;
    code?: string;
    name?: string;
    type?: number;
    unit?: string;
    requiredFlag?: boolean;
    dictType?: string;
    apiUrl?: string;
    paramCode?: string;
    paramName?: string;
    paramType?: number;
    unitMeasureId?: number;
    unitMeasureName?: string;
    required?: boolean;
    remark?: string;
    createTime?: Date;
  }
}
/** æŸ¥è¯¢å·¥è‰ºå‚数分页 */
export function getParameterPage(params: PageParam) {
export function getParameterPage(params: PageParam & {
  paramCode?: string;
  paramName?: string;
  paramType?: number;
}) {
  return requestClient.get<PageResult<MesPdParameterApi.Parameter>>(
    '/mes/pro/process-design/parameter/page',
    '/mes/pd/process-param/page',
    { params },
  );
}
@@ -29,21 +32,21 @@
/** æŸ¥è¯¢å·¥è‰ºå‚数精简列表 */
export function getParameterSimpleList() {
  return requestClient.get<MesPdParameterApi.Parameter[]>(
    '/mes/pro/process-design/parameter/simple-list',
    '/mes/pd/process-param/simple-list',
  );
}
/** æŸ¥è¯¢å·¥è‰ºå‚数详情 */
export function getParameter(id: number) {
  return requestClient.get<MesPdParameterApi.Parameter>(
    `/mes/pro/process-design/parameter/get?id=${id}`,
    `/mes/pd/process-param/get?id=${id}`,
  );
}
/** æ–°å¢žå·¥è‰ºå‚æ•° */
export function createParameter(data: MesPdParameterApi.Parameter) {
  return requestClient.post(
    '/mes/pro/process-design/parameter/create',
    '/mes/pd/process-param/create',
    data,
  );
}
@@ -51,7 +54,7 @@
/** ä¿®æ”¹å·¥è‰ºå‚æ•° */
export function updateParameter(data: MesPdParameterApi.Parameter) {
  return requestClient.put(
    '/mes/pro/process-design/parameter/update',
    '/mes/pd/process-param/update',
    data,
  );
}
@@ -59,6 +62,14 @@
/** åˆ é™¤å·¥è‰ºå‚æ•° */
export function deleteParameter(id: number) {
  return requestClient.delete(
    `/mes/pro/process-design/parameter/delete?id=${id}`,
    `/mes/pd/process-param/delete?id=${id}`,
  );
}
/** å¯¼å‡ºå·¥è‰ºå‚æ•° */
export function exportParameterExcel(params: any) {
  return requestClient.download(
    '/mes/pd/process-param/export-excel',
    { params },
  );
}
src/api/mes/pro/processdesign/template/index.ts
@@ -1,53 +1,65 @@
import type { PageParam, PageResult } from '../../../../../packages/effects/request/src';
import type { PageParam, PageResult } from "../../../../../packages/effects/request/src";
import { requestClient } from '#/api/request';
import { requestClient } from "#/api/request";
export namespace MesPdTemplateApi {
  /** æ¨¡æ¿æ˜Žç»† */
  export interface TemplateDetail {
    id?: number;
    templateId?: number;
    processParamId?: number;
    paramCode?: string;
    paramName?: string;
    paramType?: number;
    unitMeasureName?: string;
    required?: boolean;
    remark?: string;
  }
  /** å·¥è‰ºå‚数模板 */
  export interface Template {
    id?: number;
    name?: string;
    type?: number;
    parameterIds?: number[];
    createUser?: string;
    templateCode?: string;
    templateName?: string;
    templateType?: number;
    remark?: string;
    details?: TemplateDetail[];
    createTime?: Date;
  }
}
/** æŸ¥è¯¢å·¥è‰ºå‚数模板分页 */
export function getTemplatePage(params: PageParam) {
  return requestClient.get<PageResult<MesPdTemplateApi.Template>>(
    '/mes/pro/process-design/template/page',
    { params },
  );
export function getTemplatePage(
  params: PageParam & {
    templateCode?: string;
    templateName?: string;
    templateType?: number;
  },
) {
  return requestClient.get<PageResult<MesPdTemplateApi.Template>>("/mes/pd/process-param-template/page", { params });
}
/** æŸ¥è¯¢å·¥è‰ºå‚数模板详情 */
export function getTemplate(id: number) {
  return requestClient.get<MesPdTemplateApi.Template>(
    `/mes/pro/process-design/template/get?id=${id}`,
  );
  return requestClient.get<MesPdTemplateApi.Template>(`/mes/pd/process-param-template/get?id=${id}`);
}
/** æ–°å¢žå·¥è‰ºå‚数模板 */
export function createTemplate(data: MesPdTemplateApi.Template) {
  return requestClient.post(
    '/mes/pro/process-design/template/create',
    data,
  );
  return requestClient.post("/mes/pd/process-param-template/create", data);
}
/** ä¿®æ”¹å·¥è‰ºå‚数模板 */
export function updateTemplate(data: MesPdTemplateApi.Template) {
  return requestClient.put(
    '/mes/pro/process-design/template/update',
    data,
  );
  return requestClient.put("/mes/pd/process-param-template/update", data);
}
/** åˆ é™¤å·¥è‰ºå‚数模板 */
export function deleteTemplate(id: number) {
  return requestClient.delete(
    `/mes/pro/process-design/template/delete?id=${id}`,
  );
  return requestClient.delete(`/mes/pd/process-param-template/delete?id=${id}`);
}
/** å¯¼å‡ºå·¥è‰ºå‚数模板 */
export function exportTemplateExcel(params: any) {
  return requestClient.download("/mes/pd/process-param-template/export-excel", { params });
}
src/api/mes/wm/arrivalnotice/index.ts
@@ -62,3 +62,10 @@
    params,
  });
}
/** æ ¹æ®é‡‡è´­è®¢å•生成到货通知单 */
export function generateArrivalNoticeFromPurchaseOrder(purchaseOrderId: number) {
  return requestClient.post<number>(
    `/mes/wm/arrival-notice/generate-from-purchase-order?purchaseOrderId=${purchaseOrderId}`,
  );
}
src/api/mes/wm/itemreceipt/index.ts
@@ -84,3 +84,20 @@
    params,
  });
}
/** æ ¹æ®åˆ°è´§é€šçŸ¥å•生成采购入库单 */
export function generateItemReceiptFromArrivalNotice(arrivalNoticeId: number) {
  return requestClient.post<number>(
    `/mes/wm/item-receipt/generate-from-arrival-notice?arrivalNoticeId=${arrivalNoticeId}`,
  );
}
/** æäº¤é‡‡è´­å…¥åº“单审批 */
export function submitItemReceiptApproval(
  id: number,
  processDefinitionKey: string,
) {
  return requestClient.put(
    `/mes/wm/item-receipt/submit-approval?id=${id}&processDefinitionKey=${processDefinitionKey}`,
  );
}
src/api/mes/wm/returnvendor/index.ts
@@ -8,6 +8,7 @@
    id?: number; // é€€è´§å•编号
    code?: string; // é€€è´§å•编号
    name?: string; // é€€è´§å•名称
    purchaseOrderId?: number; // é‡‡è´­è®¢å•编号
    purchaseOrderCode?: string; // é‡‡è´­è®¢å•号
    vendorId?: number; // ä¾›åº”商编号
    vendorCode?: string; // ä¾›åº”商编码
src/packages/constants/src/biz-wms-enum.ts
@@ -56,3 +56,25 @@
  }
  return prefix + result;
}
/** ç›˜ç‚¹æ¡ä»¶ç±»åž‹æžšä¸¾ */
export const WlsStockTakingParamTypeEnum = {
  WAREHOUSE: 1, // ä»“库
  LOCATION: 2, // åº“区
  AREA: 3, // åº“位
  ITEM: 4, // ç‰©æ–™
  BATCH: 5, // æ‰¹æ¬¡
  QUALITY_STATUS: 6, // è´¨é‡çŠ¶æ€
} as const;
/** ç›˜ç‚¹ç±»åž‹æžšä¸¾ */
export const WlsStockTakingTypeEnum = {
  STATIC: 1, // é™æ€ç›˜ç‚¹
  DYNAMIC: 2, // åŠ¨æ€ç›˜ç‚¹
} as const;
/** è°ƒæ‹¨ç±»åž‹æžšä¸¾ */
export const WlsTransferTypeEnum = {
  INNER: 1, // å†…部调拨
  OUTER: 2, // å¤–部调拨
} as const;
src/router/routes/modules/mes.ts
@@ -12,33 +12,6 @@
    },
    children: [
      {
        path: 'wm/warehouse/location',
        name: 'MesWmLocation',
        meta: {
          title: '库区设置',
          activePath: '/mes/wm/warehouse',
        },
        component: () => import('#/views/mes/wm/warehouse/location/index.vue'),
      },
      {
        path: 'wm/warehouse/area',
        name: 'MesWmArea',
        meta: {
          title: '库位设置',
          activePath: '/mes/wm/warehouse',
        },
        component: () => import('#/views/mes/wm/warehouse/area/index.vue'),
      },
      {
        path: 'wm/barcode/config',
        name: 'MesWmBarcodeConfig',
        meta: {
          title: '条码配置',
          activePath: '/mes/wm/barcode',
        },
        component: () => import('#/views/mes/wm/barcode/config/index.vue'),
      },
      {
        path: 'pro/task/gantt-edit',
        name: 'MesProTaskGanttEdit',
        meta: {
src/views/itemreceipt/modules/line-list.vue
@@ -18,7 +18,7 @@
  getItemReceiptLinePage,
} from '#/api/mes/wm/itemreceipt/line';
import { $t } from '#/locales';
import { PrinterLabel } from '#/views/mes/wm/barcode/components';
import { PrinterLabel } from '#/views/wls/barcode/components';
import { useLineGridColumns } from '../data';
import DetailForm from './detail-form.vue';
src/views/mes/dv/machinery/modules/form.vue
@@ -1,144 +1,144 @@
<script lang="ts" setup>
import type { FormType } from '../data';
  import type { FormType } from "../data";
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
  import type { MesDvMachineryApi } from "#/api/mes/dv/machinery";
import { computed, ref } from 'vue';
  import { computed, ref } from "vue";
import { useVbenModal } from '../../../../../packages/effects/common-ui/src';
import { BarcodeBizTypeEnum } from '../../../../../packages/constants/src';
  import { useVbenModal } from "../../../../../packages/effects/common-ui/src";
  import { BarcodeBizTypeEnum } from "../../../../../packages/constants/src";
import { Button, message, Tabs } from 'ant-design-vue';
  import { Button, message, Tabs } from "ant-design-vue";
import { useVbenForm } from '#/adapter/form';
import {
  createMachinery,
  getMachinery,
  updateMachinery,
} from '#/api/mes/dv/machinery';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
  import { useVbenForm } from "#/adapter/form";
  import {
    createMachinery,
    getMachinery,
    updateMachinery,
  } from "#/api/mes/dv/machinery";
  import { $t } from "#/locales";
  import { BarcodeDetail } from "#/views/wls/barcode/components";
import { useFormSchema } from '../data';
import CheckRecordList from './check-record-list.vue';
import MaintenRecordList from './mainten-record-list.vue';
import RepairList from './repair-list.vue';
  import { useFormSchema } from "../data";
  import CheckRecordList from "./check-record-list.vue";
  import MaintenRecordList from "./mainten-record-list.vue";
  import RepairList from "./repair-list.vue";
const emit = defineEmits(['success']);
const formType = ref<FormType>('create'); // è¡¨å•模式
const subTabsName = ref('check'); // å½“前资源页签
const formData = ref<MesDvMachineryApi.Machinery>();
const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
const isDetail = computed(() => formType.value === 'detail'); // æ˜¯å¦æŸ¥çœ‹æ¨¡å¼
const getTitle = computed(() => {
  if (formType.value === 'detail') {
    return '查看设备';
  }
  return formType.value === 'update' ? '修改设备' : '新增设备';
});
  const emit = defineEmits(["success"]);
  const formType = ref<FormType>("create"); // è¡¨å•模式
  const subTabsName = ref("check"); // å½“前资源页签
  const formData = ref<MesDvMachineryApi.Machinery>();
  const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
  const isDetail = computed(() => formType.value === "detail"); // æ˜¯å¦æŸ¥çœ‹æ¨¡å¼
  const getTitle = computed(() => {
    if (formType.value === "detail") {
      return "查看设备";
    }
    return formType.value === "update" ? "修改设备" : "新增设备";
  });
const [Form, formApi] = useVbenForm({
  commonConfig: {
    componentProps: {
      class: 'w-full',
  const [Form, formApi] = useVbenForm({
    commonConfig: {
      componentProps: {
        class: "w-full",
      },
      formItemClass: "col-span-1",
      labelWidth: 110,
    },
    formItemClass: 'col-span-1',
    labelWidth: 110,
  },
  wrapperClass: 'grid-cols-3',
  layout: 'horizontal',
  schema: [],
  showDefaultActions: false,
});
    wrapperClass: "grid-cols-3",
    layout: "horizontal",
    schema: [],
    showDefaultActions: false,
  });
/** æŸ¥çœ‹è®¾å¤‡æ¡ç  */
function handleBarcode() {
  if (!formData.value?.id) {
    return;
  /** æŸ¥çœ‹è®¾å¤‡æ¡ç  */
  function handleBarcode() {
    if (!formData.value?.id) {
      return;
    }
    barcodeDetailRef.value?.openByBusiness(
      formData.value.id,
      BarcodeBizTypeEnum.MACHINERY,
      formData.value.code,
      formData.value.name
    );
  }
  barcodeDetailRef.value?.openByBusiness(
    formData.value.id,
    BarcodeBizTypeEnum.MACHINERY,
    formData.value.code,
    formData.value.name,
  );
}
const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    if (isDetail.value) {
      await modalApi.close();
      return;
    }
    const { valid } = await formApi.validate();
    if (!valid) {
      return;
    }
    modalApi.lock();
    // æäº¤è¡¨å•
    const data = (await formApi.getValues()) as MesDvMachineryApi.Machinery;
    try {
      await (data.id ? updateMachinery(data) : createMachinery(data));
      // å…³é—­å¹¶æç¤º
      await modalApi.close();
      emit('success');
      message.success($t('ui.actionMessage.operationSuccess'));
    } finally {
      modalApi.unlock();
    }
  },
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) {
      formData.value = undefined;
      return;
    }
    subTabsName.value = 'check';
    // åŠ è½½æ•°æ®
    const data = modalApi.getData<{ formType: FormType; id?: number }>();
    formType.value = data.formType;
    formApi.setState({ schema: useFormSchema(formType.value, formApi) });
    formApi.setDisabled(formType.value === 'detail');
    modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
    if (!data?.id) {
      return;
    }
    modalApi.lock();
    try {
      formData.value = await getMachinery(data.id);
      // è®¾ç½®åˆ° values
      await formApi.setValues(formData.value);
    } finally {
      modalApi.unlock();
    }
  },
});
  const [Modal, modalApi] = useVbenModal({
    async onConfirm() {
      if (isDetail.value) {
        await modalApi.close();
        return;
      }
      const { valid } = await formApi.validate();
      if (!valid) {
        return;
      }
      modalApi.lock();
      // æäº¤è¡¨å•
      const data = (await formApi.getValues()) as MesDvMachineryApi.Machinery;
      try {
        await (data.id ? updateMachinery(data) : createMachinery(data));
        // å…³é—­å¹¶æç¤º
        await modalApi.close();
        emit("success");
        message.success($t("ui.actionMessage.operationSuccess"));
      } finally {
        modalApi.unlock();
      }
    },
    async onOpenChange(isOpen: boolean) {
      if (!isOpen) {
        formData.value = undefined;
        return;
      }
      subTabsName.value = "check";
      // åŠ è½½æ•°æ®
      const data = modalApi.getData<{ formType: FormType; id?: number }>();
      formType.value = data.formType;
      formApi.setState({ schema: useFormSchema(formType.value, formApi) });
      formApi.setDisabled(formType.value === "detail");
      modalApi.setState({ showConfirmButton: formType.value !== "detail" });
      if (!data?.id) {
        return;
      }
      modalApi.lock();
      try {
        formData.value = await getMachinery(data.id);
        // è®¾ç½®åˆ° values
        await formApi.setValues(formData.value);
      } finally {
        modalApi.unlock();
      }
    },
  });
</script>
<template>
  <Modal :title="getTitle" class="w-4/5">
  <Modal :title="getTitle"
         class="w-4/5">
    <Form class="mx-4" />
    <Tabs
      v-if="formType !== 'create' && formData?.id"
      v-model:active-key="subTabsName"
      class="mx-4 mt-4"
    >
      <Tabs.TabPane key="check" tab="点检记录">
    <Tabs v-if="formType !== 'create' && formData?.id"
          v-model:active-key="subTabsName"
          class="mx-4 mt-4">
      <Tabs.TabPane key="check"
                    tab="点检记录">
        <CheckRecordList :machinery-id="formData.id" />
      </Tabs.TabPane>
      <Tabs.TabPane key="mainten" tab="保养记录">
      <Tabs.TabPane key="mainten"
                    tab="保养记录">
        <MaintenRecordList :machinery-id="formData.id" />
      </Tabs.TabPane>
      <Tabs.TabPane key="repair" tab="维修记录">
      <Tabs.TabPane key="repair"
                    tab="维修记录">
        <RepairList :machinery-id="formData.id" />
      </Tabs.TabPane>
    </Tabs>
    <template #prepend-footer>
      <div class="flex flex-auto items-center">
        <Button
          v-if="isDetail && formData?.id"
          type="primary"
          @click="handleBarcode"
        >
        <Button v-if="isDetail && formData?.id"
                type="primary"
                @click="handleBarcode">
          æŸ¥çœ‹æ¡ç 
        </Button>
      </div>
src/views/mes/md/item/components/select-dialog.vue
@@ -1,228 +1,224 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdItemApi } from '#/api/mes/md/item';
import type { MesMdItemTypeApi } from '#/api/mes/md/item/type';
  import type { VxeTableGridOptions } from "#/adapter/vxe-table";
  import type { MesMdItemApi } from "#/api/mes/md/item";
  import type { MesMdItemTypeApi } from "#/api/mes/md/item/type";
import { nextTick, ref } from 'vue';
  import { nextTick, ref } from "vue";
import { CommonStatusEnum } from '../../../../../packages/constants/src';
  import { CommonStatusEnum } from "../../../../../packages/constants/src";
import { Card, message, Modal } from 'ant-design-vue';
  import { Card, message, Modal } from "ant-design-vue";
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getItemPage } from '#/api/mes/md/item';
import { MdItemTypeTree } from '#/views/mes/md/item/type/components';
  import { useVbenVxeGrid } from "#/adapter/vxe-table";
  import { getItemPage } from "#/api/mes/md/item";
  import { MdItemTypeTree } from "#/views/mes/md/item/type/components";
import { useItemSelectGridColumns, useItemSelectGridFormSchema } from '../data';
  import { useItemSelectGridColumns, useItemSelectGridFormSchema } from "../data";
defineOptions({ name: 'MdItemSelectDialog' });
  defineOptions({ name: "MdItemSelectDialog" });
const emit = defineEmits<{
  selected: [rows: MesMdItemApi.Item[]];
}>();
  const emit = defineEmits<{
    selected: [rows: MesMdItemApi.Item[]];
  }>();
const open = ref(false); // å¼¹çª—是否打开
const multiple = ref(true); // æ˜¯å¦å¤šé€‰
const selectedRows = ref<MesMdItemApi.Item[]>([]); // å·²é€‰ç‰©æ–™åˆ—表
const selectedItemTypeId = ref<number>(); // å½“前筛选分类编号
const preSelectedIds = ref<number[]>([]); // é¢„选物料编号列表
const typeTreeRef = ref<InstanceType<typeof MdItemTypeTree>>(); // ç‰©æ–™åˆ†ç±»æ ‘
  const open = ref(false); // å¼¹çª—是否打开
  const multiple = ref(true); // æ˜¯å¦å¤šé€‰
  const selectedRows = ref<MesMdItemApi.Item[]>([]); // å·²é€‰ç‰©æ–™åˆ—表
  const selectedItemTypeId = ref<number>(); // å½“前筛选分类编号
  const preSelectedIds = ref<number[]>([]); // é¢„选物料编号列表
  const typeTreeRef = ref<InstanceType<typeof MdItemTypeTree>>(); // ç‰©æ–™åˆ†ç±»æ ‘
/** èŽ·å–å½“å‰è¡¨æ ¼æ•°æ® */
function getTableRows() {
  return gridApi.grid.getTableData().fullData as MesMdItemApi.Item[];
}
/** èŽ·å–å¤šé€‰è®°å½•ï¼ŒåŒ…å« VXE reserve è·¨é¡µè®°å½• */
function getMultipleSelectedRows() {
  const selectedMap = new Map<number, MesMdItemApi.Item>();
  const records = [
    ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
    ...(gridApi.grid.getCheckboxRecords?.() ?? []),
  ] as MesMdItemApi.Item[];
  records.forEach((row) => {
    const rowId = row.id;
    if (!rowId) {
      selectedMap.set(rowId as number, row);
    }
  });
  return [...selectedMap.values()];
}
/** å¤„理勾选变化 */
function handleCheckboxSelectChange() {
  selectedRows.value = getMultipleSelectedRows();
}
/** å¤„理单选变化 */
function handleRadioChange(row: MesMdItemApi.Item) {
  selectedRows.value = [row];
}
/** å¤šé€‰æ¨¡å¼ä¸‹åˆ‡æ¢è¡Œå‹¾é€‰ */
async function toggleMultipleRow(row: MesMdItemApi.Item) {
  const selected = gridApi.grid.isCheckedByCheckboxRow(row);
  await gridApi.grid.setCheckboxRow(row, !selected);
  selectedRows.value = getMultipleSelectedRows();
}
/** å¤„理行双击 */
async function handleCellDblclick({ row }: { row: MesMdItemApi.Item }) {
  if (multiple.value) {
    await toggleMultipleRow(row);
    return;
  /** èŽ·å–å½“å‰è¡¨æ ¼æ•°æ® */
  function getTableRows() {
    return gridApi.grid.getTableData().fullData as MesMdItemApi.Item[];
  }
  selectedRows.value = [row];
  await gridApi.grid.setRadioRow(row);
  handleConfirm();
}
/** æŒ‰åˆ†ç±»ç­›é€‰ç‰©æ–™ */
function handleItemTypeNodeClick(row: MesMdItemTypeApi.ItemType | undefined) {
  selectedItemTypeId.value = row?.id;
  gridApi.query();
}
  /** èŽ·å–å¤šé€‰è®°å½•ï¼ŒåŒ…å« VXE reserve è·¨é¡µè®°å½• */
  function getMultipleSelectedRows() {
    const selectedMap = new Map<number, MesMdItemApi.Item>();
    const records = [
      ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
      ...(gridApi.grid.getCheckboxRecords?.() ?? []),
    ] as MesMdItemApi.Item[];
    records.forEach(row => {
      const rowId = row.id;
      if (rowId) {
        selectedMap.set(rowId, row);
      }
    });
    return [...selectedMap.values()];
  }
/** å›žæ˜¾é¢„选物料 */
async function applyPreSelection() {
  if (preSelectedIds.value.length === 0) {
    return;
  }
  const rows = getTableRows();
  for (const row of rows) {
    if (
      row.id === undefined ||
      row.id === null ||
      !preSelectedIds.value.includes(row.id)
    ) {
      continue;
    }
    if (multiple.value) {
      await gridApi.grid.setCheckboxRow(row, true);
    } else {
      await gridApi.grid.setRadioRow(row);
      selectedRows.value = [row];
      return;
    }
  }
  if (multiple.value) {
  /** å¤„理勾选变化 */
  function handleCheckboxSelectChange() {
    selectedRows.value = getMultipleSelectedRows();
  }
}
const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: useItemSelectGridFormSchema(),
  },
  gridOptions: {
    columns: useItemSelectGridColumns(true),
    height: 560,
    keepSource: true,
    checkboxConfig: {
      highlight: true,
      range: true,
      reserve: true,
  /** å¤„理单选变化 */
  function handleRadioChange(row: MesMdItemApi.Item) {
    selectedRows.value = [row];
  }
  /** å¤šé€‰æ¨¡å¼ä¸‹åˆ‡æ¢è¡Œå‹¾é€‰ */
  async function toggleMultipleRow(row: MesMdItemApi.Item) {
    const selected = gridApi.grid.isCheckedByCheckboxRow(row);
    await gridApi.grid.setCheckboxRow(row, !selected);
    selectedRows.value = getMultipleSelectedRows();
  }
  /** å¤„理行双击 */
  async function handleCellDblclick({ row }: { row: MesMdItemApi.Item }) {
    if (multiple.value) {
      await toggleMultipleRow(row);
      return;
    }
    selectedRows.value = [row];
    await gridApi.grid.setRadioRow(row);
    handleConfirm();
  }
  /** æŒ‰åˆ†ç±»ç­›é€‰ç‰©æ–™ */
  function handleItemTypeNodeClick(row: MesMdItemTypeApi.ItemType | undefined) {
    selectedItemTypeId.value = row?.id;
    gridApi.query();
  }
  /** å›žæ˜¾é¢„选物料 */
  async function applyPreSelection() {
    if (preSelectedIds.value.length === 0) {
      return;
    }
    const rows = getTableRows();
    for (const row of rows) {
      if (
        row.id === undefined ||
        row.id === null ||
        !preSelectedIds.value.includes(row.id)
      ) {
        continue;
      }
      if (multiple.value) {
        await gridApi.grid.setCheckboxRow(row, true);
      } else {
        await gridApi.grid.setRadioRow(row);
        selectedRows.value = [row];
        return;
      }
    }
    if (multiple.value) {
      selectedRows.value = getMultipleSelectedRows();
    }
  }
  const [Grid, gridApi] = useVbenVxeGrid({
    formOptions: {
      schema: useItemSelectGridFormSchema(),
    },
    radioConfig: {
      highlight: true,
      trigger: 'row',
    },
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          return await getItemPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            ...formValues,
            itemTypeId: selectedItemTypeId.value,
            status: CommonStatusEnum.ENABLE,
          });
    gridOptions: {
      columns: useItemSelectGridColumns(true),
      height: 560,
      keepSource: true,
      checkboxConfig: {
        highlight: true,
        range: true,
        reserve: true,
      },
      radioConfig: {
        highlight: true,
        trigger: "row",
      },
      proxyConfig: {
        ajax: {
          query: async ({ page }, formValues) => {
            return await getItemPage({
              pageNo: page.currentPage,
              pageSize: page.pageSize,
              ...formValues,
              itemTypeId: selectedItemTypeId.value,
              status: CommonStatusEnum.ENABLE,
            });
          },
        },
      },
      rowConfig: {
        keyField: "id",
        isHover: true,
      },
      toolbarConfig: {
        refresh: true,
        search: true,
      },
    } as VxeTableGridOptions<MesMdItemApi.Item>,
    gridEvents: {
      cellDblclick: handleCellDblclick,
      checkboxAll: handleCheckboxSelectChange,
      checkboxChange: handleCheckboxSelectChange,
      radioChange: ({ row }: { row: MesMdItemApi.Item }) => {
        handleRadioChange(row);
      },
    },
    rowConfig: {
      keyField: 'id',
      isHover: true,
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
  } as VxeTableGridOptions<MesMdItemApi.Item>,
  gridEvents: {
    cellDblclick: handleCellDblclick,
    checkboxAll: handleCheckboxSelectChange,
    checkboxChange: handleCheckboxSelectChange,
    radioChange: ({ row }: { row: MesMdItemApi.Item }) => {
      handleRadioChange(row);
    },
  },
});
/** é‡ç½®æŸ¥è¯¢å’Œé€‰æ‹©çŠ¶æ€ */
async function resetQueryState() {
  selectedItemTypeId.value = undefined;
  selectedRows.value = [];
  typeTreeRef.value?.reset();
  await gridApi.grid.clearCheckboxRow();
  await gridApi.grid.clearCheckboxReserve();
  await gridApi.grid.clearRadioRow();
  await gridApi.formApi.resetForm();
}
/** æ‰“开物料选择弹窗 */
async function openModal(
  selectedIds?: number[],
  options?: { multiple?: boolean },
) {
  open.value = true;
  multiple.value = options?.multiple ?? true;
  preSelectedIds.value = selectedIds || [];
  await nextTick();
  gridApi.setGridOptions({
    columns: useItemSelectGridColumns(multiple.value),
  });
  await resetQueryState();
  await gridApi.query();
  await nextTick();
  await applyPreSelection();
}
/** å…³é—­ç‰©æ–™é€‰æ‹©å¼¹çª— */
async function closeModal() {
  open.value = false;
  await resetQueryState();
}
/** ç¡®è®¤é€‰æ‹©ç‰©æ–™ */
function handleConfirm() {
  const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
  if (rows.length === 0) {
    message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
    return;
  /** é‡ç½®æŸ¥è¯¢å’Œé€‰æ‹©çŠ¶æ€ */
  async function resetQueryState() {
    selectedItemTypeId.value = undefined;
    selectedRows.value = [];
    typeTreeRef.value?.reset();
    await gridApi.grid.clearCheckboxRow();
    await gridApi.grid.clearCheckboxReserve();
    await gridApi.grid.clearRadioRow();
    await gridApi.formApi.resetForm();
  }
  emit('selected', multiple.value ? rows : [rows[0]!]);
  open.value = false;
}
defineExpose({ open: openModal });
  /** æ‰“开物料选择弹窗 */
  async function openModal(
    selectedIds?: number[],
    options?: { multiple?: boolean }
  ) {
    open.value = true;
    multiple.value = options?.multiple ?? true;
    preSelectedIds.value = selectedIds || [];
    await nextTick();
    gridApi.setGridOptions({
      columns: useItemSelectGridColumns(multiple.value),
    });
    await resetQueryState();
    await gridApi.query();
    await nextTick();
    await applyPreSelection();
  }
  /** å…³é—­ç‰©æ–™é€‰æ‹©å¼¹çª— */
  async function closeModal() {
    open.value = false;
    await resetQueryState();
  }
  /** ç¡®è®¤é€‰æ‹©ç‰©æ–™ */
  function handleConfirm() {
    const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
    if (rows.length === 0) {
      message.warning(multiple.value ? "请至少选择一条数据" : "请选择一条数据");
      return;
    }
    emit("selected", multiple.value ? rows : [rows[0]!]);
    open.value = false;
  }
  defineExpose({ open: openModal });
</script>
<template>
  <Modal
    v-model:open="open"
    title="物料产品选择"
    width="80%"
    :destroy-on-close="true"
    @ok="handleConfirm"
    @cancel="closeModal"
  >
  <Modal v-model:open="open"
         title="物料产品选择"
         width="80%"
         :destroy-on-close="true"
         @ok="handleConfirm"
         @cancel="closeModal">
    <div class="flex h-full w-full">
      <Card class="mr-4 h-full w-1/5">
        <MdItemTypeTree
          ref="typeTreeRef"
          @node-click="handleItemTypeNodeClick"
        />
        <MdItemTypeTree ref="typeTreeRef"
                        @node-click="handleItemTypeNodeClick" />
      </Card>
      <div class="w-4/5">
        <Grid table-title="物料产品列表" />
src/views/mes/md/item/index.vue
@@ -1,149 +1,156 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdItemApi } from '#/api/mes/md/item';
import type { MesMdItemTypeApi } from '#/api/mes/md/item/type';
  import type { VxeTableGridOptions } from "#/adapter/vxe-table";
  import type { MesMdItemApi } from "#/api/mes/md/item";
  import type { MesMdItemTypeApi } from "#/api/mes/md/item/type";
import { ref } from 'vue';
  import { ref } from "vue";
import { confirm, Page, useVbenModal } from '../../../../packages/effects/common-ui/src';
import { DICT_TYPE } from '../../../../packages/constants/src';
import { getDictLabel } from '../../../../packages/effects/hooks/src';
import { downloadFileFromBlobPart } from '../../../../packages/utils/src';
  import {
    confirm,
    Page,
    useVbenModal,
  } from "../../../../packages/effects/common-ui/src";
  import { DICT_TYPE } from "../../../../packages/constants/src";
  import { getDictLabel } from "../../../../packages/effects/hooks/src";
  import { downloadFileFromBlobPart } from "../../../../packages/utils/src";
import { Button, Card, message } from 'ant-design-vue';
  import { Button, Card, message } from "ant-design-vue";
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
  deleteItem,
  exportItem,
  getItemPage,
  updateItemStatus,
} from '#/api/mes/md/item';
import { $t } from '#/locales';
import { MdItemTypeTree } from '#/views/mes/md/item/type/components';
import { PrinterLabel } from '#/views/mes/wm/barcode/components';
  import { ACTION_ICON, TableAction, useVbenVxeGrid } from "#/adapter/vxe-table";
  import {
    deleteItem,
    exportItem,
    getItemPage,
    updateItemStatus,
  } from "#/api/mes/md/item";
  import { $t } from "#/locales";
  import { MdItemTypeTree } from "#/views/mes/md/item/type/components";
  import { PrinterLabel } from "#/views/wls/barcode/components";
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
import ImportForm from './modules/import-form.vue';
  import { useGridColumns, useGridFormSchema } from "./data";
  import Form from "./modules/form.vue";
  import ImportForm from "./modules/import-form.vue";
const selectedItemTypeId = ref<number>(); // å½“前选中的物料分类编号
  const selectedItemTypeId = ref<number>(); // å½“前选中的物料分类编号
const [FormModal, formModalApi] = useVbenModal({
  connectedComponent: Form,
  destroyOnClose: true,
});
const [ImportModal, importModalApi] = useVbenModal({
  connectedComponent: ImportForm,
  destroyOnClose: true,
});
/** åˆ·æ–°è¡¨æ ¼ */
function handleRefresh() {
  gridApi.query();
}
/** åˆ›å»ºç‰©æ–™ */
function handleCreate() {
  formModalApi.setData({ formType: 'create' }).open();
}
/** æŸ¥çœ‹ç‰©æ–™ */
function handleDetail(row: MesMdItemApi.Item) {
  formModalApi.setData({ id: row.id, formType: 'detail' }).open();
}
/** ç¼–辑物料 */
function handleEdit(row: MesMdItemApi.Item) {
  formModalApi.setData({ id: row.id, formType: 'update' }).open();
}
/** åˆ é™¤ç‰©æ–™ */
async function handleDelete(row: MesMdItemApi.Item) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting', [row.name]),
    duration: 0,
  const [FormModal, formModalApi] = useVbenModal({
    connectedComponent: Form,
    destroyOnClose: true,
  });
  try {
    await deleteItem(row.id!);
    message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
  const [ImportModal, importModalApi] = useVbenModal({
    connectedComponent: ImportForm,
    destroyOnClose: true,
  });
  /** åˆ·æ–°è¡¨æ ¼ */
  function handleRefresh() {
    gridApi.query();
  }
  /** åˆ›å»ºç‰©æ–™ */
  function handleCreate() {
    formModalApi.setData({ formType: "create" }).open();
  }
  /** æŸ¥çœ‹ç‰©æ–™ */
  function handleDetail(row: MesMdItemApi.Item) {
    formModalApi.setData({ id: row.id, formType: "detail" }).open();
  }
  /** ç¼–辑物料 */
  function handleEdit(row: MesMdItemApi.Item) {
    formModalApi.setData({ id: row.id, formType: "update" }).open();
  }
  /** åˆ é™¤ç‰©æ–™ */
  async function handleDelete(row: MesMdItemApi.Item) {
    const hideLoading = message.loading({
      content: $t("ui.actionMessage.deleting", [row.name]),
      duration: 0,
    });
    try {
      await deleteItem(row.id!);
      message.success($t("ui.actionMessage.deleteSuccess", [row.name]));
      handleRefresh();
    } finally {
      hideLoading();
    }
  }
  /** å¯¼å‡ºç‰©æ–™ */
  async function handleExport() {
    const data = await exportItem(await gridApi.formApi.getValues());
    downloadFileFromBlobPart({ fileName: "物料产品.xls", source: data });
  }
  /** å¯¼å…¥ç‰©æ–™ */
  function handleImport() {
    importModalApi.open();
  }
  /** åˆ†ç±»æ ‘点击 */
  function handleTypeNodeClick(row: MesMdItemTypeApi.ItemType | undefined) {
    selectedItemTypeId.value = row?.id;
    handleRefresh();
  } finally {
    hideLoading();
  }
}
/** å¯¼å‡ºç‰©æ–™ */
async function handleExport() {
  const data = await exportItem(await gridApi.formApi.getValues());
  downloadFileFromBlobPart({ fileName: '物料产品.xls', source: data });
}
/** å¯¼å…¥ç‰©æ–™ */
function handleImport() {
  importModalApi.open();
}
/** åˆ†ç±»æ ‘点击 */
function handleTypeNodeClick(row: MesMdItemTypeApi.ItemType | undefined) {
  selectedItemTypeId.value = row?.id;
  handleRefresh();
}
/** æ›´æ–°ç‰©æ–™çŠ¶æ€ */
async function handleStatusChange(
  newStatus: number,
  row: MesMdItemApi.Item,
): Promise<boolean | undefined> {
  try {
    await confirm(
      `确认要将"${row.name}"物料切换为【${getDictLabel(DICT_TYPE.COMMON_STATUS, newStatus)}】吗?`,
    );
  } catch {
    return false;
  /** æ›´æ–°ç‰©æ–™çŠ¶æ€ */
  async function handleStatusChange(
    newStatus: number,
    row: MesMdItemApi.Item
  ): Promise<boolean | undefined> {
    try {
      await confirm(
        `确认要将"${row.name}"物料切换为【${getDictLabel(
          DICT_TYPE.COMMON_STATUS,
          newStatus
        )}】吗?`
      );
    } catch {
      return false;
    }
    await updateItemStatus(row.id!, newStatus);
    message.success($t("ui.actionMessage.operationSuccess"));
    return true;
  }
  await updateItemStatus(row.id!, newStatus);
  message.success($t('ui.actionMessage.operationSuccess'));
  return true;
}
const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: useGridFormSchema(),
  },
  gridOptions: {
    columns: useGridColumns(handleStatusChange),
    height: 'auto',
    keepSource: true,
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          return await getItemPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            ...formValues,
            itemTypeId: selectedItemTypeId.value,
          });
  const [Grid, gridApi] = useVbenVxeGrid({
    formOptions: {
      schema: useGridFormSchema(),
    },
    gridOptions: {
      columns: useGridColumns(handleStatusChange),
      height: "auto",
      keepSource: true,
      proxyConfig: {
        ajax: {
          query: async ({ page }, formValues) => {
            return await getItemPage({
              pageNo: page.currentPage,
              pageSize: page.pageSize,
              ...formValues,
              itemTypeId: selectedItemTypeId.value,
            });
          },
        },
      },
    },
    rowConfig: {
      keyField: 'id',
      isHover: true,
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
  } as VxeTableGridOptions<MesMdItemApi.Item>,
});
      rowConfig: {
        keyField: "id",
        isHover: true,
      },
      toolbarConfig: {
        refresh: true,
        search: true,
      },
    } as VxeTableGridOptions<MesMdItemApi.Item>,
  });
</script>
<template>
  <Page auto-content-height><FormModal @success="handleRefresh" />
  <Page auto-content-height>
    <FormModal @success="handleRefresh" />
    <ImportModal @success="handleRefresh" />
    <div class="flex h-full w-full">
      <Card class="mr-4 h-full w-1/6">
        <MdItemTypeTree @node-click="handleTypeNodeClick" />
@@ -151,8 +158,7 @@
      <div class="w-5/6">
        <Grid table-title="物料产品列表">
          <template #toolbar-tools>
            <TableAction
              :actions="[
            <TableAction :actions="[
                {
                  label: $t('ui.actionTitle.create', ['物料']),
                  type: 'primary',
@@ -174,18 +180,17 @@
                  auth: ['mes:md-item:export'],
                  onClick: handleExport,
                },
              ]"
            />
              ]" />
          </template>
          <template #code="{ row }">
            <Button type="link" @click="handleDetail(row)">
            <Button type="link"
                    @click="handleDetail(row)">
              {{ row.code }}
            </Button>
          </template>
          <template #actions="{ row }">
            <div class="flex items-center justify-center">
              <TableAction
                :actions="[
              <TableAction :actions="[
                  {
                    label: $t('common.edit'),
                    type: 'link',
@@ -204,13 +209,10 @@
                      confirm: handleDelete.bind(null, row),
                    },
                  },
                ]"
              />
              <PrinterLabel
                :biz-id="row.id"
                :biz-code="row.code"
                biz-type="ITEM"
              />
                ]" />
              <PrinterLabel :biz-id="row.id"
                            :biz-code="row.code"
                            biz-type="ITEM" />
            </div>
          </template>
        </Grid>
src/views/mes/md/item/modules/form.vue
@@ -13,7 +13,7 @@
import { useVbenForm } from '#/adapter/form';
import { createItem, getItem, updateItem } from '#/api/mes/md/item';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
import { BarcodeDetail } from '#/views/wls/barcode/components';
import { useFormSchema } from '../data';
import ItemBatchConfigForm from './item-batch-config-form.vue';
src/views/mes/md/vendor/components/select.vue
@@ -1,13 +1,11 @@
<script lang="ts" setup>
import type { MesMdVendorApi } from '#/api/mes/md/vendor';
import { computed, ref, useAttrs, watch } from 'vue';
import { computed, ref, useAttrs } from 'vue';
import { IconifyIcon } from '../../../../../packages/icons/src';
import { Input, Tooltip } from 'ant-design-vue';
import { getVendor } from '#/api/mes/md/vendor';
import MdVendorSelectDialog from './select-dialog.vue';
@@ -19,24 +17,26 @@
    disabled?: boolean;
    modelValue?: number;
    placeholder?: string;
    vendorName?: string;
  }>(),
  {
    allowClear: true,
    disabled: false,
    modelValue: undefined,
    placeholder: '请选择供应商',
    vendorName: undefined,
  },
);
const emit = defineEmits<{
  change: [item: MesMdVendorApi.Vendor | undefined];
  'update:modelValue': [value: number | undefined];
}>();
const attrs = useAttrs(); // é€ä¼ å±žæ€§
const dialogRef = ref<InstanceType<typeof MdVendorSelectDialog>>(); // ä¾›åº”商选择弹窗
const hovering = ref(false); // æ˜¯å¦æ‚¬åœ
const selectedItem = ref<MesMdVendorApi.Vendor>(); // å½“前选中供应商
const attrs = useAttrs();
const dialogRef = ref<InstanceType<typeof MdVendorSelectDialog>>();
const hovering = ref(false);
const selectedItem = ref<MesMdVendorApi.Vendor>();
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // é€‰æ‹©å™¨å±•示名称
const displayLabel = computed(() => props.vendorName || selectedItem.value?.name || '');
const showClear = computed(
  () =>
    props.allowClear &&
@@ -45,34 +45,12 @@
    props.modelValue !== null,
);
/** æ ¹æ®ä¾›åº”商编号回显选择器 */
async function resolveItemById(id: number | undefined) {
  if (!id) {
    selectedItem.value = undefined;
    return;
  }
  if (selectedItem.value?.id === id) {
    return;
  }
  selectedItem.value = await getVendor(id);
}
watch(
  () => props.modelValue,
  (value) => {
    resolveItemById(value);
  },
  { immediate: true },
);
/** æ¸…空已选供应商 */
function clearSelected() {
  selectedItem.value = undefined;
  emit('update:modelValue', undefined);
  emit('change', undefined);
}
/** æ‰“开供应商选择弹窗 */
function handleClick(event: MouseEvent) {
  if (props.disabled) {
    return;
@@ -89,7 +67,6 @@
  dialogRef.value?.open(selectedIds, { multiple: false });
}
/** å›žå¡«é€‰ä¸­çš„供应商 */
function handleSelected(rows: MesMdVendorApi.Vendor[]) {
  const item = rows[0];
  if (!item) {
src/views/mes/md/workstation/index.vue
@@ -17,7 +17,7 @@
  getWorkstationPage,
} from '#/api/mes/md/workstation';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
import { BarcodeDetail } from '#/views/wls/barcode/components';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
src/views/mes/md/workstation/modules/form.vue
@@ -1,151 +1,154 @@
<script lang="ts" setup>
import type { FormType } from '../data';
  import type { FormType } from "../data";
import type { MesMdWorkstationApi } from '#/api/mes/md/workstation';
  import type { MesMdWorkstationApi } from "#/api/mes/md/workstation";
import { computed, ref } from 'vue';
  import { computed, ref } from "vue";
import { useVbenModal } from '../../../../../packages/effects/common-ui/src';
import { BarcodeBizTypeEnum } from '../../../../../packages/constants/src';
  import { useVbenModal } from "../../../../../packages/effects/common-ui/src";
  import { BarcodeBizTypeEnum } from "../../../../../packages/constants/src";
import { Button, message, Tabs } from 'ant-design-vue';
  import { Button, message, Tabs } from "ant-design-vue";
import { useVbenForm } from '#/adapter/form';
import {
  createWorkstation,
  getWorkstation,
  updateWorkstation,
} from '#/api/mes/md/workstation';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
  import { useVbenForm } from "#/adapter/form";
  import {
    createWorkstation,
    getWorkstation,
    updateWorkstation,
  } from "#/api/mes/md/workstation";
  import { $t } from "#/locales";
  import { BarcodeDetail } from "#/views/wls/barcode/components";
import { useFormSchema } from '../data';
import MachineList from './machine-list.vue';
import ToolList from './tool-list.vue';
import WorkerList from './worker-list.vue';
  import { useFormSchema } from "../data";
  import MachineList from "./machine-list.vue";
  import ToolList from "./tool-list.vue";
  import WorkerList from "./worker-list.vue";
const emit = defineEmits(['success']);
const formType = ref<FormType>('create'); // è¡¨å•模式
const subTabsName = ref('machine'); // å½“前资源页签
const formData = ref<MesMdWorkstationApi.Workstation>();
const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
  const emit = defineEmits(["success"]);
  const formType = ref<FormType>("create"); // è¡¨å•模式
  const subTabsName = ref("machine"); // å½“前资源页签
  const formData = ref<MesMdWorkstationApi.Workstation>();
  const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
const isDetail = computed(() => formType.value === 'detail'); // æ˜¯å¦æŸ¥çœ‹æ¨¡å¼
const getTitle = computed(() => {
  if (formType.value === 'detail') {
    return '查看工作站';
  }
  return formType.value === 'update' ? '修改工作站' : '新增工作站';
});
  const isDetail = computed(() => formType.value === "detail"); // æ˜¯å¦æŸ¥çœ‹æ¨¡å¼
  const getTitle = computed(() => {
    if (formType.value === "detail") {
      return "查看工作站";
    }
    return formType.value === "update" ? "修改工作站" : "新增工作站";
  });
const [Form, formApi] = useVbenForm({
  commonConfig: {
    componentProps: {
      class: 'w-full',
  const [Form, formApi] = useVbenForm({
    commonConfig: {
      componentProps: {
        class: "w-full",
      },
      formItemClass: "col-span-1",
      labelWidth: 110,
    },
    formItemClass: 'col-span-1',
    labelWidth: 110,
  },
  wrapperClass: 'grid-cols-3',
  layout: 'horizontal',
  schema: [],
  showDefaultActions: false,
});
    wrapperClass: "grid-cols-3",
    layout: "horizontal",
    schema: [],
    showDefaultActions: false,
  });
/** æŸ¥çœ‹å·¥ä½œç«™æ¡ç  */
function handleBarcode() {
  if (!formData.value?.id) {
    return;
  /** æŸ¥çœ‹å·¥ä½œç«™æ¡ç  */
  function handleBarcode() {
    if (!formData.value?.id) {
      return;
    }
    barcodeDetailRef.value?.openByBusiness(
      formData.value.id,
      BarcodeBizTypeEnum.WORKSTATION,
      formData.value.code,
      formData.value.name
    );
  }
  barcodeDetailRef.value?.openByBusiness(
    formData.value.id,
    BarcodeBizTypeEnum.WORKSTATION,
    formData.value.code,
    formData.value.name,
  );
}
const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    if (isDetail.value) {
      await modalApi.close();
      return;
    }
    const { valid } = await formApi.validate();
    if (!valid) {
      return;
    }
    modalApi.lock();
    // æäº¤è¡¨å•
    const data = (await formApi.getValues()) as MesMdWorkstationApi.Workstation;
    try {
      if (formType.value === 'create') {
        const id = await createWorkstation(data);
        formData.value = { ...data, id };
        await formApi.setFieldValue('id', id);
        formType.value = 'update';
      } else {
        await updateWorkstation(data);
        formData.value = { ...formData.value, ...data };
  const [Modal, modalApi] = useVbenModal({
    async onConfirm() {
      if (isDetail.value) {
        await modalApi.close();
        return;
      }
      emit('success');
      message.success($t('ui.actionMessage.operationSuccess'));
    } finally {
      modalApi.unlock();
    }
  },
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) {
      formData.value = undefined;
      return;
    }
    subTabsName.value = 'machine';
    // åŠ è½½æ•°æ®
    const data = modalApi.getData<{ formType: FormType; id?: number }>();
    formType.value = data.formType;
    formApi.setState({ schema: useFormSchema(data.formType, formApi) });
    formApi.setDisabled(formType.value === 'detail');
    modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
    if (!data?.id) {
      return;
    }
    modalApi.lock();
    try {
      formData.value = await getWorkstation(data.id);
      // è®¾ç½®åˆ° values
      await formApi.setValues(formData.value);
    } finally {
      modalApi.unlock();
    }
  },
});
      const { valid } = await formApi.validate();
      if (!valid) {
        return;
      }
      modalApi.lock();
      // æäº¤è¡¨å•
      const data = (await formApi.getValues()) as MesMdWorkstationApi.Workstation;
      try {
        if (formType.value === "create") {
          const id = await createWorkstation(data);
          formData.value = { ...data, id };
          await formApi.setFieldValue("id", id);
          formType.value = "update";
        } else {
          await updateWorkstation(data);
          formData.value = { ...formData.value, ...data };
        }
        emit("success");
        message.success($t("ui.actionMessage.operationSuccess"));
      } finally {
        modalApi.unlock();
      }
    },
    async onOpenChange(isOpen: boolean) {
      if (!isOpen) {
        formData.value = undefined;
        return;
      }
      subTabsName.value = "machine";
      // åŠ è½½æ•°æ®
      const data = modalApi.getData<{ formType: FormType; id?: number }>();
      formType.value = data.formType;
      formApi.setState({ schema: useFormSchema(data.formType, formApi) });
      formApi.setDisabled(formType.value === "detail");
      modalApi.setState({ showConfirmButton: formType.value !== "detail" });
      if (!data?.id) {
        return;
      }
      modalApi.lock();
      try {
        formData.value = await getWorkstation(data.id);
        // è®¾ç½®åˆ° values
        await formApi.setValues(formData.value);
      } finally {
        modalApi.unlock();
      }
    },
  });
</script>
<template>
  <Modal :title="getTitle" class="w-4/5">
  <Modal :title="getTitle"
         class="w-4/5">
    <Form class="mx-4" />
    <Tabs
      v-if="formType !== 'create' && formData?.id"
      v-model:active-key="subTabsName"
      class="mx-4 mt-4"
    >
      <Tabs.TabPane key="machine" tab="设备资源">
        <MachineList :form-type="formType" :workstation-id="formData.id" />
    <Tabs v-if="formType !== 'create' && formData?.id"
          v-model:active-key="subTabsName"
          class="mx-4 mt-4">
      <Tabs.TabPane key="machine"
                    tab="设备资源">
        <MachineList :form-type="formType"
                     :workstation-id="formData.id" />
      </Tabs.TabPane>
      <Tabs.TabPane key="tool" tab="工装夹具">
        <ToolList :form-type="formType" :workstation-id="formData.id" />
      <Tabs.TabPane key="tool"
                    tab="工装夹具">
        <ToolList :form-type="formType"
                  :workstation-id="formData.id" />
      </Tabs.TabPane>
      <Tabs.TabPane key="worker" tab="人力资源">
        <WorkerList :form-type="formType" :workstation-id="formData.id" />
      <Tabs.TabPane key="worker"
                    tab="人力资源">
        <WorkerList :form-type="formType"
                    :workstation-id="formData.id" />
      </Tabs.TabPane>
    </Tabs>
    <template #prepend-footer>
      <div class="flex flex-auto items-center">
        <Button
          v-if="isDetail && formData?.id"
          type="primary"
          @click="handleBarcode"
        >
        <Button v-if="isDetail && formData?.id"
                type="primary"
                @click="handleBarcode">
          æŸ¥çœ‹æ¡ç 
        </Button>
      </div>
src/views/mes/md/workstation/workshop/modules/form.vue
@@ -1,119 +1,121 @@
<script lang="ts" setup>
import type { FormType } from '../data';
  import type { FormType } from "../data";
import type { MesMdWorkshopApi } from '#/api/mes/md/workstation/workshop';
  import type { MesMdWorkshopApi } from "#/api/mes/md/workstation/workshop";
import { computed, ref } from 'vue';
  import { computed, ref } from "vue";
import { useVbenModal } from '../../../../../../packages/effects/common-ui/src';
import { BarcodeBizTypeEnum } from '../../../../../../packages/constants/src';
  import { useVbenModal } from "../../../../../../packages/effects/common-ui/src";
  import { BarcodeBizTypeEnum } from "../../../../../../packages/constants/src";
import { Button, message } from 'ant-design-vue';
  import { Button, message } from "ant-design-vue";
import { useVbenForm } from '#/adapter/form';
import {
  createWorkshop,
  getWorkshop,
  updateWorkshop,
} from '#/api/mes/md/workstation/workshop';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
  import { useVbenForm } from "#/adapter/form";
  import {
    createWorkshop,
    getWorkshop,
    updateWorkshop,
  } from "#/api/mes/md/workstation/workshop";
  import { $t } from "#/locales";
  import { BarcodeDetail } from "#/views/wls/barcode/components";
import { useFormSchema } from '../data';
  import { useFormSchema } from "../data";
const emit = defineEmits(['success']);
const formType = ref<FormType>('create'); // è¡¨å•模式
const formData = ref<MesMdWorkshopApi.Workshop>();
const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
  const emit = defineEmits(["success"]);
  const formType = ref<FormType>("create"); // è¡¨å•模式
  const formData = ref<MesMdWorkshopApi.Workshop>();
  const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
const isDetail = computed(() => formType.value === 'detail'); // æ˜¯å¦æŸ¥çœ‹æ¨¡å¼
const getTitle = computed(() => {
  if (formType.value === 'detail') {
    return '查看车间';
  }
  return formType.value === 'update' ? '修改车间' : '新增车间';
});
  const isDetail = computed(() => formType.value === "detail"); // æ˜¯å¦æŸ¥çœ‹æ¨¡å¼
  const getTitle = computed(() => {
    if (formType.value === "detail") {
      return "查看车间";
    }
    return formType.value === "update" ? "修改车间" : "新增车间";
  });
const [Form, formApi] = useVbenForm({
  commonConfig: {
    componentProps: {
      class: 'w-full',
  const [Form, formApi] = useVbenForm({
    commonConfig: {
      componentProps: {
        class: "w-full",
      },
      formItemClass: "col-span-1",
      labelWidth: 100,
    },
    formItemClass: 'col-span-1',
    labelWidth: 100,
  },
  wrapperClass: 'grid-cols-2',
  layout: 'horizontal',
  schema: [],
  showDefaultActions: false,
});
    wrapperClass: "grid-cols-2",
    layout: "horizontal",
    schema: [],
    showDefaultActions: false,
  });
/** æŸ¥çœ‹è½¦é—´æ¡ç  */
function handleBarcode() {
  if (!formData.value?.id) {
    return;
  /** æŸ¥çœ‹è½¦é—´æ¡ç  */
  function handleBarcode() {
    if (!formData.value?.id) {
      return;
    }
    barcodeDetailRef.value?.openByBusiness(
      formData.value.id,
      BarcodeBizTypeEnum.WORKSHOP,
      formData.value.code,
      formData.value.name
    );
  }
  barcodeDetailRef.value?.openByBusiness(
    formData.value.id,
    BarcodeBizTypeEnum.WORKSHOP,
    formData.value.code,
    formData.value.name,
  );
}
const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    if (isDetail.value) {
      await modalApi.close();
      return;
    }
    const { valid } = await formApi.validate();
    if (!valid) {
      return;
    }
    modalApi.lock();
    // æäº¤è¡¨å•
    const data = (await formApi.getValues()) as MesMdWorkshopApi.Workshop;
    try {
      await (formData.value?.id ? updateWorkshop(data) : createWorkshop(data));
      // å…³é—­å¹¶æç¤º
      await modalApi.close();
      emit('success');
      message.success($t('ui.actionMessage.operationSuccess'));
    } finally {
      modalApi.unlock();
    }
  },
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) {
      formData.value = undefined;
      return;
    }
    // åŠ è½½æ•°æ®
    const data = modalApi.getData<{ formType: FormType; id?: number }>();
    formType.value = data.formType;
    formApi.setState({ schema: useFormSchema(data.formType, formApi) });
    formApi.setDisabled(formType.value === 'detail');
    modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
    if (!data?.id) {
      return;
    }
    modalApi.lock();
    try {
      formData.value = await getWorkshop(data.id);
      // è®¾ç½®åˆ° values
      await formApi.setValues(formData.value);
    } finally {
      modalApi.unlock();
    }
  },
});
  const [Modal, modalApi] = useVbenModal({
    async onConfirm() {
      if (isDetail.value) {
        await modalApi.close();
        return;
      }
      const { valid } = await formApi.validate();
      if (!valid) {
        return;
      }
      modalApi.lock();
      // æäº¤è¡¨å•
      const data = (await formApi.getValues()) as MesMdWorkshopApi.Workshop;
      try {
        await (formData.value?.id ? updateWorkshop(data) : createWorkshop(data));
        // å…³é—­å¹¶æç¤º
        await modalApi.close();
        emit("success");
        message.success($t("ui.actionMessage.operationSuccess"));
      } finally {
        modalApi.unlock();
      }
    },
    async onOpenChange(isOpen: boolean) {
      if (!isOpen) {
        formData.value = undefined;
        return;
      }
      // åŠ è½½æ•°æ®
      const data = modalApi.getData<{ formType: FormType; id?: number }>();
      formType.value = data.formType;
      formApi.setState({ schema: useFormSchema(data.formType, formApi) });
      formApi.setDisabled(formType.value === "detail");
      modalApi.setState({ showConfirmButton: formType.value !== "detail" });
      if (!data?.id) {
        return;
      }
      modalApi.lock();
      try {
        formData.value = await getWorkshop(data.id);
        // è®¾ç½®åˆ° values
        await formApi.setValues(formData.value);
      } finally {
        modalApi.unlock();
      }
    },
  });
</script>
<template>
  <Modal :title="getTitle" class="w-1/2">
  <Modal :title="getTitle"
         class="w-1/2">
    <Form class="mx-4" />
    <template v-if="isDetail && formData?.id" #prepend-footer>
    <template v-if="isDetail && formData?.id"
              #prepend-footer>
      <div class="flex flex-auto items-center">
        <Button @click="handleBarcode">查看条码</Button>
      </div>
src/views/mes/pro/card/index.vue
@@ -16,7 +16,7 @@
  getCardPage,
} from '#/api/mes/pro/card';
import { $t } from '#/locales';
import { PrinterLabel } from '#/views/mes/wm/barcode/components';
import { PrinterLabel } from '#/views/wls/barcode/components';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
src/views/mes/pro/card/modules/form.vue
@@ -19,7 +19,7 @@
  updateCard,
} from '#/api/mes/pro/card';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
import { BarcodeDetail } from '#/views/wls/barcode/components';
import { useFormSchema } from '../data';
import ProcessList from './process-list.vue';
src/views/mes/pro/workorder/index.vue
@@ -1,152 +1,151 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
<script lang="ts" setup>
  import type { VxeTableGridOptions } from "#/adapter/vxe-table";
  import type { MesProWorkOrderApi } from "#/api/mes/pro/workorder";
import { ref } from 'vue';
  import { ref } from "vue";
import { Page, useVbenModal } from '../../../../packages/effects/common-ui/src';
import {
  BarcodeBizTypeEnum,
  MesProWorkOrderStatusEnum,
  MesProWorkOrderTypeEnum,
} from '../../../../packages/constants/src';
import { downloadFileFromBlobPart } from '../../../../packages/utils/src';
  import { Page, useVbenModal } from "../../../../packages/effects/common-ui/src";
  import {
    BarcodeBizTypeEnum,
    MesProWorkOrderStatusEnum,
    MesProWorkOrderTypeEnum,
  } from "../../../../packages/constants/src";
  import { downloadFileFromBlobPart } from "../../../../packages/utils/src";
import { Button, message } from 'ant-design-vue';
  import { Button, message } from "ant-design-vue";
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
  cancelWorkOrder,
  deleteWorkOrder,
  exportWorkOrder,
  getWorkOrderPage,
} from '#/api/mes/pro/workorder';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
  import { ACTION_ICON, TableAction, useVbenVxeGrid } from "#/adapter/vxe-table";
  import {
    cancelWorkOrder,
    deleteWorkOrder,
    exportWorkOrder,
    getWorkOrderPage,
  } from "#/api/mes/pro/workorder";
  import { $t } from "#/locales";
  import { BarcodeDetail } from "#/views/wls/barcode/components";
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
  import { useGridColumns, useGridFormSchema } from "./data";
  import Form from "./modules/form.vue";
const [FormModal, formModalApi] = useVbenModal({
  connectedComponent: Form,
  destroyOnClose: true,
});
const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
/** åˆ·æ–°è¡¨æ ¼ */
function handleRefresh() {
  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]),
    duration: 0,
  const [FormModal, formModalApi] = useVbenModal({
    connectedComponent: Form,
    destroyOnClose: true,
  });
  try {
    await deleteWorkOrder(row.id!);
    message.success($t('ui.actionMessage.deleteSuccess', [row.code]));
    handleRefresh();
  } finally {
    hideLoading();
  const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
  /** åˆ·æ–°è¡¨æ ¼ */
  function handleRefresh() {
    gridApi.query();
  }
}
/** å–消订单 */
async function handleCancel(row: MesProWorkOrderApi.WorkOrder) {
  await cancelWorkOrder(row.id!);
  message.success('订单已取消');
  handleRefresh();
}
  /** åˆ›å»ºè®¢å• */
  function handleCreate() {
    formModalApi.setData({ formType: "create" }).open();
  }
/** æŸ¥çœ‹è®¢å•条码 */
function handleBarcode(row: MesProWorkOrderApi.WorkOrder) {
  barcodeDetailRef.value?.openByBusiness(
    row.id!,
    BarcodeBizTypeEnum.WORKORDER,
    row.code,
    row.name,
  );
}
  /** æ–°å¢žå­è®¢å• */
  function handleAddChild(row: MesProWorkOrderApi.WorkOrder) {
    formModalApi.setData({ formType: "create", parentRow: row }).open();
  }
/** å¯¼å‡ºè¡¨æ ¼ */
async function handleExport() {
  const data = await exportWorkOrder(await gridApi.formApi.getValues());
  downloadFileFromBlobPart({ fileName: '生产订单.xls', source: data });
}
  /** æŸ¥çœ‹è®¢å•详情 */
  function handleDetail(row: MesProWorkOrderApi.WorkOrder) {
    formModalApi.setData({ formType: "detail", id: row.id }).open();
  }
const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: useGridFormSchema(),
  },
  gridOptions: {
    columns: useGridColumns(),
    height: 'auto',
    keepSource: true,
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          return await getWorkOrderPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            ...formValues,
          });
  /** ç¼–辑订单 */
  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]),
      duration: 0,
    });
    try {
      await deleteWorkOrder(row.id!);
      message.success($t("ui.actionMessage.deleteSuccess", [row.code]));
      handleRefresh();
    } finally {
      hideLoading();
    }
  }
  /** å–消订单 */
  async function handleCancel(row: MesProWorkOrderApi.WorkOrder) {
    await cancelWorkOrder(row.id!);
    message.success("订单已取消");
    handleRefresh();
  }
  /** æŸ¥çœ‹è®¢å•条码 */
  function handleBarcode(row: MesProWorkOrderApi.WorkOrder) {
    barcodeDetailRef.value?.openByBusiness(
      row.id!,
      BarcodeBizTypeEnum.WORKORDER,
      row.code,
      row.name
    );
  }
  /** å¯¼å‡ºè¡¨æ ¼ */
  async function handleExport() {
    const data = await exportWorkOrder(await gridApi.formApi.getValues());
    downloadFileFromBlobPart({ fileName: "生产订单.xls", source: data });
  }
  const [Grid, gridApi] = useVbenVxeGrid({
    formOptions: {
      schema: useGridFormSchema(),
    },
    gridOptions: {
      columns: useGridColumns(),
      height: "auto",
      keepSource: true,
      proxyConfig: {
        ajax: {
          query: async ({ page }, formValues) => {
            return await getWorkOrderPage({
              pageNo: page.currentPage,
              pageSize: page.pageSize,
              ...formValues,
            });
          },
        },
      },
    },
    rowConfig: {
      keyField: 'id',
      isHover: true,
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
    treeConfig: {
      parentField: 'parentId',
      rowField: 'id',
      transform: true,
      expandAll: true,
      reserve: true,
    },
  } as VxeTableGridOptions<MesProWorkOrderApi.WorkOrder>,
});
      rowConfig: {
        keyField: "id",
        isHover: true,
      },
      toolbarConfig: {
        refresh: true,
        search: true,
      },
      treeConfig: {
        parentField: "parentId",
        rowField: "id",
        transform: true,
        expandAll: true,
        reserve: true,
      },
    } as VxeTableGridOptions<MesProWorkOrderApi.WorkOrder>,
  });
</script>
<template>
  <Page auto-content-height><FormModal @success="handleRefresh" />
  <Page auto-content-height>
    <FormModal @success="handleRefresh" />
    <Grid table-title="生产订单列表">
      <template #toolbar-tools>
        <TableAction
          :actions="[
        <TableAction :actions="[
            {
              label: $t('ui.actionTitle.create', ['订单']),
              type: 'primary',
@@ -161,17 +160,16 @@
              auth: ['mes:pro-work-order:export'],
              onClick: handleExport,
            },
          ]"
        />
          ]" />
      </template>
      <template #code="{ row }">
        <Button type="link" @click="handleDetail(row)">
        <Button type="link"
                @click="handleDetail(row)">
          {{ row.code }}
        </Button>
      </template>
      <template #actions="{ row }">
        <TableAction
          :actions="[
        <TableAction :actions="[
            {
              label: $t('common.edit'),
              type: 'link',
@@ -225,8 +223,7 @@
              auth: ['mes:pro-work-order:query'],
              onClick: handleBarcode.bind(null, row),
            },
          ]"
        />
          ]" />
      </template>
    </Grid>
    <BarcodeDetail ref="barcodeDetailRef" />
src/views/mes/pro/workorder/modules/form.vue
@@ -1,292 +1,293 @@
<script lang="ts" setup>
import type { FormType } from '../data';
<script lang="ts" setup>
  import type { FormType } from "../data";
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
import type { MesProWorkOrderBomApi } from '#/api/mes/pro/workorder/bom';
  import type { MesProWorkOrderApi } from "#/api/mes/pro/workorder";
  import type { MesProWorkOrderBomApi } from "#/api/mes/pro/workorder/bom";
import { computed, ref } from 'vue';
  import { computed, ref } from "vue";
import { useVbenModal } from '../../../../../packages/effects/common-ui/src';
import { BarcodeBizTypeEnum, MesProWorkOrderStatusEnum } from '../../../../../packages/constants/src';
  import { useVbenModal } from "../../../../../packages/effects/common-ui/src";
  import {
    BarcodeBizTypeEnum,
    MesProWorkOrderStatusEnum,
  } from "../../../../../packages/constants/src";
import { Button, message, Popconfirm, Tabs } from 'ant-design-vue';
  import { Button, message, Popconfirm, Tabs } from "ant-design-vue";
import { useVbenForm } from '#/adapter/form';
import {
  confirmWorkOrder,
  createWorkOrder,
  finishWorkOrder,
  getWorkOrder,
  updateWorkOrder,
} from '#/api/mes/pro/workorder';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
  import { useVbenForm } from "#/adapter/form";
  import {
    confirmWorkOrder,
    createWorkOrder,
    finishWorkOrder,
    getWorkOrder,
    updateWorkOrder,
  } from "#/api/mes/pro/workorder";
  import { $t } from "#/locales";
  import { BarcodeDetail } from "#/views/wls/barcode/components";
import { useFormSchema } from '../data';
import BomList from './bom-list.vue';
import ItemList from './item-list.vue';
  import { useFormSchema } from "../data";
  import BomList from "./bom-list.vue";
  import ItemList from "./item-list.vue";
const emit = defineEmits(['success']);
const formType = ref<FormType>('create');
const formData = ref<MesProWorkOrderApi.WorkOrder>();
const originalSnapshot = ref(''); // è¡¨å•原始数据快照,用于确认时跳过未变更的保存请求
const subTabsName = ref('bom'); // å½“前选中的子表 Tab
const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
  const emit = defineEmits(["success"]);
  const formType = ref<FormType>("create");
  const formData = ref<MesProWorkOrderApi.WorkOrder>();
  const originalSnapshot = ref(""); // è¡¨å•原始数据快照,用于确认时跳过未变更的保存请求
  const subTabsName = ref("bom"); // å½“前选中的子表 Tab
  const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
const isEditable = computed(() =>
  // æ˜¯å¦ä¸ºç¼–辑模式(可保存)
  ['create', 'update'].includes(formType.value),
);
const isConfirm = computed(() => formType.value === 'confirm'); // æ˜¯å¦ä¸ºç¡®è®¤æ¨¡å¼
const isFinish = computed(() => formType.value === 'finish'); // æ˜¯å¦ä¸ºå®Œæˆæ¨¡å¼
const canConfirm = computed(
  () =>
    // ç¼–辑态草稿可确认
    formType.value === 'update' &&
    formData.value?.status === MesProWorkOrderStatusEnum.PREPARE,
);
const getTitle = computed(() => {
  const isChild = !!formData.value?.parentId;
  switch (formType.value) {
    case 'confirm': {
      return '确认订单';
    }
    case 'detail': {
      return $t('ui.actionTitle.view', ['订单']);
    }
    case 'finish': {
      return '完成订单';
    }
    case 'update': {
      return isChild ? '编辑子订单' : $t('ui.actionTitle.edit', ['订单']);
    }
    default: {
      return isChild ? '新增子订单' : $t('ui.actionTitle.create', ['订单']);
    }
  }
});
const [Form, formApi] = useVbenForm({
  commonConfig: {
    componentProps: {
      class: 'w-full',
    },
    formItemClass: 'col-span-1',
    labelWidth: 110,
  },
  layout: 'horizontal',
  schema: [],
  showDefaultActions: false,
  wrapperClass: 'grid-cols-3',
});
/** é‡æ–°æŒ‚è½½ schema å¹¶æŒ‰è¡¨å•态控制底部确认按钮 */
function applySchema() {
  formApi.setState({ schema: useFormSchema(formType.value, formApi) });
  modalApi.setState({ showConfirmButton: isEditable.value });
}
/** æŸ¥çœ‹è®¢å•条码 */
function handleBarcode() {
  if (!formData.value?.id) {
    return;
  }
  barcodeDetailRef.value?.openByBusiness(
    formData.value.id,
    BarcodeBizTypeEnum.WORKORDER,
    formData.value.code,
    formData.value.name,
  const isEditable = computed(() =>
    // æ˜¯å¦ä¸ºç¼–辑模式(可保存)
    ["create", "update"].includes(formType.value)
  );
}
/** ç¡®è®¤è®¢å•:编辑态有修改时先保存,再确认 */
async function handleConfirm() {
  const { valid } = await formApi.validate();
  if (!valid || !formData.value?.id) {
    return;
  }
  modalApi.lock();
  try {
    if (isEditable.value) {
      const current = JSON.stringify(await formApi.getValues());
      if (current !== originalSnapshot.value) {
        const data =
          (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder;
        await updateWorkOrder({ ...formData.value, ...data });
        originalSnapshot.value = current;
  const isConfirm = computed(() => formType.value === "confirm"); // æ˜¯å¦ä¸ºç¡®è®¤æ¨¡å¼
  const isFinish = computed(() => formType.value === "finish"); // æ˜¯å¦ä¸ºå®Œæˆæ¨¡å¼
  const canConfirm = computed(
    () =>
      // ç¼–辑态草稿可确认
      formType.value === "update" &&
      formData.value?.status === MesProWorkOrderStatusEnum.PREPARE
  );
  const getTitle = computed(() => {
    const isChild = !!formData.value?.parentId;
    switch (formType.value) {
      case "confirm": {
        return "确认订单";
      }
      case "detail": {
        return $t("ui.actionTitle.view", ["订单"]);
      }
      case "finish": {
        return "完成订单";
      }
      case "update": {
        return isChild ? "编辑子订单" : $t("ui.actionTitle.edit", ["订单"]);
      }
      default: {
        return isChild ? "新增子订单" : $t("ui.actionTitle.create", ["订单"]);
      }
    }
    await confirmWorkOrder(formData.value.id);
    message.success('订单已确认');
    await modalApi.close();
    emit('success');
  } finally {
    modalApi.unlock();
  }
}
/** å®Œæˆè®¢å• */
async function handleFinish() {
  if (!formData.value?.id) {
    return;
  }
  modalApi.lock();
  try {
    await finishWorkOrder(formData.value.id);
    message.success('订单已完成');
    await modalApi.close();
    emit('success');
  } finally {
    modalApi.unlock();
  }
}
/** ä»Ž BOM ç‰©æ–™è¡Œé¢„填子订单:复用当前订单上下文,进入新增态 */
async function handleGenerateWorkOrder(
  bomRow: MesProWorkOrderBomApi.WorkOrderBom,
) {
  const current = formData.value;
  if (!current) {
    return;
  }
  formData.value = undefined;
  formType.value = 'create';
  subTabsName.value = 'bom';
  applySchema();
  await formApi.resetForm();
  await formApi.setValues({
    clientId: current.clientId,
    name: `${bomRow.itemName}【${bomRow.quantity}】${bomRow.unitMeasureName || ''}`,
    orderSourceCode: current.orderSourceCode,
    orderSourceType: current.orderSourceType,
    parentId: current.id,
    productId: bomRow.itemId,
    quantity: bomRow.quantity,
    requestDate: current.requestDate,
    type: current.type,
    vendorId: current.vendorId,
  });
  originalSnapshot.value = JSON.stringify(await formApi.getValues());
  message.info('已从 BOM ç‰©æ–™é¢„填子订单,请补充订单编码等信息后保存');
}
const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    if (!isEditable.value) {
      await modalApi.close();
  const [Form, formApi] = useVbenForm({
    commonConfig: {
      componentProps: {
        class: "w-full",
      },
      formItemClass: "col-span-1",
      labelWidth: 110,
    },
    layout: "horizontal",
    schema: [],
    showDefaultActions: false,
    wrapperClass: "grid-cols-3",
  });
  /** é‡æ–°æŒ‚è½½ schema å¹¶æŒ‰è¡¨å•态控制底部确认按钮 */
  function applySchema() {
    formApi.setState({ schema: useFormSchema(formType.value, formApi) });
    modalApi.setState({ showConfirmButton: isEditable.value });
  }
  /** æŸ¥çœ‹è®¢å•条码 */
  function handleBarcode() {
    if (!formData.value?.id) {
      return;
    }
    barcodeDetailRef.value?.openByBusiness(
      formData.value.id,
      BarcodeBizTypeEnum.WORKORDER,
      formData.value.code,
      formData.value.name
    );
  }
  /** ç¡®è®¤è®¢å•:编辑态有修改时先保存,再确认 */
  async function handleConfirm() {
    const { valid } = await formApi.validate();
    if (!valid) {
    if (!valid || !formData.value?.id) {
      return;
    }
    modalApi.lock();
    // æäº¤è¡¨å•
    const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder;
    try {
      if (formData.value?.id) {
        await updateWorkOrder({ ...formData.value, ...data });
        formData.value = { ...formData.value, ...data };
      } else {
        const id = await createWorkOrder(data);
        formData.value = {
          ...data,
          id,
          status: MesProWorkOrderStatusEnum.PREPARE,
        };
        formType.value = 'update';
        // åˆ›å»ºæˆåŠŸåŽåˆ‡æ¢ç¼–è¾‘æ€ï¼Œé‡æŒ‚ schema è®©ã€Œè®¢å•状态」字段显现
        applySchema();
        await formApi.setValues(formData.value);
      if (isEditable.value) {
        const current = JSON.stringify(await formApi.getValues());
        if (current !== originalSnapshot.value) {
          const data =
            (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder;
          await updateWorkOrder({ ...formData.value, ...data });
          originalSnapshot.value = current;
        }
      }
      originalSnapshot.value = JSON.stringify(await formApi.getValues());
      emit('success');
      message.success($t('ui.actionMessage.operationSuccess'));
      await confirmWorkOrder(formData.value.id);
      message.success("订单已确认");
      await modalApi.close();
      emit("success");
    } finally {
      modalApi.unlock();
    }
  },
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) {
      formData.value = undefined;
      originalSnapshot.value = '';
  }
  /** å®Œæˆè®¢å• */
  async function handleFinish() {
    if (!formData.value?.id) {
      return;
    }
    // åŠ è½½æ•°æ®
    const data = modalApi.getData<{
      formType: FormType;
      id?: number;
      parentRow?: MesProWorkOrderApi.WorkOrder;
    }>();
    formType.value = data.formType;
    subTabsName.value = 'bom';
    modalApi.lock();
    try {
      await finishWorkOrder(formData.value.id);
      message.success("订单已完成");
      await modalApi.close();
      emit("success");
    } finally {
      modalApi.unlock();
    }
  }
  /** ä»Ž BOM ç‰©æ–™è¡Œé¢„填子订单:复用当前订单上下文,进入新增态 */
  async function handleGenerateWorkOrder(
    bomRow: MesProWorkOrderBomApi.WorkOrderBom
  ) {
    const current = formData.value;
    if (!current) {
      return;
    }
    formData.value = undefined;
    formType.value = "create";
    subTabsName.value = "bom";
    applySchema();
    if (data?.id) {
    await formApi.resetForm();
    await formApi.setValues({
      clientId: current.clientId,
      name: `${bomRow.itemName}【${bomRow.quantity}】${
        bomRow.unitMeasureName || ""
      }`,
      orderSourceCode: current.orderSourceCode,
      orderSourceType: current.orderSourceType,
      parentId: current.id,
      productId: bomRow.itemId,
      quantity: bomRow.quantity,
      requestDate: current.requestDate,
      type: current.type,
      vendorId: current.vendorId,
    });
    originalSnapshot.value = JSON.stringify(await formApi.getValues());
    message.info("已从 BOM ç‰©æ–™é¢„填子订单,请补充订单编码等信息后保存");
  }
  const [Modal, modalApi] = useVbenModal({
    async onConfirm() {
      if (!isEditable.value) {
        await modalApi.close();
        return;
      }
      const { valid } = await formApi.validate();
      if (!valid) {
        return;
      }
      modalApi.lock();
      // æäº¤è¡¨å•
      const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder;
      try {
        formData.value = await getWorkOrder(data.id);
        // è®¾ç½®åˆ° values
        await formApi.setValues(formData.value);
        if (formData.value?.id) {
          await updateWorkOrder({ ...formData.value, ...data });
          formData.value = { ...formData.value, ...data };
        } else {
          const id = await createWorkOrder(data);
          formData.value = {
            ...data,
            id,
            status: MesProWorkOrderStatusEnum.PREPARE,
          };
          formType.value = "update";
          // åˆ›å»ºæˆåŠŸåŽåˆ‡æ¢ç¼–è¾‘æ€ï¼Œé‡æŒ‚ schema è®©ã€Œè®¢å•状态」字段显现
          applySchema();
          await formApi.setValues(formData.value);
        }
        originalSnapshot.value = JSON.stringify(await formApi.getValues());
        emit("success");
        message.success($t("ui.actionMessage.operationSuccess"));
      } finally {
        modalApi.unlock();
      }
    } else if (data?.parentRow) {
      // æ–°å¢žå­è®¢å•时,预填父订单信息
      const parent = data.parentRow;
      await formApi.setValues({
        clientId: parent.clientId,
        orderSourceCode: parent.orderSourceCode,
        orderSourceType: parent.orderSourceType,
        parentId: parent.id,
        requestDate: parent.requestDate,
        type: parent.type,
        vendorId: parent.vendorId,
      });
    }
    originalSnapshot.value = JSON.stringify(await formApi.getValues());
  },
});
    },
    async onOpenChange(isOpen: boolean) {
      if (!isOpen) {
        formData.value = undefined;
        originalSnapshot.value = "";
        return;
      }
      // åŠ è½½æ•°æ®
      const data = modalApi.getData<{
        formType: FormType;
        id?: number;
        parentRow?: MesProWorkOrderApi.WorkOrder;
      }>();
      formType.value = data.formType;
      subTabsName.value = "bom";
      applySchema();
      if (data?.id) {
        modalApi.lock();
        try {
          formData.value = await getWorkOrder(data.id);
          // è®¾ç½®åˆ° values
          await formApi.setValues(formData.value);
        } finally {
          modalApi.unlock();
        }
      } else if (data?.parentRow) {
        // æ–°å¢žå­è®¢å•时,预填父订单信息
        const parent = data.parentRow;
        await formApi.setValues({
          clientId: parent.clientId,
          orderSourceCode: parent.orderSourceCode,
          orderSourceType: parent.orderSourceType,
          parentId: parent.id,
          requestDate: parent.requestDate,
          type: parent.type,
          vendorId: parent.vendorId,
        });
      }
      originalSnapshot.value = JSON.stringify(await formApi.getValues());
    },
  });
</script>
<template>
  <Modal :title="getTitle" class="w-3/5">
  <Modal :title="getTitle"
         class="w-3/5">
    <Form class="mx-4" />
    <!-- BOM / ç‰©æ–™éœ€æ±‚ Tab:非新建态展示 -->
    <template v-if="formData?.id">
      <Tabs v-model:active-key="subTabsName" class="mx-4">
        <Tabs.TabPane key="bom" tab="订单 BOM">
          <BomList
            :form-type="formType"
            :work-order="formData"
            :work-order-id="formData.id"
            @generate-work-order="handleGenerateWorkOrder"
          />
      <Tabs v-model:active-key="subTabsName"
            class="mx-4">
        <Tabs.TabPane key="bom"
                      tab="订单 BOM">
          <BomList :form-type="formType"
                   :work-order="formData"
                   :work-order-id="formData.id"
                   @generate-work-order="handleGenerateWorkOrder" />
        </Tabs.TabPane>
        <Tabs.TabPane key="item" tab="物料需求">
        <Tabs.TabPane key="item"
                      tab="物料需求">
          <ItemList :work-order-id="formData.id" />
        </Tabs.TabPane>
      </Tabs>
    </template>
    <template #prepend-footer>
      <div class="flex flex-auto items-center gap-2">
        <Popconfirm
          v-if="canConfirm || isConfirm"
          title="确认要完成订单编制吗?确认后将不能更改。"
          @confirm="handleConfirm"
        >
        <Popconfirm v-if="canConfirm || isConfirm"
                    title="确认要完成订单编制吗?确认后将不能更改。"
                    @confirm="handleConfirm">
          <Button type="primary">确认</Button>
        </Popconfirm>
        <Popconfirm
          v-if="isFinish"
          title="确认要完成该订单吗?"
          @confirm="handleFinish"
        >
        <Popconfirm v-if="isFinish"
                    title="确认要完成该订单吗?"
                    @confirm="handleFinish">
          <Button type="primary">完成</Button>
        </Popconfirm>
        <Button
          v-if="formType === 'detail' && formData?.id"
          @click="handleBarcode"
        >
        <Button v-if="formType === 'detail' && formData?.id"
                @click="handleBarcode">
          æŸ¥çœ‹æ¡ç 
        </Button>
      </div>
src/views/mes/process-design/parameter/data.ts
@@ -1,113 +1,96 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesPdParameterApi } from '#/api/mes/pro/processdesign/parameter';
import type { VbenFormSchema } from "#/adapter/form";
import type { VxeTableGridOptions } from "#/adapter/vxe-table";
import type { MesPdParameterApi } from "#/api/mes/pro/processdesign/parameter";
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";
import { z } from '#/adapter/form';
/** å‚数类型 - ä¸‹æ‹‰é€‰é¡¹çš„值 */
const PARAM_TYPE_SELECT = 3;
import { z } from "#/adapter/form";
import { getUnitPage } from "#/api/mdm/unit";
/** è¡¨å•类型 */
export type FormType = 'create' | 'detail' | 'update';
export type FormType = "create" | "detail" | "update";
/** æ–°å¢ž/修改工艺参数的表单 */
export function useFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'id',
      component: 'Input',
      fieldName: "id",
      component: "Input",
      dependencies: {
        triggerFields: [''],
        triggerFields: [""],
        show: () => false,
      },
    },
    {
      fieldName: 'code',
      label: '参数编码',
      component: 'Input',
      fieldName: "paramCode",
      label: "参数编码",
      component: "Input",
      componentProps: {
        maxLength: 64,
        placeholder: '请输入参数编码',
        placeholder: "参数编码自动生成",
        disabled: true,
      },
      rules: z.string().min(1, '参数编码不能为空').max(64),
    },
    {
      fieldName: 'name',
      label: '参数名称',
      component: 'Input',
      fieldName: "paramName",
      label: "参数名称",
      component: "Input",
      componentProps: {
        maxLength: 100,
        placeholder: '请输入参数名称',
        placeholder: "请输入参数名称",
      },
      rules: z.string().min(1, '参数名称不能为空').max(100),
      rules: z.string().min(1, "参数名称不能为空").max(100),
    },
    {
      fieldName: 'type',
      label: '参数类型',
      component: 'Select',
      fieldName: "paramType",
      label: "参数类型",
      component: "Select",
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_PD_PARAM_TYPE, 'number'),
        placeholder: '请选择参数类型',
        options: getDictOptions(DICT_TYPE.MES_PD_PARAM_TYPE, "number"),
        placeholder: "请选择参数类型",
      },
      rules: 'selectRequired',
      rules: "selectRequired",
    },
    {
      fieldName: 'unit',
      label: '单位',
      component: 'Input',
      fieldName: "unitMeasureId",
      label: "计量单位",
      component: "ApiSelect",
      componentProps: {
        maxLength: 20,
        placeholder: '请输入单位',
        allowClear: true,
        api: async () => {
          const result = await getUnitPage({ pageNo: 1, pageSize: 100 });
          return result.list.map(item => ({
            label: item.name || "",
            value: item.id || 0,
          }));
        },
        labelField: "label",
        valueField: "value",
        placeholder: "请选择计量单位",
        showSearch: true,
        filterOption: true,
      },
    },
    {
      fieldName: 'requiredFlag',
      label: '是否必填',
      component: 'Switch',
      fieldName: "required",
      label: "是否必填",
      component: "Switch",
      componentProps: {
        checkedChildren: '是',
        unCheckedChildren: '否',
        checkedChildren: "是",
        unCheckedChildren: "否",
      },
      rules: z.boolean().default(false),
    },
    {
      fieldName: 'dictType',
      label: '字典类型',
      component: 'Input',
      componentProps: {
        maxLength: 64,
        placeholder: '请输入字典类型(下拉选项时配置)',
      },
      dependencies: {
        triggerFields: ['type'],
        show: (values) => values?.type === PARAM_TYPE_SELECT,
      },
    },
    {
      fieldName: 'apiUrl',
      label: '接口地址',
      component: 'Input',
      fieldName: "remark",
      label: "备注",
      component: "Textarea",
      formItemClass: "col-span-2",
      componentProps: {
        maxLength: 250,
        placeholder: '请输入接口地址(下拉选项时配置)',
      },
      dependencies: {
        triggerFields: ['type'],
        show: (values) => values?.type === PARAM_TYPE_SELECT,
      },
    },
    {
      fieldName: 'remark',
      label: '备注',
      component: 'Textarea',
      formItemClass: 'col-span-2',
      componentProps: {
        maxLength: 250,
        placeholder: '请输入备注',
        placeholder: "请输入备注",
        rows: 3,
      },
    },
@@ -118,79 +101,79 @@
export function useGridFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'code',
      label: '参数编码',
      component: 'Input',
      fieldName: "paramCode",
      label: "参数编码",
      component: "Input",
      componentProps: {
        allowClear: true,
        placeholder: '请输入参数编码',
        placeholder: "请输入参数编码",
      },
    },
    {
      fieldName: 'name',
      label: '参数名称',
      component: 'Input',
      fieldName: "paramName",
      label: "参数名称",
      component: "Input",
      componentProps: {
        allowClear: true,
        placeholder: '请输入参数名称',
        placeholder: "请输入参数名称",
      },
    },
    {
      fieldName: 'type',
      label: '参数类型',
      component: 'Select',
      fieldName: "paramType",
      label: "参数类型",
      component: "Select",
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_PD_PARAM_TYPE, 'number'),
        placeholder: '请选择参数类型',
        options: getDictOptions(DICT_TYPE.MES_PD_PARAM_TYPE, "number"),
        placeholder: "请选择参数类型",
      },
    },
  ];
}
/** åˆ—表的字段 */
export function useGridColumns(): VxeTableGridOptions<MesPdParameterApi.Parameter>['columns'] {
export function useGridColumns(): VxeTableGridOptions<MesPdParameterApi.Parameter>["columns"] {
  return [
    {
      type: 'seq',
      title: '序号',
      type: "seq",
      title: "序号",
      width: 60,
      align: 'center',
      align: "center",
    },
    { field: 'code', title: '参数编码', minWidth: 150 },
    { field: 'name', title: '参数名称', minWidth: 180 },
    { field: "paramCode", title: "参数编码", minWidth: 150 },
    { field: "paramName", title: "参数名称", minWidth: 180 },
    {
      field: 'type',
      title: '参数类型',
      field: "paramType",
      title: "参数类型",
      width: 120,
      cellRender: {
        name: 'CellDict',
        name: "CellDict",
        props: { type: DICT_TYPE.MES_PD_PARAM_TYPE },
      },
    },
    { field: 'unit', title: '单位', width: 80 },
    { field: "unitMeasureName", title: "单位", width: 80 },
    {
      field: 'requiredFlag',
      title: '是否必填',
      field: "required",
      title: "是否必填",
      width: 90,
      cellRender: {
        name: 'CellDict',
        name: "CellDict",
        props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
      },
    },
    { field: 'remark', title: '备注', minWidth: 180 },
    { field: "remark", title: "备注", minWidth: 180 },
    {
      field: 'createTime',
      title: '创建时间',
      field: "createTime",
      title: "创建时间",
      width: 180,
      formatter: 'formatDateTime',
      formatter: "formatDateTime",
    },
    {
      title: '操作',
      title: "操作",
      width: 150,
      fixed: 'right',
      fixed: "right",
      slots: {
        default: 'actions',
        default: "actions",
      },
    },
  ];
src/views/mes/process-design/parameter/index.vue
@@ -10,8 +10,10 @@
import {
  deleteParameter,
  getParameterPage,
  exportParameterExcel,
} from '#/api/mes/pro/processdesign/parameter';
import { $t } from '#/locales';
import { downloadFileFromBlobPart } from '../../../../packages/utils/src';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
@@ -44,16 +46,23 @@
/** åˆ é™¤å·¥è‰ºå‚æ•° */
async function handleDelete(row: MesPdParameterApi.Parameter) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting', [row.name]),
    content: $t('ui.actionMessage.deleting', [row.paramName]),
    duration: 0,
  });
  try {
    await deleteParameter(row.id!);
    message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
    message.success($t('ui.actionMessage.deleteSuccess', [row.paramName]));
    handleRefresh();
  } finally {
    hideLoading();
  }
}
/** å¯¼å‡ºå·¥è‰ºå‚æ•° */
async function handleExport() {
  const formValues = await gridApi.formApi.getValues();
  const data = await exportParameterExcel(formValues);
  downloadFileFromBlobPart({ fileName: '工艺参数.xlsx', source: data });
}
const [Grid, gridApi] = useVbenVxeGrid({
@@ -100,11 +109,18 @@
              auth: ['mes:pd:parameter:create'],
              onClick: handleCreate,
            },
            {
              label: $t('ui.actionTitle.export'),
              type: 'primary',
              icon: ACTION_ICON.DOWNLOAD,
              auth: ['mes:pd:parameter:export'],
              onClick: handleExport,
            },
          ]"
        />
      </template>
      <template #code="{ row }">
        <Button type="link" @click="handleDetail(row)">{{ row.code }}</Button>
      <template #paramCode="{ row }">
        <Button type="link" @click="handleDetail(row)">{{ row.paramCode }}</Button>
      </template>
      <template #actions="{ row }">
        <TableAction
@@ -123,7 +139,7 @@
              icon: ACTION_ICON.DELETE,
              auth: ['mes:pd:parameter:delete'],
              popConfirm: {
                title: $t('ui.actionMessage.deleteConfirm', [row.name]),
                title: $t('ui.actionMessage.deleteConfirm', [row.paramName]),
                confirm: handleDelete.bind(null, row),
              },
            },
src/views/mes/process-design/template/data.ts
@@ -1,64 +1,65 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesPdTemplateApi } from '#/api/mes/pro/processdesign/template';
import type { VbenFormSchema } from "#/adapter/form";
import type { VxeTableGridOptions } from "#/adapter/vxe-table";
import type { MesPdTemplateApi } from "#/api/mes/pro/processdesign/template";
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";
import { z } from '#/adapter/form';
import { z } from "#/adapter/form";
/** è¡¨å•类型 */
export type FormType = 'create' | 'detail' | 'update';
export type FormType = "create" | "detail" | "update";
/** æ–°å¢ž/修改工艺参数模板的表单 */
export function useFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'id',
      component: 'Input',
      fieldName: "id",
      component: "Input",
      dependencies: {
        triggerFields: [''],
        triggerFields: [""],
        show: () => false,
      },
    },
    {
      fieldName: 'name',
      label: '模板名称',
      component: 'Input',
      fieldName: "templateCode",
      label: "模板编码",
      component: "Input",
      componentProps: {
        maxLength: 64,
        placeholder: "模板编码自动生成",
        disabled: true,
      },
    },
    {
      fieldName: "templateName",
      label: "模板名称",
      component: "Input",
      componentProps: {
        maxLength: 100,
        placeholder: '请输入模板名称',
        placeholder: "请输入模板名称",
      },
      rules: z.string().min(1, '模板名称不能为空').max(100),
      rules: z.string().min(1, "模板名称不能为空").max(100),
    },
    {
      fieldName: 'type',
      label: '模板类型',
      component: 'Select',
      fieldName: "templateType",
      label: "模板类型",
      component: "Select",
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_PD_TEMPLATE_TYPE, 'number'),
        placeholder: '请选择模板类型',
        options: getDictOptions(DICT_TYPE.MES_PD_TEMPLATE_TYPE, "number"),
        placeholder: "请选择模板类型",
      },
      rules: 'selectRequired',
      rules: "selectRequired",
    },
    {
      fieldName: 'parameterIds',
      label: '关联参数',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'remark',
      label: '备注',
      component: 'Textarea',
      formItemClass: 'col-span-2',
      fieldName: "remark",
      label: "备注",
      component: "Textarea",
      formItemClass: "col-span-2",
      componentProps: {
        maxLength: 250,
        placeholder: '请输入备注',
        placeholder: "请输入备注",
        rows: 3,
      },
    },
@@ -69,59 +70,69 @@
export function useGridFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'name',
      label: '模板名称',
      component: 'Input',
      fieldName: "templateCode",
      label: "模板编码",
      component: "Input",
      componentProps: {
        allowClear: true,
        placeholder: '请输入模板名称',
        placeholder: "请输入模板编码",
      },
    },
    {
      fieldName: 'type',
      label: '模板类型',
      component: 'Select',
      fieldName: "templateName",
      label: "模板名称",
      component: "Input",
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_PD_TEMPLATE_TYPE, 'number'),
        placeholder: '请选择模板类型',
        placeholder: "请输入模板名称",
      },
    },
    {
      fieldName: "templateType",
      label: "模板类型",
      component: "Select",
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_PD_TEMPLATE_TYPE, "number"),
        placeholder: "请选择模板类型",
      },
    },
  ];
}
/** åˆ—表的字段 */
export function useGridColumns(): VxeTableGridOptions<MesPdTemplateApi.Template>['columns'] {
export function useGridColumns(): VxeTableGridOptions<MesPdTemplateApi.Template>["columns"] {
  return [
    {
      type: 'seq',
      title: '序号',
      type: "seq",
      title: "序号",
      width: 60,
      align: 'center',
      align: "center",
    },
    { field: 'name', title: '模板名称', minWidth: 180 },
    { field: "templateCode", title: "模板编码", minWidth: 150 },
    { field: "templateName", title: "模板名称", minWidth: 180 },
    {
      field: 'type',
      title: '模板类型',
      field: "templateType",
      title: "模板类型",
      width: 120,
      cellRender: {
        name: 'CellDict',
        name: "CellDict",
        props: { type: DICT_TYPE.MES_PD_TEMPLATE_TYPE },
      },
    },
    { field: 'createUser', title: '创建人', width: 120 },
    { field: "remark", title: "备注", minWidth: 180 },
    {
      field: 'createTime',
      title: '创建时间',
      field: "createTime",
      title: "创建时间",
      width: 180,
      formatter: 'formatDateTime',
      formatter: "formatDateTime",
    },
    {
      title: '操作',
      width: 150,
      fixed: 'right',
      title: "操作",
      width: 240,
      fixed: "right",
      slots: {
        default: 'actions',
        default: "actions",
      },
    },
  ];
src/views/mes/process-design/template/index.vue
@@ -1,98 +1,107 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesPdTemplateApi } from '#/api/mes/pro/processdesign/template';
  import type { VxeTableGridOptions } from "#/adapter/vxe-table";
  import type { MesPdTemplateApi } from "#/api/mes/pro/processdesign/template";
import { Page, useVbenModal } from '../../../../packages/effects/common-ui/src';
  import { Page, useVbenModal } from "../../../../packages/effects/common-ui/src";
import { Button, message } from 'ant-design-vue';
  import { Button, message } from "ant-design-vue";
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
  deleteTemplate,
  getTemplatePage,
} from '#/api/mes/pro/processdesign/template';
import { $t } from '#/locales';
  import { ACTION_ICON, TableAction, useVbenVxeGrid } from "#/adapter/vxe-table";
  import {
    deleteTemplate,
    getTemplatePage,
    exportTemplateExcel,
  } from "#/api/mes/pro/processdesign/template";
  import { $t } from "#/locales";
  import { downloadFileFromBlobPart } from "../../../../packages/utils/src";
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
  import { useGridColumns, useGridFormSchema } from "./data";
  import Form from "./modules/form.vue";
const [FormModal, formModalApi] = useVbenModal({
  connectedComponent: Form,
  destroyOnClose: true,
});
/** åˆ·æ–°è¡¨æ ¼ */
function handleRefresh() {
  gridApi.query();
}
/** åˆ›å»ºå·¥è‰ºå‚数模板 */
function handleCreate() {
  formModalApi.setData({ formType: 'create' }).open();
}
/** ç¼–辑工艺参数模板 */
function handleEdit(row: MesPdTemplateApi.Template) {
  formModalApi.setData({ id: row.id, formType: 'update' }).open();
}
/** æŸ¥çœ‹å·¥è‰ºå‚数模板详情 */
function handleDetail(row: MesPdTemplateApi.Template) {
  formModalApi.setData({ id: row.id, formType: 'detail' }).open();
}
/** åˆ é™¤å·¥è‰ºå‚数模板 */
async function handleDelete(row: MesPdTemplateApi.Template) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting', [row.name]),
    duration: 0,
  const [FormModal, formModalApi] = useVbenModal({
    connectedComponent: Form,
    destroyOnClose: true,
  });
  try {
    await deleteTemplate(row.id!);
    message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
    handleRefresh();
  } finally {
    hideLoading();
  }
}
const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: useGridFormSchema(),
  },
  gridOptions: {
    columns: useGridColumns(),
    height: 'auto',
    keepSource: true,
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          return await getTemplatePage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            ...formValues,
          });
  /** åˆ·æ–°è¡¨æ ¼ */
  function handleRefresh() {
    gridApi.query();
  }
  /** åˆ›å»ºå·¥è‰ºå‚数模板 */
  function handleCreate() {
    formModalApi.setData({ formType: "create" }).open();
  }
  /** ç¼–辑工艺参数模板 */
  function handleEdit(row: MesPdTemplateApi.Template) {
    formModalApi.setData({ id: row.id, formType: "update" }).open();
  }
  /** æŸ¥çœ‹å·¥è‰ºå‚数模板详情 */
  function handleDetail(row: MesPdTemplateApi.Template) {
    formModalApi.setData({ id: row.id, formType: "detail" }).open();
  }
  /** åˆ é™¤å·¥è‰ºå‚数模板 */
  async function handleDelete(row: MesPdTemplateApi.Template) {
    const hideLoading = message.loading({
      content: $t("ui.actionMessage.deleting", [row.templateName]),
      duration: 0,
    });
    try {
      await deleteTemplate(row.id!);
      message.success($t("ui.actionMessage.deleteSuccess", [row.templateName]));
      handleRefresh();
    } finally {
      hideLoading();
    }
  }
  /** å¯¼å‡ºå·¥è‰ºå‚数模板 */
  async function handleExport() {
    const formValues = await gridApi.formApi.getValues();
    const data = await exportTemplateExcel(formValues);
    downloadFileFromBlobPart({ fileName: "工艺参数模板.xlsx", source: data });
  }
  const [Grid, gridApi] = useVbenVxeGrid({
    formOptions: {
      schema: useGridFormSchema(),
    },
    gridOptions: {
      columns: useGridColumns(),
      height: "auto",
      keepSource: true,
      proxyConfig: {
        ajax: {
          query: async ({ page }, formValues) => {
            return await getTemplatePage({
              pageNo: page.currentPage,
              pageSize: page.pageSize,
              ...formValues,
            });
          },
        },
      },
    },
    rowConfig: {
      isHover: true,
      keyField: 'id',
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
  } as VxeTableGridOptions<MesPdTemplateApi.Template>,
});
      rowConfig: {
        isHover: true,
        keyField: "id",
      },
      toolbarConfig: {
        refresh: true,
        search: true,
      },
    } as VxeTableGridOptions<MesPdTemplateApi.Template>,
  });
</script>
<template>
  <Page auto-content-height><FormModal @success="handleRefresh" />
  <Page auto-content-height>
    <FormModal @success="handleRefresh" />
    <Grid table-title="工艺参数模板列表">
      <template #toolbar-tools>
        <TableAction
          :actions="[
        <TableAction :actions="[
            {
              label: $t('ui.actionTitle.create', ['模板']),
              type: 'primary',
@@ -100,15 +109,27 @@
              auth: ['mes:pd:template:create'],
              onClick: handleCreate,
            },
          ]"
        />
            {
              label: $t('ui.actionTitle.export'),
              type: 'primary',
              icon: ACTION_ICON.DOWNLOAD,
              auth: ['mes:pd:template:export'],
              onClick: handleExport,
            },
          ]" />
      </template>
      <template #name="{ row }">
        <Button type="link" @click="handleDetail(row)">{{ row.name }}</Button>
      <template #templateCode="{ row }">
        <Button type="link"
                @click="handleDetail(row)">{{ row.templateCode }}</Button>
      </template>
      <template #actions="{ row }">
        <TableAction
          :actions="[
        <TableAction :actions="[
            {
              label: $t('common.detail'),
              type: 'link',
              icon: ACTION_ICON.VIEW,
              onClick: handleDetail.bind(null, row),
            },
            {
              label: $t('common.edit'),
              type: 'link',
@@ -123,12 +144,11 @@
              icon: ACTION_ICON.DELETE,
              auth: ['mes:pd:template:delete'],
              popConfirm: {
                title: $t('ui.actionMessage.deleteConfirm', [row.name]),
                title: $t('ui.actionMessage.deleteConfirm', [row.templateName]),
                confirm: handleDelete.bind(null, row),
              },
            },
          ]"
        />
          ]" />
      </template>
    </Grid>
  </Page>
src/views/mes/process-design/template/modules/form.vue
@@ -1,173 +1,206 @@
<script lang="ts" setup>
import type { FormType } from '../data';
  import type { FormType } from "../data";
import type { MesPdParameterApi } from '#/api/mes/pro/processdesign/parameter';
import type { MesPdTemplateApi } from '#/api/mes/pro/processdesign/template';
  import type { MesPdParameterApi } from "#/api/mes/pro/processdesign/parameter";
  import type { MesPdTemplateApi } from "#/api/mes/pro/processdesign/template";
import { computed, ref } from 'vue';
  import { computed, ref } from "vue";
import { useVbenModal } from '../../../../../packages/effects/common-ui/src';
  import { useVbenModal } from "../../../../../packages/effects/common-ui/src";
import { Button, message, Tag } from 'ant-design-vue';
  import { Button, message, Switch, Input } from "ant-design-vue";
import { useVbenForm } from '#/adapter/form';
import {
  createTemplate,
  getTemplate,
  updateTemplate,
} from '#/api/mes/pro/processdesign/template';
import { $t } from '#/locales';
  import { useVbenForm } from "#/adapter/form";
  import {
    createTemplate,
    getTemplate,
    updateTemplate,
  } from "#/api/mes/pro/processdesign/template";
  import { $t } from "#/locales";
import { useFormSchema } from '../data';
import ParameterSelectDialog from './parameter-select-dialog.vue';
  import { useFormSchema } from "../data";
  import ParameterSelectDialog from "./parameter-select-dialog.vue";
const emit = defineEmits(['success']);
const formType = ref<FormType>('create');
const formData = ref<MesPdTemplateApi.Template>();
  const emit = defineEmits(["success"]);
  const formType = ref<FormType>("create");
  const formData = ref<MesPdTemplateApi.Template>();
/** å·²é€‰å‚数列表(用于页面展示) */
const selectedParameters = ref<MesPdParameterApi.Parameter[]>([]);
const parameterSelectDialogRef = ref<InstanceType<typeof ParameterSelectDialog>>();
  /** å·²é€‰å‚数列表(用于页面展示) */
  const selectedDetails = ref<MesPdTemplateApi.TemplateDetail[]>([]);
  const parameterSelectDialogRef =
    ref<InstanceType<typeof ParameterSelectDialog>>();
const isDetail = computed(() => formType.value === 'detail');
const getTitle = computed(() => {
  if (formType.value === 'detail') {
    return $t('ui.actionTitle.detail', ['工艺参数模板']);
  }
  return formType.value === 'update'
    ? $t('ui.actionTitle.edit', ['工艺参数模板'])
    : $t('ui.actionTitle.create', ['工艺参数模板']);
});
  const isDetail = computed(() => formType.value === "detail");
  const getTitle = computed(() => {
    if (formType.value === "detail") {
      return $t("ui.actionTitle.detail", ["工艺参数模板"]);
    }
    return formType.value === "update"
      ? $t("ui.actionTitle.edit", ["工艺参数模板"])
      : $t("ui.actionTitle.create", ["工艺参数模板"]);
  });
const [Form, formApi] = useVbenForm({
  commonConfig: {
    componentProps: {
      class: 'w-full',
  const [Form, formApi] = useVbenForm({
    commonConfig: {
      componentProps: {
        class: "w-full",
      },
      formItemClass: "col-span-1",
      labelWidth: 100,
    },
    formItemClass: 'col-span-1',
    labelWidth: 100,
  },
  layout: 'horizontal',
  schema: [],
  showDefaultActions: false,
  wrapperClass: 'grid-cols-2',
});
    layout: "horizontal",
    schema: [],
    showDefaultActions: false,
    wrapperClass: "grid-cols-2",
  });
/** æ‰“开工艺参数选择弹窗 */
function handleSelectParameters() {
  parameterSelectDialogRef.value?.open(
    selectedParameters.value
      .map((p) => p.id)
      .filter((id): id is number => id !== undefined),
  );
}
  /** æ‰“开工艺参数选择弹窗 */
  function handleSelectParameters() {
    parameterSelectDialogRef.value?.open(
      selectedDetails.value
        .map(d => d.processParamId)
        .filter((id): id is number => id !== undefined)
    );
  }
/** æŽ¥æ”¶é€‰ä¸­çš„参数 */
function onParametersSelected(rows: MesPdParameterApi.Parameter[]) {
  selectedParameters.value = rows;
}
  /** æŽ¥æ”¶é€‰ä¸­çš„参数 */
  function onParametersSelected(rows: MesPdParameterApi.Parameter[]) {
    selectedDetails.value = rows.map(row => ({
      processParamId: row.id,
      paramCode: row.paramCode,
      paramName: row.paramName,
      paramType: row.paramType,
      unitMeasureName: row.unitMeasureName,
      required: row.required,
      remark: row.remark,
    }));
  }
/** ç§»é™¤å·²é€‰å‚æ•° */
function removeParameter(param: MesPdParameterApi.Parameter) {
  selectedParameters.value = selectedParameters.value.filter(
    (p) => p.id !== param.id,
  );
}
  /** ç§»é™¤å·²é€‰å‚æ•° */
  function removeParameter(index: number) {
    selectedDetails.value.splice(index, 1);
  }
const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    if (isDetail.value) {
      await modalApi.close();
      return;
    }
    const { valid } = await formApi.validate();
    if (!valid) {
      return;
    }
    if (selectedParameters.value.length === 0) {
      message.warning('请至少选择一个工艺参数');
      return;
    }
    modalApi.lock();
    const data = (await formApi.getValues()) as MesPdTemplateApi.Template;
    data.parameterIds = selectedParameters.value
      .map((p) => p.id)
      .filter((id): id is number => id !== undefined);
    try {
      await (formData.value?.id ? updateTemplate(data) : createTemplate(data));
      await modalApi.close();
      emit('success');
      message.success($t('ui.actionMessage.operationSuccess'));
    } finally {
      modalApi.unlock();
    }
  },
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) {
      formData.value = undefined;
      selectedParameters.value = [];
      return;
    }
    formApi.setState({ schema: useFormSchema() });
    const data = modalApi.getData<{ formType: FormType; id?: number }>();
    formType.value = data.formType;
    formApi.setDisabled(formType.value === 'detail');
    modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
    if (!data?.id) {
      return;
    }
    modalApi.lock();
    try {
      formData.value = await getTemplate(data.id);
      await formApi.setValues(formData.value);
      // å›žæ˜¾å·²é€‰å‚数(从详情接口返回的参数列表中获取)
      if (formData.value.parameterIds) {
        // parameterIds å·²ç”±åŽç«¯è¿”回,selectedParameters åœ¨è¯¦æƒ…模式下展示
  const [Modal, modalApi] = useVbenModal({
    async onConfirm() {
      if (isDetail.value) {
        await modalApi.close();
        return;
      }
    } finally {
      modalApi.unlock();
    }
  },
});
      const { valid } = await formApi.validate();
      if (!valid) {
        return;
      }
      if (selectedDetails.value.length === 0) {
        message.warning("请至少选择一个工艺参数");
        return;
      }
      modalApi.lock();
      const data = (await formApi.getValues()) as MesPdTemplateApi.Template;
      data.details = selectedDetails.value.map(d => ({
        ...d,
        processParamId: d.processParamId,
        required: d.required,
        remark: d.remark,
      }));
      try {
        await (formData.value?.id ? updateTemplate(data) : createTemplate(data));
        await modalApi.close();
        emit("success");
        message.success($t("ui.actionMessage.operationSuccess"));
      } finally {
        modalApi.unlock();
      }
    },
    async onOpenChange(isOpen: boolean) {
      if (!isOpen) {
        formData.value = undefined;
        selectedDetails.value = [];
        return;
      }
      formApi.setState({ schema: useFormSchema() });
      const data = modalApi.getData<{ formType: FormType; id?: number }>();
      formType.value = data.formType;
      formApi.setDisabled(formType.value === "detail");
      modalApi.setState({ showConfirmButton: formType.value !== "detail" });
      if (!data?.id) {
        return;
      }
      modalApi.lock();
      try {
        formData.value = await getTemplate(data.id);
        await formApi.setValues(formData.value);
        // å›žæ˜¾å·²é€‰å‚数(从详情接口返回的参数列表中获取)
        if (formData.value.details) {
          selectedDetails.value = [...formData.value.details];
        }
      } finally {
        modalApi.unlock();
      }
    },
  });
</script>
<template>
  <Modal :title="getTitle" class="w-4/5">
  <Modal :title="getTitle"
         class="w-4/5">
    <Form class="mx-4" />
    <!-- å…³è”参数选择区域 -->
    <!-- æ¨¡æ¿æ˜Žç»†åŒºåŸŸ -->
    <div class="mx-4 mt-4">
      <div class="mb-2 flex items-center justify-between">
        <span class="text-sm font-medium">关联工艺参数</span>
        <Button
          v-if="!isDetail"
          type="primary"
          size="small"
          @click="handleSelectParameters"
        >
          é€‰æ‹©å‚æ•°
        <span class="text-sm font-medium">模板明细</span>
        <Button v-if="!isDetail"
                type="primary"
                size="small"
                @click="handleSelectParameters">
          æ·»åŠ å‚æ•°
        </Button>
      </div>
      <div
        v-if="selectedParameters.length > 0"
        class="min-h-10 rounded border border-gray-200 p-2"
      >
        <Tag
          v-for="param in selectedParameters"
          :key="param.id"
          :closable="!isDetail"
          class="mb-1 mr-1"
          @close="removeParameter(param)"
        >
          {{ param.name }}({{ param.code }})
        </Tag>
      <div v-if="selectedDetails.length > 0"
           class="rounded border border-gray-200">
        <div class="grid grid-cols-12 gap-2 border-b border-gray-200 bg-gray-50 p-2 text-xs font-medium">
          <div class="col-span-3">参数名称</div>
          <div class="col-span-2">参数编码</div>
          <div class="col-span-2">单位</div>
          <div class="col-span-2 text-center">是否必填</div>
          <div class="col-span-2">备注</div>
          <div v-if="!isDetail"
               class="col-span-1 text-center">操作</div>
        </div>
        <div v-for="(detail, index) in selectedDetails"
             :key="detail.processParamId || index"
             class="grid grid-cols-12 gap-2 border-b border-gray-100 p-2">
          <div class="col-span-3 text-sm">{{ detail.paramName }}</div>
          <div class="col-span-2 text-sm text-gray-500">{{ detail.paramCode }}</div>
          <div class="col-span-2 text-sm">{{ detail.unitMeasureName }}</div>
          <div class="col-span-2 flex items-center justify-center">
            <Switch v-model:checked="detail.required"
                    :disabled="isDetail"
                    size="small" />
          </div>
          <div class="col-span-2">
            <Input v-model:value="detail.remark"
                   :disabled="isDetail"
                   placeholder="备注"
                   size="small" />
          </div>
          <div v-if="!isDetail"
               class="col-span-1 flex items-center justify-center">
            <Button type="text"
                    danger
                    size="small"
                    @click="removeParameter(index)">
              åˆ é™¤
            </Button>
          </div>
        </div>
      </div>
      <div v-else class="rounded border border-dashed border-gray-300 p-4 text-center text-gray-400">
      <div v-else
           class="rounded border border-dashed border-gray-300 p-4 text-center text-gray-400">
        æš‚未选择参数
      </div>
    </div>
    <ParameterSelectDialog
      ref="parameterSelectDialogRef"
      @selected="onParametersSelected"
    />
    <ParameterSelectDialog ref="parameterSelectDialogRef"
                           @selected="onParametersSelected" />
  </Modal>
</template>
src/views/mes/process-design/template/modules/parameter-select-dialog.vue
@@ -57,7 +57,7 @@
  formOptions: {
    schema: [
      {
        fieldName: 'code',
        fieldName: 'paramCode',
        label: '参数编码',
        component: 'Input',
        componentProps: {
@@ -66,7 +66,7 @@
        },
      },
      {
        fieldName: 'name',
        fieldName: 'paramName',
        label: '参数名称',
        component: 'Input',
        componentProps: {
@@ -79,10 +79,10 @@
  gridOptions: {
    columns: [
      { type: 'checkbox', width: 50 },
      { field: 'code', title: '参数编码', minWidth: 150 },
      { field: 'name', title: '参数名称', minWidth: 180 },
      { field: 'paramCode', title: '参数编码', minWidth: 150 },
      { field: 'paramName', title: '参数名称', minWidth: 180 },
      {
        field: 'type',
        field: 'paramType',
        title: '参数类型',
        width: 120,
        cellRender: {
@@ -90,9 +90,9 @@
          props: { type: DICT_TYPE.MES_PD_PARAM_TYPE },
        },
      },
      { field: 'unit', title: '单位', width: 80 },
      { field: 'unitMeasureName', title: '单位', width: 80 },
      {
        field: 'requiredFlag',
        field: 'required',
        title: '是否必填',
        width: 90,
        cellRender: {
src/views/mes/tm/tool/modules/form.vue
@@ -1,123 +1,122 @@
<script lang="ts" setup>
import type { FormType } from '../data';
  import type { FormType } from "../data";
import type { MesTmToolApi } from '#/api/mes/tm/tool';
  import type { MesTmToolApi } from "#/api/mes/tm/tool";
import { computed, ref } from 'vue';
  import { computed, ref } from "vue";
import { useVbenModal } from '../../../../../packages/effects/common-ui/src';
import { BarcodeBizTypeEnum } from '../../../../../packages/constants/src';
  import { useVbenModal } from "../../../../../packages/effects/common-ui/src";
  import { BarcodeBizTypeEnum } from "../../../../../packages/constants/src";
import { Button, message } from 'ant-design-vue';
  import { Button, message } from "ant-design-vue";
import { useVbenForm } from '#/adapter/form';
import { createTool, getTool, updateTool } from '#/api/mes/tm/tool';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
  import { useVbenForm } from "#/adapter/form";
  import { createTool, getTool, updateTool } from "#/api/mes/tm/tool";
  import { $t } from "#/locales";
  import { BarcodeDetail } from "#/views/wls/barcode/components";
import { useFormSchema } from '../data';
  import { useFormSchema } from "../data";
const emit = defineEmits(['success']);
const formType = ref<FormType>('create'); // è¡¨å•模式
const formData = ref<MesTmToolApi.Tool>();
const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
  const emit = defineEmits(["success"]);
  const formType = ref<FormType>("create"); // è¡¨å•模式
  const formData = ref<MesTmToolApi.Tool>();
  const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // æ¡ç è¯¦æƒ…弹窗
const isDetail = computed(() => formType.value === 'detail'); // æ˜¯å¦æŸ¥çœ‹æ¨¡å¼
const getTitle = computed(() => {
  if (formType.value === 'detail') {
    return $t('ui.actionTitle.view', ['工具']);
  }
  return formType.value === 'update'
    ? $t('ui.actionTitle.edit', ['工具'])
    : $t('ui.actionTitle.create', ['工具']);
});
  const isDetail = computed(() => formType.value === "detail"); // æ˜¯å¦æŸ¥çœ‹æ¨¡å¼
  const getTitle = computed(() => {
    if (formType.value === "detail") {
      return $t("ui.actionTitle.view", ["工具"]);
    }
    return formType.value === "update"
      ? $t("ui.actionTitle.edit", ["工具"])
      : $t("ui.actionTitle.create", ["工具"]);
  });
const [Form, formApi] = useVbenForm({
  commonConfig: {
    componentProps: {
      class: 'w-full',
  const [Form, formApi] = useVbenForm({
    commonConfig: {
      componentProps: {
        class: "w-full",
      },
      formItemClass: "col-span-1",
      labelWidth: 110,
    },
    formItemClass: 'col-span-1',
    labelWidth: 110,
  },
  wrapperClass: 'grid-cols-3',
  layout: 'horizontal',
  schema: [],
  showDefaultActions: false,
});
    wrapperClass: "grid-cols-3",
    layout: "horizontal",
    schema: [],
    showDefaultActions: false,
  });
/** æŸ¥çœ‹å·¥å…·æ¡ç  */
function handleBarcode() {
  if (!formData.value?.id) {
    return;
  /** æŸ¥çœ‹å·¥å…·æ¡ç  */
  function handleBarcode() {
    if (!formData.value?.id) {
      return;
    }
    barcodeDetailRef.value?.openByBusiness(
      formData.value.id,
      BarcodeBizTypeEnum.TOOL,
      formData.value.code,
      formData.value.name
    );
  }
  barcodeDetailRef.value?.openByBusiness(
    formData.value.id,
    BarcodeBizTypeEnum.TOOL,
    formData.value.code,
    formData.value.name,
  );
}
const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    if (isDetail.value) {
      await modalApi.close();
      return;
    }
    const { valid } = await formApi.validate();
    if (!valid) {
      return;
    }
    modalApi.lock();
    // æäº¤è¡¨å•
    const data = (await formApi.getValues()) as MesTmToolApi.Tool;
    try {
      await (data.id ? updateTool(data) : createTool(data));
      // å…³é—­å¹¶æç¤º
      await modalApi.close();
      emit('success');
      message.success($t('ui.actionMessage.operationSuccess'));
    } finally {
      modalApi.unlock();
    }
  },
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) {
      formData.value = undefined;
      return;
    }
    // åŠ è½½æ•°æ®
    const data = modalApi.getData<{ formType: FormType; id?: number }>();
    formType.value = data.formType;
    formApi.setState({ schema: useFormSchema(data.formType, formApi) });
    formApi.setDisabled(formType.value === 'detail');
    modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
    if (!data?.id) {
      return;
    }
    modalApi.lock();
    try {
      formData.value = await getTool(data.id);
      // è®¾ç½®åˆ° values
      await formApi.setValues(formData.value);
    } finally {
      modalApi.unlock();
    }
  },
});
  const [Modal, modalApi] = useVbenModal({
    async onConfirm() {
      if (isDetail.value) {
        await modalApi.close();
        return;
      }
      const { valid } = await formApi.validate();
      if (!valid) {
        return;
      }
      modalApi.lock();
      // æäº¤è¡¨å•
      const data = (await formApi.getValues()) as MesTmToolApi.Tool;
      try {
        await (data.id ? updateTool(data) : createTool(data));
        // å…³é—­å¹¶æç¤º
        await modalApi.close();
        emit("success");
        message.success($t("ui.actionMessage.operationSuccess"));
      } finally {
        modalApi.unlock();
      }
    },
    async onOpenChange(isOpen: boolean) {
      if (!isOpen) {
        formData.value = undefined;
        return;
      }
      // åŠ è½½æ•°æ®
      const data = modalApi.getData<{ formType: FormType; id?: number }>();
      formType.value = data.formType;
      formApi.setState({ schema: useFormSchema(data.formType, formApi) });
      formApi.setDisabled(formType.value === "detail");
      modalApi.setState({ showConfirmButton: formType.value !== "detail" });
      if (!data?.id) {
        return;
      }
      modalApi.lock();
      try {
        formData.value = await getTool(data.id);
        // è®¾ç½®åˆ° values
        await formApi.setValues(formData.value);
      } finally {
        modalApi.unlock();
      }
    },
  });
</script>
<template>
  <Modal :title="getTitle" class="w-4/5">
  <Modal :title="getTitle"
         class="w-4/5">
    <Form class="mx-4" />
    <template #prepend-footer>
      <div class="flex flex-auto items-center">
        <Button
          v-if="isDetail && formData?.id"
          type="primary"
          @click="handleBarcode"
        >
        <Button v-if="isDetail && formData?.id"
                type="primary"
                @click="handleBarcode">
          æŸ¥çœ‹æ¡ç 
        </Button>
      </div>
src/views/outsourcereceipt/modules/line-list.vue
@@ -18,7 +18,7 @@
  getOutsourceReceiptLinePage,
} from '#/api/mes/wm/outsourcereceipt/line';
import { $t } from '#/locales';
import { PrinterLabel } from '#/views/mes/wm/barcode/components';
import { PrinterLabel } from '#/views/wls/barcode/components';
import { useLineGridColumns } from '../data';
import DetailForm from './detail-form.vue';
src/views/productreceipt/modules/line-list.vue
@@ -19,7 +19,7 @@
  getProductReceiptLinePage,
} from '#/api/mes/wm/productreceipt/line';
import { $t } from '#/locales';
import { BarcodeDetail, PrinterLabel } from '#/views/mes/wm/barcode/components';
import { BarcodeDetail, PrinterLabel } from '#/views/wls/barcode/components';
import { useLineGridColumns } from '../data';
import DetailForm from './detail-form.vue';
src/views/returnissue/modules/line-list.vue
@@ -19,7 +19,7 @@
  getReturnIssueLinePage,
} from '#/api/mes/wm/returnissue/line';
import { $t } from '#/locales';
import { BarcodeDetail, PrinterLabel } from '#/views/mes/wm/barcode/components';
import { BarcodeDetail, PrinterLabel } from '#/views/wls/barcode/components';
import { useLineGridColumns } from '../data';
import DetailForm from './detail-form.vue';
src/views/returnsales/modules/line-list.vue
@@ -18,7 +18,7 @@
  getReturnSalesLinePage,
} from '#/api/mes/wm/returnsales/line';
import { $t } from '#/locales';
import { PrinterLabel } from '#/views/mes/wm/barcode/components';
import { PrinterLabel } from '#/views/wls/barcode/components';
import { useLineGridColumns } from '../data';
import DetailForm from './detail-form.vue';
src/views/returnvendor/modules/line-list.vue
@@ -18,7 +18,7 @@
  getReturnVendorLinePage,
} from '#/api/mes/wm/returnvendor/line';
import { $t } from '#/locales';
import { PrinterLabel } from '#/views/mes/wm/barcode/components';
import { PrinterLabel } from '#/views/wls/barcode/components';
import { useLineGridColumns } from '../data';
import DetailForm from './detail-form.vue';
src/views/sn/modules/detail.vue
@@ -11,7 +11,7 @@
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getSnListByUuid } from '#/api/mes/wm/sn';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
import { BarcodeDetail } from '#/views/wls/barcode/components';
import { useDetailGridColumns } from '../data';
src/views/transfer/modules/line-list.vue
@@ -18,7 +18,7 @@
  getTransferLineList,
} from '#/api/mes/wm/transfer/line';
import { $t } from '#/locales';
import { PrinterLabel } from '#/views/mes/wm/barcode/components';
import { PrinterLabel } from '#/views/wls/barcode/components';
import { useLineGridColumns } from '../data';
import DetailForm from './detail-form.vue';
src/views/warehouse/area/modules/form.vue
@@ -18,7 +18,7 @@
} from '#/api/mes/wm/warehouse/area';
import { getWarehouseLocation } from '#/api/mes/wm/warehouse/location';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
import { BarcodeDetail } from '#/views/wls/barcode/components';
import { useFormSchema } from '../data';
src/views/warehouse/location/modules/form.vue
@@ -18,7 +18,7 @@
  updateWarehouseLocation,
} from '#/api/mes/wm/warehouse/location';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
import { BarcodeDetail } from '#/views/wls/barcode/components';
import { useFormSchema } from '../data';
src/views/warehouse/modules/form.vue
@@ -17,7 +17,7 @@
  updateWarehouse,
} from '#/api/mes/wm/warehouse';
import { $t } from '#/locales';
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
import { BarcodeDetail } from '#/views/wls/barcode/components';
import { useFormSchema } from '../data';
src/views/wls/arrivalnotice/data.ts
@@ -77,11 +77,26 @@
      rules: 'required',
    },
    {
      fieldName: 'vendorName',
      label: '',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'purchaseOrderId',
      label: '采购订单',
      component: markRaw(ErpPurchaseOrderSelect),
      componentProps: {
        placeholder: '请选择采购订单',
        onChange: (item: any) => {
          if (item?.supplierId && formApi) {
            formApi.setFieldValue('vendorId', item.supplierId);
            formApi.setFieldValue('vendorName', item.supplierName);
          }
        },
      },
    },
    {
@@ -90,6 +105,15 @@
      component: markRaw(MdVendorSelect),
      componentProps: {
        placeholder: '请选择供应商',
        disabled: true,
      },
      dependencies: {
        triggerFields: ['vendorName'],
        componentProps: (values) => ({
          placeholder: '请选择供应商',
          disabled: true,
          vendorName: values.vendorName as string,
        }),
      },
      rules: 'selectRequired',
    },
src/views/wls/arrivalnotice/index.vue
@@ -2,9 +2,9 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmArrivalNoticeApi } from '#/api/mes/wm/arrivalnotice';
import { Page, useVbenModal } from '../../../packages/effects/common-ui/src';
import { MesWmArrivalNoticeStatusEnum } from '../../../packages/constants/src';
import { downloadFileFromBlobPart } from '../../../packages/utils/src';
import { Page, useVbenModal } from '@vben/common-ui';
import { MesWmArrivalNoticeStatusEnum } from '@vben/constants';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
@@ -12,15 +12,22 @@
import {
  deleteArrivalNotice,
  exportArrivalNotice,
  generateArrivalNoticeFromPurchaseOrder,
  getArrivalNoticePage,
} from '#/api/mes/wm/arrivalnotice';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
import PoSelectDialog from './modules/po-select-dialog.vue';
const [FormModal, formModalApi] = useVbenModal({
  connectedComponent: Form,
  destroyOnClose: true,
});
const [PoSelectModal, poSelectModalApi] = useVbenModal({
  connectedComponent: PoSelectDialog,
  destroyOnClose: true,
});
@@ -69,6 +76,28 @@
  message.info('请前往【仓库管理 - é‡‡è´­å…¥åº“】中进行入库操作');
}
/** ä»Žé‡‡è´­è®¢å•生成到货通知单 */
async function handleGenerateFromPO() {
  poSelectModalApi.open();
}
/** é‡‡è´­è®¢å•选择回调 */
async function handlePurchaseOrderSelected(purchaseOrderId: number) {
  const hideLoading = message.loading({
    content: '正在生成到货通知单...',
    duration: 0,
  });
  try {
    const id = await generateArrivalNoticeFromPurchaseOrder(purchaseOrderId);
    message.success('到货通知单生成成功');
    // è·³è½¬åˆ°ç¼–辑页
    formModalApi.setData({ formType: 'update', id }).open();
    handleRefresh();
  } finally {
    hideLoading();
  }
}
/** å¯¼å‡ºè¡¨æ ¼ */
async function handleExport() {
  const data = await exportArrivalNotice(await gridApi.formApi.getValues());
@@ -107,7 +136,9 @@
</script>
<template>
  <Page auto-content-height><FormModal @success="handleRefresh" />
  <Page auto-content-height>
    <FormModal @success="handleRefresh" />
    <PoSelectModal @selected="handlePurchaseOrderSelected" />
    <Grid table-title="到货通知单列表">
      <template #toolbar-tools>
@@ -121,6 +152,13 @@
              onClick: handleCreate,
            },
            {
              label: '从采购订单生成',
              type: 'primary',
              icon: ACTION_ICON.ADD,
              auth: ['mes:wm-arrival-notice:create'],
              onClick: handleGenerateFromPO,
            },
            {
              label: $t('ui.actionTitle.export'),
              type: 'primary',
              icon: ACTION_ICON.DOWNLOAD,
src/views/wls/arrivalnotice/modules/po-select-dialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
<script lang="ts" setup>
import type { ErpPurchaseOrderApi } from '#/api/erp/purchase/order';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { Page, useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getPurchaseOrderPage } from '#/api/erp/purchase/order';
const emit = defineEmits<{
  selected: [purchaseOrderId: number];
}>();
const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: [
      {
        fieldName: 'no',
        label: '订单号',
        component: 'Input',
        componentProps: {
          allowClear: true,
          placeholder: '请输入订单号',
        },
      },
      {
        fieldName: 'supplierName',
        label: '供应商',
        component: 'Input',
        componentProps: {
          allowClear: true,
          placeholder: '请输入供应商名称',
        },
      },
    ],
  },
  gridOptions: {
    columns: [
      {
        type: 'radio',
        width: 60,
      },
      {
        field: 'no',
        title: '订单号',
        minWidth: 160,
      },
      {
        field: 'supplierName',
        title: '供应商名称',
        minWidth: 120,
      },
      {
        field: 'orderTime',
        title: '订单日期',
        width: 180,
        formatter: 'formatDate',
      },
      {
        field: 'totalPrice',
        title: '总金额',
        width: 120,
        formatter: 'formatMoney',
      },
      {
        field: 'status',
        title: '状态',
        width: 100,
        cellRender: {
          name: 'CellDict',
          props: { type: 'erp_purchase_order_status' },
        },
      },
    ],
    height: 'auto',
    keepSource: true,
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          return await getPurchaseOrderPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            ...formValues,
          });
        },
      },
    },
    rowConfig: {
      keyField: 'id',
      isHover: true,
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
  } as VxeTableGridOptions<ErpPurchaseOrderApi.PurchaseOrder>,
});
const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    const selectedRows = gridApi.grid.getRadioRecord();
    if (!selectedRows) {
      message.warning('请选择采购订单');
      return;
    }
    emit('selected', selectedRows.id);
    await modalApi.close();
  },
  async onOpenChange(isOpen: boolean) {
    if (isOpen) {
      await gridApi.query();
    }
  },
});
</script>
<template>
  <Modal title="选择采购订单" class="w-2/3">
    <Grid table-title="采购订单列表" />
  </Modal>
</template>
src/views/wls/barcode/components/detail.vue
@@ -24,6 +24,7 @@
  column: 1,
  schema: useBarcodeDetailSchema(),
  useCard: false,
  labelStyle: { width: '80px', whiteSpace: 'nowrap' },
});
function openModal(row: Partial<MesWmBarcodeApi.Barcode>) {
@@ -98,11 +99,86 @@
    message.warning('条码生成失败,无法下载');
    return;
  }
  const link = document.createElement('a');
  link.href = base64;
  link.download = `barcode_${barcodeData.value.bizCode || 'unknown'}_${Date.now()}.png`;
  link.click();
  message.success('下载成功');
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  if (!ctx) {
    message.error('Canvas åˆå§‹åŒ–失败');
    return;
  }
  const img = new Image();
  img.onload = () => {
    const padding = 20;
    const infoX = img.width + padding * 2;
    const infoWidth = 320;
    canvas.width = infoX + infoWidth + padding;
    canvas.height = Math.max(img.height + padding * 2, 360);
    ctx.fillStyle = '#ffffff';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(img, padding, padding);
    ctx.fillStyle = '#000000';
    let y = padding + 10;
    const lineHeight = 26;
    // èŽ·å–å­—å…¸æ–‡æœ¬çš„è¾…åŠ©å‡½æ•°
    const getFormatText = (val?: number) => {
      const map: Record<number, string> = { 1: 'QR_CODE', 2: 'CODE_128', 3: 'CODE_39', 4: 'EAN_13', 5: 'UPC_A' };
      return map[val || 1] || 'QR_CODE';
    };
    const getBizTypeText = (val?: number) => {
      const map: Record<number, string> = { 1: '仓库', 2: '库区', 3: '库位', 4: '物料', 5: '客户', 6: '供应商', 7: '工单', 8: '设备', 9: '工具', 10: '人员', 11: '工作站', 12: '车间', 13: '库存', 14: '流转卡', 15: '装箱单', 16: '批次' };
      return map[val || 1] || '-';
    };
    const getStatusText = (val?: number) => val === 0 ? '禁用' : '启用';
    const items = [
      { label: '条码格式', value: getFormatText(barcodeData.value.format) },
      { label: '业务类型', value: getBizTypeText(barcodeData.value.bizType) },
      { label: '业务编码', value: barcodeData.value.bizCode },
      { label: '业务名称', value: barcodeData.value.bizName },
      { label: '条码内容', value: barcodeData.value.content },
      { label: '状态', value: getStatusText(barcodeData.value.status) },
    ];
    items.forEach((item) => {
      ctx.font = 'bold 13px Arial, sans-serif';
      ctx.fillText(`${item.label}:`, infoX, y);
      y += 18;
      ctx.font = '13px Arial, sans-serif';
      const text = item.value || '-';
      // å¤„理长文本换行
      const maxWidth = infoWidth - 10;
      if (ctx.measureText(text).width > maxWidth) {
        let currentText = text;
        while (currentText.length > 0) {
          let line = '';
          for (let i = 0; i < currentText.length; i++) {
            const testLine = line + currentText[i];
            if (ctx.measureText(testLine).width > maxWidth) break;
            line = testLine;
          }
          ctx.fillText(line, infoX + 5, y);
          y += 16;
          currentText = currentText.slice(line.length);
        }
        y += 6;
      } else {
        ctx.fillText(text, infoX + 5, y);
        y += lineHeight;
      }
    });
    const link = document.createElement('a');
    link.href = canvas.toDataURL('image/png');
    link.download = `barcode_${barcodeData.value.bizCode || 'unknown'}_${Date.now()}.png`;
    link.click();
    message.success('下载成功');
  };
  img.src = base64;
}
async function handleGenerate() {
@@ -126,36 +202,35 @@
</script>
<template>
  <Modal v-model:open="open" title="查看条码" width="500px">
  <Modal v-model:open="open" title="查看条码" width="750px">
    <div>
      <div
        class="mb-5 flex min-h-50 items-center justify-center rounded bg-gray-100 p-5"
      >
        <div
          v-if="barcodeData.content"
          class="flex items-center justify-center"
        >
          <WlsBarcode
            ref="barcodeRef"
            :content="barcodeData.content"
            :format="barcodeData.format"
            :height="150"
            :width="400"
          />
      <div class="mb-5 flex min-h-50 items-center rounded bg-gray-100 p-5">
        <div v-if="barcodeData.content" class="flex w-full items-center gap-6">
          <div class="flex-shrink-0">
            <WlsBarcode
              ref="barcodeRef"
              :content="barcodeData.content"
              :format="barcodeData.format"
              :height="150"
              :width="280"
            />
          </div>
          <div class="flex-1">
            <Descriptions :data="barcodeData">
              <template #content>
                <Tooltip :title="barcodeData.content">
                  <span
                    class="inline-block max-w-60 overflow-hidden text-ellipsis whitespace-nowrap"
                  >
                    {{ barcodeData.content }}
                  </span>
                </Tooltip>
              </template>
            </Descriptions>
          </div>
        </div>
        <Empty v-else description="暂无条码数据" />
        <Empty v-else description="暂无条码数据" class="flex-1" />
      </div>
      <Descriptions :data="barcodeData">
        <template #content>
          <Tooltip :title="barcodeData.content">
            <span
              class="inline-block max-w-75 overflow-hidden text-ellipsis whitespace-nowrap"
            >
              {{ barcodeData.content }}
            </span>
          </Tooltip>
        </template>
      </Descriptions>
    </div>
    <template #footer>
      <Button v-if="!barcodeData.content" @click="handleGenerate">
src/views/wls/barcode/index.vue
@@ -97,7 +97,7 @@
/** è·³è½¬æ¡ç é…ç½® */
function handleConfig() {
  router.push({ name: 'WlsBarcodeConfig' });
  router.push({ path: '/wls/barcode/config' });
}
/** å¯¼å‡ºè¡¨æ ¼ */
@@ -193,7 +193,7 @@
            :content="row.content"
            :format="row.format"
            :height="60"
            :width="140"
            :width="60"
          />
        </div>
      </template>
src/views/wls/itemreceipt/index.vue
@@ -2,9 +2,9 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesWmItemReceiptApi } from '#/api/mes/wm/itemreceipt';
import { Page, useVbenModal } from '../../../packages/effects/common-ui/src';
import { MesWmItemReceiptStatusEnum } from '../../../packages/constants/src';
import { downloadFileFromBlobPart } from '../../../packages/utils/src';
import { Page, useVbenModal } from '@vben/common-ui';
import { MesWmItemReceiptStatusEnum } from '@vben/constants';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
@@ -13,15 +13,22 @@
  cancelItemReceipt,
  deleteItemReceipt,
  exportItemReceipt,
  generateItemReceiptFromArrivalNotice,
  getItemReceiptPage,
} from '#/api/mes/wm/itemreceipt';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
import AnSelectDialog from './modules/an-select-dialog.vue';
const [FormModal, formModalApi] = useVbenModal({
  connectedComponent: Form,
  destroyOnClose: true,
});
const [AnSelectModal, anSelectModalApi] = useVbenModal({
  connectedComponent: AnSelectDialog,
  destroyOnClose: true,
});
@@ -77,6 +84,28 @@
  handleRefresh();
}
/** ä»Žåˆ°è´§é€šçŸ¥ç”Ÿæˆé‡‡è´­å…¥åº“单 */
async function handleGenerateFromAN() {
  anSelectModalApi.open();
}
/** åˆ°è´§é€šçŸ¥é€‰æ‹©å›žè°ƒ */
async function handleArrivalNoticeSelected(arrivalNoticeId: number) {
  const hideLoading = message.loading({
    content: '正在生成采购入库单...',
    duration: 0,
  });
  try {
    const id = await generateItemReceiptFromArrivalNotice(arrivalNoticeId);
    message.success('采购入库单生成成功');
    // è·³è½¬åˆ°ç¼–辑页
    formModalApi.setData({ formType: 'update', id }).open();
    handleRefresh();
  } finally {
    hideLoading();
  }
}
/** å¯¼å‡ºè¡¨æ ¼ */
async function handleExport() {
  const data = await exportItemReceipt(await gridApi.formApi.getValues());
@@ -115,7 +144,9 @@
</script>
<template>
  <Page auto-content-height><FormModal @success="handleRefresh" />
  <Page auto-content-height>
    <FormModal @success="handleRefresh" />
    <AnSelectModal @selected="handleArrivalNoticeSelected" />
    <Grid table-title="采购入库单列表">
      <template #toolbar-tools>
@@ -129,6 +160,13 @@
              onClick: handleCreate,
            },
            {
              label: '从到货通知生成',
              type: 'primary',
              icon: ACTION_ICON.ADD,
              auth: ['mes:wm-item-receipt:create'],
              onClick: handleGenerateFromAN,
            },
            {
              label: $t('ui.actionTitle.export'),
              type: 'primary',
              icon: ACTION_ICON.DOWNLOAD,
src/views/wls/itemreceipt/modules/an-select-dialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,129 @@
<script lang="ts" setup>
import type { MesWmArrivalNoticeApi } from '#/api/mes/wm/arrivalnotice';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { Page, useVbenModal } from '@vben/common-ui';
import { MesWmArrivalNoticeStatusEnum } from '@vben/constants';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getArrivalNoticePage } from '#/api/mes/wm/arrivalnotice';
const emit = defineEmits<{
  selected: [arrivalNoticeId: number];
}>();
const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: [
      {
        fieldName: 'code',
        label: '通知单号',
        component: 'Input',
        componentProps: {
          allowClear: true,
          placeholder: '请输入通知单号',
        },
      },
      {
        fieldName: 'vendorName',
        label: '供应商',
        component: 'Input',
        componentProps: {
          allowClear: true,
          placeholder: '请输入供应商名称',
        },
      },
    ],
  },
  gridOptions: {
    columns: [
      {
        type: 'radio',
        width: 60,
      },
      {
        field: 'code',
        title: '通知单号',
        minWidth: 160,
      },
      {
        field: 'name',
        title: '通知单名称',
        minWidth: 150,
      },
      {
        field: 'vendorName',
        title: '供应商名称',
        minWidth: 120,
      },
      {
        field: 'purchaseOrderNo',
        title: '采购订单号',
        minWidth: 140,
      },
      {
        field: 'arrivalDate',
        title: '到货日期',
        width: 180,
        formatter: 'formatDate',
      },
      {
        field: 'status',
        title: '状态',
        width: 100,
        cellRender: {
          name: 'CellDict',
          props: { type: 'mes_wm_arrival_notice_status' },
        },
      },
    ],
    height: 'auto',
    keepSource: true,
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          return await getArrivalNoticePage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            status: MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT,
            ...formValues,
          });
        },
      },
    },
    rowConfig: {
      keyField: 'id',
      isHover: true,
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
  } as VxeTableGridOptions<MesWmArrivalNoticeApi.ArrivalNotice>,
});
const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    const selectedRows = gridApi.grid.getRadioRecord();
    if (!selectedRows) {
      message.warning('请选择到货通知单');
      return;
    }
    emit('selected', selectedRows.id);
    await modalApi.close();
  },
  async onOpenChange(isOpen: boolean) {
    if (isOpen) {
      await gridApi.query();
    }
  },
});
</script>
<template>
  <Modal title="选择到货通知单" class="w-2/3">
    <Grid table-title="到货通知单列表" />
  </Modal>
</template>
src/views/wls/itemreceipt/modules/form.vue
@@ -5,8 +5,8 @@
import { computed, ref } from 'vue';
import { confirm, useVbenModal } from '../../../../packages/effects/common-ui/src';
import { MesWmItemReceiptStatusEnum } from '../../../../packages/constants/src';
import { confirm, useVbenModal } from '@vben/common-ui';
import { MesWmItemReceiptStatusEnum } from '@vben/constants';
import { Button, Divider, message, Popconfirm } from 'ant-design-vue';
@@ -17,6 +17,7 @@
  getItemReceipt,
  stockItemReceipt,
  submitItemReceipt,
  submitItemReceiptApproval,
  updateItemReceipt,
} from '#/api/mes/wm/itemreceipt';
import { $t } from '#/locales';
@@ -37,6 +38,12 @@
const canSubmit = computed(
  () =>
    // æ˜¯å¦å¯æäº¤
    formType.value === 'update' &&
    formData.value?.status === MesWmItemReceiptStatusEnum.PREPARE,
);
const canSubmitApproval = computed(
  () =>
    // æ˜¯å¦å¯æäº¤å®¡æ‰¹
    formType.value === 'update' &&
    formData.value?.status === MesWmItemReceiptStatusEnum.PREPARE,
);
@@ -118,6 +125,24 @@
  try {
    await finishItemReceipt(formData.value.id);
    message.success('入库成功');
    await modalApi.close();
    emit('success');
  } finally {
    modalApi.unlock();
  }
}
/** æäº¤å®¡æ‰¹ */
async function handleSubmitApproval() {
  if (!formData.value?.id) {
    return;
  }
  modalApi.lock();
  try {
    // ä½¿ç”¨é»˜è®¤æµç¨‹key,实际项目中可能需要弹窗选择
    const processDefinitionKey = 'item_receipt_approval';
    await submitItemReceiptApproval(formData.value.id, processDefinitionKey);
    message.success('提交审批成功');
    await modalApi.close();
    emit('success');
  } finally {
@@ -230,6 +255,9 @@
        >
          <Button type="primary">提交</Button>
        </Popconfirm>
        <Button v-if="canSubmitApproval" type="primary" @click="handleSubmitApproval">
          æäº¤å®¡æ‰¹
        </Button>
        <Button v-if="isStock" type="primary" @click="confirmStock">
          æ‰§è¡Œä¸Šæž¶
        </Button>
src/views/wls/returnvendor/data.ts
@@ -14,6 +14,7 @@
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { MdItemSelect } from '#/views/mes/md/item/components';
import { MdVendorSelect } from '#/views/mes/md/vendor/components';
import { ErpPurchaseOrderSelect } from '#/views/erp/purchase/order/components';
import { WmBatchSelect } from '#/views/wls/batch/components';
import { WmMaterialStockSelect } from '#/views/wls/materialstock/components';
import {
@@ -87,11 +88,26 @@
      rules: 'required',
    },
    {
      fieldName: 'purchaseOrderCode',
      label: '采购订单号',
      fieldName: 'vendorName',
      label: '',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'purchaseOrderId',
      label: '采购订单',
      component: markRaw(ErpPurchaseOrderSelect),
      componentProps: {
        placeholder: '请输入采购订单号',
        placeholder: '请选择采购订单',
        onChange: (item: any) => {
          if (item?.supplierId && formApi) {
            formApi.setFieldValue('vendorId', item.supplierId);
            formApi.setFieldValue('vendorName', item.supplierName);
          }
        },
      },
    },
    {
@@ -100,6 +116,15 @@
      component: markRaw(MdVendorSelect),
      componentProps: {
        placeholder: '请选择供应商',
        disabled: true,
      },
      dependencies: {
        triggerFields: ['vendorName'],
        componentProps: (values) => ({
          placeholder: '请选择供应商',
          disabled: true,
          vendorName: values.vendorName as string,
        }),
      },
      rules: 'selectRequired',
    },
src/views/wls/warehouse/area/index.vue
@@ -22,6 +22,8 @@
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
defineOptions({ name: 'WlsArea' });
const route = useRoute();
const [FormModal, formModalApi] = useVbenModal({
src/views/wls/warehouse/index.vue
@@ -18,6 +18,8 @@
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
defineOptions({ name: 'WlsWarehouse' });
const router = useRouter();
const [FormModal, formModalApi] = useVbenModal({
@@ -65,7 +67,7 @@
/** è·³è½¬åº“区列表 */
function handleOpenLocation(row: MesWmWarehouseApi.Warehouse) {
  router.push({
    name: 'WlsLocation',
    path: '/wls/location',
    query: { warehouseId: String(row.id) },
  });
}
src/views/wls/warehouse/location/index.vue
@@ -22,6 +22,8 @@
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
defineOptions({ name: 'WlsLocation' });
const route = useRoute();
const router = useRouter();
@@ -99,7 +101,7 @@
/** è·³è½¬åº“位列表 */
function handleOpenArea(row: MesWmWarehouseLocationApi.WarehouseLocation) {
  router.push({
    name: 'WlsArea',
    path: '/wls/area',
    query: { locationId: String(row.id) },
  });
}
vite.config.ts
@@ -7,6 +7,40 @@
      oxc: {
        enable: false,
      },
      optimizeDeps: {
        include: [
          "vue",
          "vue-router",
          "pinia",
          "ant-design-vue",
          "dayjs",
          "@vueuse/core",
          "axios",
        ],
        exclude: [
          "dhtmlx-gantt",
          "tinymce",
          "bpmn-js",
          "bpmn-js-properties-panel",
          "video.js",
        ],
      },
      build: {
        rollupOptions: {
          output: {
            manualChunks: {
              "vendor-vue": ["vue", "vue-router", "pinia"],
              "vendor-ui": ["ant-design-vue", "@vueuse/core"],
              "vendor-utils": ["dayjs", "axios", "@vee-validate/zod", "zod"],
              "vendor-editor": [
                "tinymce",
                "@tinymce/tinymce-vue",
                "@form-create/ant-design-vue",
              ],
            },
          },
        },
      },
      server: {
        allowedHosts: true,
        watch: {