2 天以前 46624ce480a3c14da21f39cc4ad5f7eecea2bb1e
feat(mes): 优化生产批次和袋码管理功能

- 在托盘绑定批次界面添加操作提示信息
- 移除托盘列表中的容量字段显示
- 重构物料基础数据界面,移除品种分类相关功能
- 为计量单位添加类型选项和表单字段
- 更新袋码界面增加品种筛选和显示列
- 重构生产批次表单,将品种改为加工单位,添加绑定托盘功能
- 在袋码生成界面实现按计量单位类型动态生成托盘或袋码
- 添加未绑定批次托盘查询接口和绑定功能
- 更新业务类型选项和条码详情组件的枚举值
- 移除物料API中的品类和采购价相关字段
- 添加原始袋码生成功能模块
已添加1个文件
已修改26个文件
721 ■■■■■ 文件已修改
src/api/mdm/item/index.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mdm/unit/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/bagCode/index.ts 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/batch/index.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/pallet/index.ts 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/mdm/components/data.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/mdm/components/select-dialog.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/mdm/data.ts 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/mdm/index.vue 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/mdm/modules/form.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/mdm/unit/data.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/order/modules/item-form.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/request/modules/item-form.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/bagCode/data.ts 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/bagCode/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/bagCode/modules/bind-pallet.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/bagCode/modules/generate-form.vue 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/bagCode/modules/raw-generate-form.vue 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/batch/data.ts 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/batch/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/batch/modules/form.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/pallet/data.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/pallet/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/pallet/modules/bind-batch.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/pallet/modules/generate-form.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/barcode/components/detail.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/transaction/data.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mdm/item/index.ts
@@ -10,8 +10,6 @@
    name: string;
    barCode?: string;
    specification?: string;
    categoryId?: number;
    categoryName?: string;
    unitMeasureId?: number;
    unitMeasureName?: string;
    unitMeasureId2?: number;
@@ -24,7 +22,6 @@
    brandName?: string;
    itemType: number;
    status: number;
    purchasePrice?: number;
    salesPrice?: number;
    costPrice?: number;
    safetyStock?: number;
@@ -54,7 +51,6 @@
  export interface PageParams extends PageParam {
    code?: string;
    name?: string;
    categoryId?: number;
    itemType?: number;
    status?: number;
  }
src/api/mdm/unit/index.ts
@@ -8,6 +8,7 @@
    id?: number; // å•位编号
    code: string; // å•位编码
    name: string; // å•位名称
    type?: string; // ç±»åž‹ï¼ˆ48千克/100千克/吨)
    primaryFlag?: boolean; // æ˜¯å¦ä¸»å•位
    primaryId?: number; // ä¸»å•位编号
    changeRate?: number; // ä¸Žä¸»å•位换算比例
src/api/mes/pro/bagCode/index.ts
@@ -15,6 +15,9 @@
    lineName?: string; // äº§çº¿åç§°
    palletId?: number; // ç»‘定托盘编号
    palletCode?: string; // æ‰˜ç›˜ç 
    itemId?: number; // å“ç§ï¼ˆç‰©æ–™ï¼‰ç¼–号
    itemCode?: string; // å“ç§ç¼–码
    itemName?: string; // å“ç§åç§°
    status?: number; // çŠ¶æ€ï¼ˆå­—å…¸ mes_pro_bag_code_status)
    printTime?: string; // å¯¼å‡ºï¼ˆäº¤ä»˜å°åˆ·ï¼‰æ—¶é—´
    cancelTime?: string; // ä½œåºŸæ—¶é—´
@@ -30,18 +33,32 @@
    batchCode?: string;
    uuid?: string;
    palletId?: number;
    itemId?: number;
    status?: number;
    createTime?: string[];
  }
  /** ç”Ÿæˆæ•°æ®åŒ…请求 */
  /** ç”Ÿæˆæ•°æ®åŒ…请求(品种主计量单位类型为 N千克 æ—¶æŒ‰æ‰˜ç›˜æ•°æ¢ç®—袋数;为 å¨ æ—¶æ— æ‰˜ç›˜ã€ç›´æŽ¥è¾“入袋码数) */
  export interface GenerateParams {
    count: number; // ç”Ÿæˆè¢‹æ•°
    itemId: number; // å“ç§ï¼ˆç‰©æ–™ï¼‰ç¼–号
    palletCount?: number; // æ‰˜ç›˜æ•°ï¼ˆ48千克/100千克 å“ç§å¿…填)
    bagCount?: number; // è¢‹ç æ•°ï¼ˆå¨å“ç§å¿…填)
    remark?: string; // å¤‡æ³¨
  }
  export interface GenerateResult {
    uuid?: string;
    uuid?: string; // ç”Ÿæˆç»„ UUID
    type?: string; // å“ç§ä¸»è®¡é‡å•位类型(48千克/100千克/吨)
    perPallet?: number; // æ¯æ‰˜è¢‹æ•°ï¼ˆå¨å“ç§ä¸º 0)
    totalCount?: number; // ç”Ÿæˆè¢‹ç æ€»æ•°
    palletIds?: number[]; // è‡ªåŠ¨ç”Ÿæˆçš„æ‰˜ç›˜ç¼–å·åˆ—è¡¨ï¼ˆå¨å“ç§ä¸ºç©ºï¼‰
  }
  /** ç”ŸæˆåŽŸå§‹è¢‹ç è¯·æ±‚ï¼ˆä»…å“ç§+袋码数量,不关联计量单位类型、不生成托盘) */
  export interface RawGenerateParams {
    itemId: number; // å“ç§ï¼ˆç‰©æ–™ï¼‰ç¼–号
    count: number; // è¢‹ç æ•°é‡
    remark?: string; // å¤‡æ³¨
  }
  export interface BindBatchParams {
@@ -125,6 +142,14 @@
  );
}
/** ç”ŸæˆåŽŸå§‹è¢‹ç ï¼ˆä»…é€‰å“ç§ä¸Žè¢‹ç æ•°é‡ï¼Œä¸ç”Ÿæˆæ‰˜ç›˜ï¼›è¿”å›žç”Ÿæˆç»„ UUID) */
export function generateRawBagCodes(data: MesProBagCodeApi.RawGenerateParams) {
  return requestClient.post<MesProBagCodeApi.GenerateResult>(
    '/mes/pro/bag-code/raw-generate',
    data,
  );
}
/** å½•入生产批次 */
export function bindBagCodeBatch(data: MesProBagCodeApi.BindBatchParams) {
  return requestClient.put('/mes/pro/bag-code/bind-batch', data);
src/api/mes/pro/batch/index.ts
@@ -20,15 +20,12 @@
    id?: number; // ç¼–号
    code?: string; // æ‰¹æ¬¡å·ï¼ˆè¡¥å½•正式信息后自动生成)
    tempCode?: string; // ä¸´æ—¶æ‰¹æ¬¡ç ï¼ˆé¢„生成二维码阶段使用,扫码追溯兼容)
    itemId?: number; // å“ç§ï¼ˆäº§å“ç‰©æ–™ï¼‰ç¼–号
    itemCode?: string; // å“ç§ç¼–码(后端拼接)
    itemName?: string; // å“ç§åç§°ï¼ˆåŽç«¯æ‹¼æŽ¥ï¼‰
    itemSpecification?: string; // å“ç§è§„格(后端拼接)
    lineId?: number; // äº§çº¿ç¼–号
    lineCode?: string; // äº§çº¿ç¼–码(后端回填)
    lineName?: string; // äº§çº¿åç§°ï¼ˆåŽç«¯å›žå¡«ï¼‰
    lineId?: number; // äº§çº¿ç¼–号(页面展示为「加工单位」)
    lineCode?: string; // äº§çº¿ç¼–码(后端回填,页面已隐藏)
    lineName?: string; // äº§çº¿åç§°ï¼ˆåŽç«¯å›žå¡«ï¼Œé¡µé¢å±•示为「加工单位」)
    processingTeam?: string; // åŠ å·¥ç­ç»„ï¼ˆæ‰‹å·¥å¡«å†™ï¼‰
    produceDate?: string; // ç”Ÿäº§æ—¥æœŸ
    planQuantity?: number; // è®¡åˆ’数量
    planQuantity?: number; // åŠ å·¥æ•°é‡ï¼ˆåŽŸè®¡åˆ’æ•°é‡ï¼Œé¡µé¢å±•ç¤ºä¸ºã€ŒåŠ å·¥æ•°é‡ã€ï¼‰
    ownerUserId?: number; // è´£ä»»äººç”¨æˆ·ç¼–号
    ownerUserName?: string; // è´£ä»»äººæ˜µç§°
    bagQuantity?: number; // å·²ç”Ÿæˆè¢‹ç æ•°é‡
@@ -37,6 +34,7 @@
    remark?: string; // å¤‡æ³¨
    createTime?: string; // åˆ›å»ºæ—¶é—´
    workers?: BatchWorker[]; // ç”Ÿäº§äººå‘˜é…ç½®åˆ—表
    palletIds?: number[]; // ç»‘定托盘编号列表(仅新增/补录提交时传入,绑定时批次直接进入生产中)
  }
  /** ç”Ÿäº§æ‰¹æ¬¡æ‰«ç è¿½æº¯ */
@@ -68,10 +66,10 @@
  export interface PageParams extends PageParam {
    code?: string;
    tempCode?: string;
    itemId?: number;
    lineId?: number;
    lineCode?: string;
    lineName?: string;
    processingTeam?: string;
    ownerUserId?: number;
    status?: number;
    produceDate?: string[];
src/api/mes/pro/pallet/index.ts
@@ -12,7 +12,7 @@
    batchId?: number; // ç”Ÿäº§æ‰¹æ¬¡ç¼–号
    batchCode?: string; // æ‰¹æ¬¡å·
    palletNo?: number; // æ‰¹æ¬¡å†…托盘序号
    capacity?: number; // å¯è£…袋数(袋/托盘)
    autoGenerated?: boolean; // æ˜¯å¦è‡ªåŠ¨ç”Ÿæˆï¼ˆè¢‹ç æ•°æ®åŒ…ç”Ÿæˆæ—¶è‡ªåŠ¨åˆ›å»ºï¼‰
    bagCount?: number; // å·²ç»‘定袋码数量
    status?: number; // çŠ¶æ€ï¼ˆå­—å…¸ mes_pro_pallet_status)
    remark?: string; // å¤‡æ³¨
@@ -58,7 +58,6 @@
  export interface BatchCreateParams {
    batchId: number; // ç”Ÿäº§æ‰¹æ¬¡ç¼–号
    count: number; // ç”Ÿæˆæ•°é‡
    capacity?: number; // å¯è£…袋数
    remark?: string; // å¤‡æ³¨
  }
@@ -77,7 +76,6 @@
/** é¢„生成托盘临时二维码(不绑定生产批次) */
export function preGeneratePallet(data: {
  count: number;
  capacity?: number;
  remark?: string;
}) {
  return requestClient.post<MesProPalletApi.PreGenerateResult[]>(
@@ -118,6 +116,13 @@
  );
}
/** æŸ¥è¯¢æœªç»‘定批次的临时托盘列表(供批次新增/补录绑定托盘下拉使用) */
export function getUnboundPalletList() {
  return requestClient.get<MesProPalletApi.Pallet[]>(
    '/mes/pro/pallet/unbound-list',
  );
}
/** å¯¼å‡ºæ‰˜ç›˜ç  Excel */
export function exportPalletExcel(params: MesProPalletApi.PageParams) {
  return requestClient.download('/mes/pro/pallet/export-excel', { params });
src/views/basicData/mdm/components/data.ts
@@ -5,8 +5,6 @@
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { getItemTypeSimpleList } from '#/api/mes/md/item/type';
/** ç‰©æ–™é€‰æ‹©å¼¹çª—搜索表单 */
export function useMdmItemSelectGridFormSchema(): VbenFormSchema[] {
  return [
@@ -26,21 +24,6 @@
      componentProps: {
        allowClear: true,
        placeholder: '请输入品种名称',
      },
    },
    {
      fieldName: 'categoryId',
      label: '品种分类',
      component: 'ApiSelect',
      componentProps: {
        placeholder: '请选择品种分类',
        allowClear: true,
        api: async () => {
          const res = await getItemTypeSimpleList();
          return res || [];
        },
        labelField: 'name',
        valueField: 'id',
      },
    },
    {
@@ -78,13 +61,6 @@
      title: '规格型号',
      minWidth: 150,
      align: 'left',
    },
    {
      field: 'categoryId',
      title: '品种分类',
      minWidth: 120,
      align: 'center',
      slots: { default: 'categoryId' },
    },
    {
      field: 'unitMeasureName',
src/views/basicData/mdm/components/select-dialog.vue
@@ -1,7 +1,6 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MdmItemApi } from '#/api/mdm/item';
import type { MesMdItemTypeApi } from '#/api/mes/md/item/type';
import { nextTick, ref } from 'vue';
@@ -9,7 +8,6 @@
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getItemPage } from '#/api/mdm/item';
import { getItemTypeSimpleList } from '#/api/mes/md/item/type';
import {
  useMdmItemSelectGridColumns,
@@ -26,21 +24,6 @@
const multiple = ref(true); // æ˜¯å¦å¤šé€‰
const selectedRows = ref<MdmItemApi.Item[]>([]); // å·²é€‰ç‰©æ–™åˆ—表
const preSelectedIds = ref<number[]>([]); // é¢„选物料编号列表
const categoryList = ref<MesMdItemTypeApi.ItemType[]>([]); // åˆ†ç±»åˆ—表缓存
const TAG_COLORS = ['green', 'orange', 'blue', 'purple', 'cyan', 'magenta', 'geekblue', 'volcano', 'gold', 'lime'] as const;
function getCategoryName(categoryId?: number) {
  if (!categoryId || !categoryList.value.length) return '';
  const category = categoryList.value.find((item) => item.id === categoryId);
  return category?.name || '';
}
function getCategoryColor(categoryId?: number): string {
  if (!categoryId) return '';
  const index = categoryList.value.findIndex((item) => item.id === categoryId);
  return index >= 0 ? TAG_COLORS[index % TAG_COLORS.length]! : '';
}
/** èŽ·å–å½“å‰è¡¨æ ¼æ•°æ® */
function getTableRows() {
@@ -138,13 +121,6 @@
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          if (!categoryList.value.length) {
            try {
              categoryList.value = await getItemTypeSimpleList();
            } catch {
              categoryList.value = [];
            }
          }
          return await getItemPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
@@ -229,10 +205,6 @@
    @cancel="closeModal"
  >
    <Grid table-title="品种列表">
      <template #categoryId="{ row }">
        <Tag v-if="getCategoryColor(row.categoryId)" :color="getCategoryColor(row.categoryId)">{{ getCategoryName(row.categoryId) }}</Tag>
        <span v-else>{{ getCategoryName(row.categoryId) || '-' }}</span>
      </template>
      <template #isBatchManaged="{ row }">
        <Tag v-if="row.isBatchManaged" color="success">是</Tag>
        <Tag v-else>否</Tag>
src/views/basicData/mdm/data.ts
@@ -9,7 +9,6 @@
import { z } from '#/adapter/form';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { MesAutoCodeRuleCode } from '@vben/constants';
import { getItemTypeSimpleList } from '#/api/mes/md/item/type';
import { getUnitPage } from '#/api/mdm/unit';
import { getWarehouseSimpleList } from '#/api/mes/wm/warehouse';
@@ -22,24 +21,6 @@
// ç¼“存仓库列表
let warehouseListCache: any[] | null = null;
let warehouseListPromise: Promise<any[]> | null = null;
/** å°†æ‰å¹³åˆ†ç±»åˆ—表转为 TreeSelect æ‰€éœ€çš„æ ‘形结构 */
export function buildCategoryTree(list: any[]): any[] {
  const map = new Map<number, any>();
  const roots: any[] = [];
  for (const item of list) {
    map.set(item.id, { label: item.name, value: item.id, children: [] });
  }
  for (const item of list) {
    const node = map.get(item.id)!;
    if (item.parentId && map.has(item.parentId)) {
      map.get(item.parentId)!.children.push(node);
    } else {
      roots.push(node);
    }
  }
  return roots;
}
/** èŽ·å–å•ä½åˆ—è¡¨ï¼ˆå¸¦ç¼“å­˜ï¼‰ */
async function getUnitListCached() {
@@ -82,7 +63,7 @@
}
/** æ–°å¢ž/修改的表单 */
export function useFormSchema(formApi?: VbenFormApi, categoryTreeData: any[] = []): VbenFormSchema[] {
export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
  return [
    {
      component: "Input",
@@ -124,19 +105,6 @@
        placeholder: "请输入品种名称",
      },
      rules: "required",
    },
    {
      component: "TreeSelect",
      fieldName: "categoryId",
      label: "品种分类",
      rules: "selectRequired",
      componentProps: {
        placeholder: "请选择品种分类",
        allowClear: true,
        treeDefaultExpandAll: true,
        treeData: categoryTreeData,
        fieldNames: { label: 'label', value: 'value', children: 'children' },
      },
    },
    {
      component: "Input",
@@ -242,17 +210,6 @@
      label: "条码",
      componentProps: {
        placeholder: "请输入条码",
      },
    },
    {
      component: "InputNumber",
      fieldName: "purchasePrice",
      label: "采购价",
      componentProps: {
        class: "!w-full",
        min: 0,
        precision: 2,
        placeholder: "请输入采购价",
      },
    },
    {
@@ -541,12 +498,6 @@
      minWidth: 150,
    },
    {
      field: "categoryName",
      title: "品种分类",
      minWidth: 120,
      slots: { default: "categoryName" },
    },
    {
      field: "unitMeasureName",
      title: "单位",
      minWidth: 80,
@@ -575,14 +526,6 @@
      field: "warehouseName",
      title: "默认仓库",
      minWidth: 100,
    },
    {
      field: "purchasePrice",
      title: "采购价",
      minWidth: 100,
      formatter: ({ cellValue }) => {
        return cellValue != null ? `Â¥${Number(cellValue).toFixed(2)}` : "";
      },
    },
    {
      field: "salesPrice",
src/views/basicData/mdm/index.vue
@@ -1,9 +1,8 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MdmItemApi } from '#/api/mdm/item';
import type { MesMdItemTypeApi } from '#/api/mes/md/item/type';
import { computed, ref } from 'vue';
import { ref } from 'vue';
import { confirm, Page, useVbenModal } from '#/packages/effects/common-ui/src';
import { downloadFileFromBlobPart, isEmpty } from '#/packages/utils/src';
@@ -19,7 +18,6 @@
  updateItemStatus,
  updateItemSync,
} from '#/api/mdm/item';
import { getItemTypeSimpleList } from '#/api/mes/md/item/type';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
@@ -31,44 +29,6 @@
  connectedComponent: Form,
  destroyOnClose: true,
});
// åˆ†ç±»åˆ—表缓存
const categoryList = ref<MesMdItemTypeApi.ItemType[]>([]);
const TAG_COLORS = [
  'green',
  'orange',
  'blue',
  'purple',
  'cyan',
  'magenta',
  'geekblue',
  'volcano',
  'gold',
  'lime',
] as const;
/** åˆ†ç±»ID â†’ é¢œè‰²æ˜ å°„(基于分类在列表中的索引循环分配) */
const categoryColorMap = computed(() => {
  const map = new Map<number, string>();
  categoryList.value.forEach((item, index) => {
    map.set(item.id!, TAG_COLORS[index % TAG_COLORS.length]!);
  });
  return map;
});
/** èŽ·å–åˆ†ç±»Tag颜色 */
function getCategoryColor(categoryId?: number): string {
  if (!categoryId) return '';
  return categoryColorMap.value.get(categoryId) || '';
}
/** èŽ·å–åˆ†ç±»åç§° */
function getCategoryName(categoryId?: number) {
  if (!categoryId || !categoryList.value.length) return '';
  const category = categoryList.value.find((item) => item.id === categoryId);
  return category?.name || '';
}
/** åˆ·æ–°è¡¨æ ¼ */
function handleRefresh() {
@@ -176,14 +136,6 @@
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          // åŠ è½½åˆ†ç±»åˆ—è¡¨ï¼ˆç”¨äºŽæ˜¾ç¤ºåˆ†ç±»åç§°ï¼‰
          if (!categoryList.value.length) {
            try {
              categoryList.value = await getItemTypeSimpleList();
            } catch {
              categoryList.value = [];
            }
          }
          return await getItemPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
@@ -240,10 +192,6 @@
            },
          ]"
        />
      </template>
      <template #categoryName="{ row }">
        <Tag v-if="getCategoryColor(row.categoryId)" :color="getCategoryColor(row.categoryId)">{{ getCategoryName(row.categoryId) }}</Tag>
        <span v-else>{{ getCategoryName(row.categoryId) }}</span>
      </template>
      <template #isBatchManaged="{ row }">
        <Tag v-if="row.isBatchManaged" color="success">是</Tag>
src/views/basicData/mdm/modules/form.vue
@@ -11,8 +11,7 @@
import { createItem, getItem, updateItem } from '#/api/mdm/item';
import { $t } from '#/locales';
import { useFormSchema, clearUnitListCache, buildCategoryTree } from '../data';
import { getItemTypeSimpleList } from '#/api/mes/md/item/type';
import { useFormSchema, clearUnitListCache } from '../data';
defineOptions({ name: "ItemForm" });
@@ -57,11 +56,8 @@
    const data = modalApi.getData<MdmItemApi.Item>();
    // æ¸…除单位缓存,确保获取最新数据
    clearUnitListCache();
    // åŠ è½½æœ€æ–°åˆ†ç±»æ•°æ®
    const categoryRes = await getItemTypeSimpleList();
    const categoryTreeData = buildCategoryTree(categoryRes || []);
    // è®¾ç½® schema(formApi å·²åˆå§‹åŒ–)
    formApi.setState({ schema: useFormSchema(formApi, categoryTreeData) });
    formApi.setState({ schema: useFormSchema(formApi) });
    if (data?.id) {
      // ç¼–辑模式
      modalApi.lock();
src/views/basicData/mdm/unit/data.ts
@@ -6,6 +6,13 @@
import { z } from '#/adapter/form';
/** è®¡é‡å•位类型选项 */
export const UNIT_TYPE_OPTIONS = [
  { label: '48千克', value: '48千克' },
  { label: '100千克', value: '100千克' },
  { label: '吨', value: '吨' },
];
/** æ–°å¢ž/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
  return [
@@ -33,6 +40,17 @@
      rules: 'required',
      componentProps: {
        placeholder: '请输入单位名称',
      },
    },
    {
      fieldName: 'type',
      label: '类型',
      component: 'Select',
      rules: 'required',
      componentProps: {
        placeholder: '请选择类型',
        allowClear: true,
        options: UNIT_TYPE_OPTIONS,
      },
    },
    {
@@ -107,6 +125,12 @@
      minWidth: 150,
    },
    {
      field: 'type',
      title: '类型',
      minWidth: 100,
      formatter: ({ cellValue }) => cellValue || '-',
    },
    {
      field: 'status',
      title: '状态',
      minWidth: 80,
src/views/erp/purchase/order/modules/item-form.vue
@@ -159,7 +159,7 @@
  row.productUnitId = product.unitMeasureId;
  row.productUnitName = product.unitMeasureName;
  row.productName = product.name;
  row.productPrice = product.purchasePrice || 0;
  row.productPrice = 0; // å“ç§ç®¡ç†å·²åŽ»æŽ‰é‡‡è´­ä»·ï¼Œé‡‘é¢éœ€æ‰‹å·¥å¡«å†™
  row.count = row.count || 1;
  handleRowChange(row);
}
src/views/erp/purchase/request/modules/item-form.vue
@@ -121,7 +121,7 @@
  row.productUnitId = product.unitMeasureId;
  row.productUnitName = product.unitMeasureName;
  row.productName = product.name;
  row.productPrice = product.purchasePrice || 0;
  row.productPrice = 0; // å“ç§ç®¡ç†å·²åŽ»æŽ‰é‡‡è´­ä»·ï¼Œé‡‘é¢éœ€æ‰‹å·¥å¡«å†™
  row.count = row.count || 1;
  handleRowChange(row);
}
src/views/mes/pro/bagCode/data.ts
@@ -2,8 +2,12 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProBagCodeApi } from '#/api/mes/pro/bagCode';
import { markRaw } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { MdmItemSelect } from '#/views/basicData/mdm/components';
import { getRangePickerDefaultProps } from '#/utils';
@@ -35,6 +39,14 @@
      componentProps: {
        allowClear: true,
        placeholder: '请输入生成组编号',
      },
    },
    {
      fieldName: 'itemId',
      label: '品种',
      component: markRaw(MdmItemSelect),
      componentProps: {
        placeholder: '请选择品种(产品品种)',
      },
    },
    {
@@ -85,6 +97,16 @@
      minWidth: 190,
    },
    {
      field: 'itemCode',
      title: '品种编码',
      width: 120,
    },
    {
      field: 'itemName',
      title: '品种名称',
      width: 140,
    },
    {
      field: 'lineCode',
      title: '产线编码',
      width: 100,
src/views/mes/pro/bagCode/index.vue
@@ -23,6 +23,7 @@
  import BindBatchModal from "./modules/bind-batch.vue";
  import BindPalletModal from "./modules/bind-pallet.vue";
  import GenerateModal from "./modules/generate-form.vue";
  import RawGenerateModal from "./modules/raw-generate-form.vue";
  import ReplaceModal from "./modules/replace-form.vue";
  import QrTraceModal from "../components/qr-trace-modal.vue";
@@ -30,6 +31,11 @@
  const [GenerateFormModal, generateFormModalApi] = useVbenModal({
    connectedComponent: GenerateModal,
    destroyOnClose: true,
  });
  const [RawGenerateFormModal, rawGenerateFormModalApi] = useVbenModal({
    connectedComponent: RawGenerateModal,
    destroyOnClose: true,
  });
@@ -61,6 +67,11 @@
  /** ç”Ÿæˆæ•°æ®åŒ… */
  function handleGenerate() {
    generateFormModalApi.open();
  }
  /** ç”ŸæˆåŽŸå§‹è¢‹ç  */
  function handleRawGenerate() {
    rawGenerateFormModalApi.open();
  }
  /** æ›¿æ¢æˆåŠŸåŽå±•ç¤ºç»“æžœå¹¶åˆ·æ–°åˆ—è¡¨ */
@@ -303,6 +314,7 @@
<template>
  <Page auto-content-height>
    <GenerateFormModal @success="handleRefresh" />
    <RawGenerateFormModal @success="handleRefresh" />
    <BindBatchFormModal @success="handleRefresh" />
    <BindPalletFormModal @success="handleRefresh" />
    <ReplaceFormModal @success="handleReplaceSuccess" />
@@ -318,6 +330,12 @@
              onClick: handleGenerate,
            },
            {
              label: '生成原始袋码',
              icon: ACTION_ICON.ADD,
              auth: ['mes:pro-bag-code:create'],
              onClick: handleRawGenerate,
            },
            {
              label: '录入批次',
              type: 'primary',
              auth: ['mes:pro-bag-code:update'],
src/views/mes/pro/bagCode/modules/bind-pallet.vue
@@ -97,9 +97,7 @@
            (pallet) => pallet.status !== MesProPalletStatusEnum.CANCELED
          )
          .map((pallet) => ({
            label: `${pallet.code}(已装 ${pallet.bagCount ?? 0} è¢‹ / å¯è£… ${
              pallet.capacity ?? "不限"
            } è¢‹ï¼‰`,
            label: `${pallet.code}(已装 ${pallet.bagCount ?? 0} è¢‹ï¼‰`,
            value: pallet.id!,
          }));
        await formApi.setState({ schema: buildSchema(options) });
src/views/mes/pro/bagCode/modules/generate-form.vue
@@ -1,6 +1,9 @@
<script lang="ts" setup>
  import type { VbenFormSchema } from "#/adapter/form";
  import type { MdmItemApi } from "#/api/mdm/item";
  import type { MesProBagCodeApi } from "#/api/mes/pro/bagCode";
  import { computed, markRaw, ref } from "vue";
  import { useVbenModal } from "@vben/common-ui";
@@ -8,26 +11,91 @@
  import { useVbenForm } from "#/adapter/form";
  import { generateBagCodes } from "#/api/mes/pro/bagCode";
  import { getUnit } from "#/api/mdm/unit";
  import { MdmItemSelect } from "#/views/basicData/mdm/components";
  defineOptions({ name: "MesProBagCodeGenerateForm" });
  const emit = defineEmits(["success"]);
  // æ‰€é€‰å“ç§ä¸»è®¡é‡å•位类型:48千克/100千克 -> æ¯æ‰˜ N è¢‹ï¼›å¨ -> æ— æ‰˜ç›˜ç›´æŽ¥è¾“袋码数
  const unitType = ref("");
  const kgPerPallet = ref<number | null>(null);
  const isTon = ref(false);
  const unitTypeError = ref(false);
  const palletCountVal = ref<number | null>(null);
  const bagCountVal = ref<number | null>(null);
  /** æ¢ç®—/配置提示文案 */
  const tipText = computed(() => {
    if (unitTypeError.value) {
      return "该品种主计量单位未配置类型(仅支持 48千克/100千克/吨),请先前往「计量单位」维护后重试";
    }
    if (!unitType.value) {
      return "";
    }
    if (isTon.value) {
      return `计量单位类型为「吨」:不生成托盘,袋码仅绑定生产批次${
        bagCountVal.value ? `,将生成 ${bagCountVal.value} ä¸ªè¢‹ç ` : ""
      }`;
    }
    const per = kgPerPallet.value ?? 0;
    const count = palletCountVal.value ?? 0;
    return `计量单位类型为「${unitType.value}」:每托 ${per} è¢‹${
      count > 0
        ? ` Ã— ${count} æ‰˜ = ${per * count} ä¸ªè¢‹ç ï¼Œå°†è‡ªåŠ¨ç”Ÿæˆ ${count} ä¸ªæ‰˜ç›˜å¹¶ç»‘定`
        : ",请输入托盘数"
    }`;
  });
  function buildSchema(): VbenFormSchema[] {
    return [
    const schema: VbenFormSchema[] = [
      {
        fieldName: "count",
        label: "生成袋数",
        fieldName: "itemId",
        label: "品种",
        component: markRaw(MdmItemSelect),
        componentProps: {
          placeholder: "请选择品种(产品品种)",
          onChange: handleItemChange,
        },
        rules: "selectRequired",
      },
    ];
    if (kgPerPallet.value) {
      schema.push({
        fieldName: "palletCount",
        label: "托盘数",
        component: "InputNumber",
        componentProps: {
          class: "!w-full",
          min: 1,
          placeholder: "请输入需要生成的袋码数量",
          placeholder: "请输入托盘数(系统自动生成托盘并均分绑定袋码)",
          precision: 0,
          onChange: (value: number | string) => {
            palletCountVal.value = Number(value) || null;
          },
        },
        rules: "required",
      });
    }
    if (isTon.value) {
      schema.push({
        fieldName: "bagCount",
        label: "袋码数",
        component: "InputNumber",
        componentProps: {
          class: "!w-full",
          min: 1,
          placeholder: "请输入袋码数(吨品种无托盘,袋码仅绑定批次)",
          precision: 0,
          onChange: (value: number | string) => {
            bagCountVal.value = Number(value) || null;
      },
      {
        },
        rules: "required",
      });
    }
    schema.push({
        fieldName: "remark",
        label: "备注",
        component: "Textarea",
@@ -35,8 +103,40 @@
          placeholder: "请输入备注",
          rows: 2,
        },
      },
    ];
    });
    return schema;
  }
  /** é€‰æ‹©å“ç§åŽè¯»å–主计量单位类型,动态切换托盘数/袋码数输入 */
  async function handleItemChange(item?: MdmItemApi.Item) {
    unitType.value = "";
    kgPerPallet.value = null;
    isTon.value = false;
    unitTypeError.value = false;
    palletCountVal.value = null;
    bagCountVal.value = null;
    if (item?.unitMeasureId) {
      try {
        const unit = await getUnit(item.unitMeasureId);
        const type = (unit?.type ?? "").trim();
        unitType.value = type;
        if (type === "吨") {
          isTon.value = true;
        } else {
          const matcher = /^(\d{1,5})千克$/.exec(type);
          if (matcher) {
            kgPerPallet.value = Number(matcher[1]);
          } else {
            unitTypeError.value = true;
          }
        }
      } catch {
        unitTypeError.value = true;
      }
    } else if (item) {
      unitTypeError.value = true;
    }
    await formApi.setState({ schema: buildSchema() });
  }
  const [Form, formApi] = useVbenForm({
@@ -55,12 +155,30 @@
    async onConfirm() {
      const { valid } = await formApi.validate();
      if (!valid) return;
      if (unitTypeError.value) {
        message.warning(tipText.value);
        return;
      }
      modalApi.lock();
      try {
        const values =
          (await formApi.getValues()) as MesProBagCodeApi.GenerateParams;
        await generateBagCodes(values);
        message.success(`已成功生成 ${values.count} æ¡è¢‹ç `);
        const data: MesProBagCodeApi.GenerateParams = {
          itemId: values.itemId,
          remark: values.remark,
        };
        if (isTon.value) {
          data.bagCount = values.bagCount;
        } else {
          data.palletCount = values.palletCount;
        }
        const result = await generateBagCodes(data);
        message.success(
          `已成功生成 ${result.totalCount} æ¡è¢‹ç ` +
            (result.palletIds?.length
              ? `,并自动生成 ${result.palletIds.length} ä¸ªæ‰˜ç›˜`
              : ""),
        );
        emit("success");
        await modalApi.close();
      } finally {
@@ -71,6 +189,18 @@
      if (!isOpen) return;
      modalApi.lock();
      try {
        unitType.value = "";
        kgPerPallet.value = null;
        isTon.value = false;
        unitTypeError.value = false;
        palletCountVal.value = null;
        bagCountVal.value = null;
        await formApi.setValues({
          itemId: undefined,
          palletCount: undefined,
          bagCount: undefined,
          remark: undefined,
        });
        await formApi.setState({ schema: buildSchema() });
      } finally {
        modalApi.unlock();
@@ -82,5 +212,12 @@
<template>
  <Modal title="生成袋码数据包" class="w-2/5">
    <Form class="mx-4" />
    <div
      v-if="tipText"
      class="mx-4 mb-2 rounded bg-gray-50 px-3 py-2 text-sm"
      :class="unitTypeError ? 'text-red-500' : 'text-gray-500'"
    >
      {{ tipText }}
    </div>
  </Modal>
</template>
src/views/mes/pro/bagCode/modules/raw-generate-form.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,100 @@
<script lang="ts" setup>
  import type { VbenFormSchema } from "#/adapter/form";
  import { markRaw } from "vue";
  import { useVbenModal } from "@vben/common-ui";
  import { message } from "ant-design-vue";
  import { useVbenForm } from "#/adapter/form";
  import { generateRawBagCodes } from "#/api/mes/pro/bagCode";
  import { MdmItemSelect } from "#/views/basicData/mdm/components";
  defineOptions({ name: "MesProBagCodeRawGenerateForm" });
  const emit = defineEmits(["success"]);
  const schema: VbenFormSchema[] = [
    {
      fieldName: "itemId",
      label: "品种",
      component: markRaw(MdmItemSelect),
      componentProps: {
        placeholder: "请选择品种(产品品种)",
      },
      rules: "selectRequired",
    },
    {
      fieldName: "count",
      label: "袋码数量",
      component: "InputNumber",
      componentProps: {
        class: "!w-full",
        min: 1,
        placeholder: "请输入需要生成的袋码数量",
        precision: 0,
      },
      rules: "required",
    },
    {
      fieldName: "remark",
      label: "备注",
      component: "Textarea",
      componentProps: {
        placeholder: "请输入备注",
        rows: 2,
      },
    },
  ];
  const [Form, formApi] = useVbenForm({
    commonConfig: {
      componentProps: {
        class: "w-full",
      },
      labelWidth: 100,
    },
    layout: "horizontal",
    schema,
    showDefaultActions: false,
  });
  const [Modal, modalApi] = useVbenModal({
    async onConfirm() {
      const { valid } = await formApi.validate();
      if (!valid) return;
      modalApi.lock();
      try {
        const values = await formApi.getValues();
        const result = await generateRawBagCodes({
          itemId: values.itemId,
          count: values.count,
          remark: values.remark,
        });
        message.success(`已成功生成 ${result.totalCount} æ¡åŽŸå§‹è¢‹ç `);
        emit("success");
        await modalApi.close();
      } finally {
        modalApi.unlock();
      }
    },
    async onOpenChange(isOpen: boolean) {
      if (!isOpen) return;
      await formApi.setValues({
        itemId: undefined,
        count: undefined,
        remark: undefined,
      });
    },
  });
</script>
<template>
  <Modal title="生成原始袋码" class="w-2/5">
    <Form class="mx-4" />
    <div class="mx-4 mb-2 rounded bg-gray-50 px-3 py-2 text-sm text-gray-500">
      åŽŸå§‹è¢‹ç ä»…éœ€é€‰æ‹©å“ç§å¹¶è¾“å…¥æ•°é‡ï¼Œä¸æ¢ç®—è®¡é‡å•ä½ç±»åž‹ã€ä¸ç”Ÿæˆæ‰˜ç›˜ï¼›åŽç»­å¯æ­£å¸¸å½•å…¥æ‰¹æ¬¡æˆ–ç»‘å®šæ‰˜ç›˜
    </div>
  </Modal>
</template>
src/views/mes/pro/batch/data.ts
@@ -7,7 +7,6 @@
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { MdmItemSelect } from '#/views/basicData/mdm/components';
import { ProLineSelect } from '#/views/mes/pro/line/components';
import { UserSelect } from '#/views/system/user/components';
import { getRangePickerDefaultProps } from '#/utils';
@@ -15,8 +14,17 @@
/** è¡¨å•类型(fill:临时批次码补录正式信息) */
export type FormType = 'create' | 'detail' | 'update' | 'fill';
/** æœªç»‘定托盘下拉选项 */
export interface PalletOption {
  label: string;
  value: number;
}
/** æ–°å¢ž/修改的表单 */
export function useFormSchema(formType: FormType): VbenFormSchema[] {
export function useFormSchema(
  formType: FormType,
  palletOptions: PalletOption[] = [],
): VbenFormSchema[] {
  const disabled = formType === 'detail';
  return [
    {
@@ -67,24 +75,25 @@
      },
    },
    {
      fieldName: 'itemId',
      label: '品种',
      component: markRaw(MdmItemSelect),
      fieldName: 'lineId',
      label: '加工单位',
      component: markRaw(ProLineSelect),
      componentProps: {
        disabled,
        placeholder: '请选择品种(产品品种)',
        placeholder: '请选择加工单位',
      },
      rules: 'selectRequired',
    },
    {
      fieldName: 'lineId',
      label: '产线',
      component: markRaw(ProLineSelect),
      fieldName: 'processingTeam',
      label: '加工班组',
      component: 'Input',
      componentProps: {
        allowClear: true,
        disabled,
        placeholder: '请选择产线',
        maxlength: 64,
        placeholder: '请输入加工班组',
      },
      rules: 'selectRequired',
    },
    {
      fieldName: 'produceDate',
@@ -102,13 +111,13 @@
    },
    {
      fieldName: 'planQuantity',
      label: '计划数量',
      label: '加工数量',
      component: 'InputNumber',
      componentProps: {
        class: '!w-full',
        disabled,
        min: 0,
        placeholder: '请输入计划数量',
        placeholder: '请输入加工数量',
        precision: 2,
      },
    },
@@ -121,6 +130,26 @@
        placeholder: '请选择责任人',
      },
      rules: 'selectRequired',
    },
    {
      fieldName: 'palletIds',
      label: '绑定托盘',
      component: 'Select',
      componentProps: {
        allowClear: true,
        disabled,
        maxTagCount: 3,
        mode: 'multiple',
        optionFilterProp: 'label',
        options: palletOptions,
        placeholder: '请选择托盘(可不选)',
        showSearch: true,
      },
      dependencies: {
        triggerFields: [''],
        show: () => formType !== 'detail',
      },
      help: '绑定后批次直接进入生产中:托盘生成正式托盘码,托盘下未录批次的袋码自动级联录入本批次(袋码品种须一致);编辑时仅需勾选新增托盘,已绑定的不可解绑',
    },
    {
      fieldName: 'remark',
@@ -149,29 +178,21 @@
      },
    },
    {
      fieldName: 'itemId',
      label: '品种',
      component: markRaw(MdmItemSelect),
      componentProps: {
        placeholder: '请选择品种',
      },
    },
    {
      fieldName: 'lineCode',
      label: '产线编码',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入产线编码',
      },
    },
    {
      fieldName: 'lineName',
      label: '产线名称',
      label: '加工单位',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入产线名称',
        placeholder: '请输入加工单位',
      },
    },
    {
      fieldName: 'processingTeam',
      label: '加工班组',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入加工班组',
      },
    },
    {
@@ -224,23 +245,13 @@
      slots: { default: 'code' },
    },
    {
      field: 'itemCode',
      title: '品种编码',
      field: 'lineName',
      title: '加工单位',
      width: 120,
    },
    {
      field: 'itemName',
      title: '品种名称',
      width: 140,
    },
    {
      field: 'lineCode',
      title: '产线编码',
      width: 100,
    },
    {
      field: 'lineName',
      title: '产线名称',
      field: 'processingTeam',
      title: '加工班组',
      width: 120,
    },
    {
@@ -251,7 +262,7 @@
    },
    {
      field: 'planQuantity',
      title: '计划数量',
      title: '加工数量',
      width: 100,
    },
    {
src/views/mes/pro/batch/index.vue
@@ -67,8 +67,9 @@
    });
  }
  /** é¢„生成批次二维码成功:展示生成的临时批次码 */
  /** é¢„生成批次二维码成功:刷新列表并展示生成的临时批次码 */
  function handlePreGenerateSuccess(result: { id: number; tempCode: string }[]) {
    handleRefresh();
    Modal.success({
      title: "预生成临时批次码成功",
      content: result.map((item) => item.tempCode).join("\n"),
src/views/mes/pro/batch/modules/form.vue
@@ -1,5 +1,5 @@
<script lang="ts" setup>
  import type { FormType } from "../data";
  import type { FormType, PalletOption } from "../data";
  import type { VxeTableGridOptions } from "#/adapter/vxe-table";
  import type { MesProBatchApi } from "#/api/mes/pro/batch";
@@ -21,6 +21,7 @@
    getBatch,
    updateBatch,
  } from "#/api/mes/pro/batch";
  import { getUnboundPalletList } from "#/api/mes/pro/pallet";
  import { $t } from "#/locales";
  import { UserSelect } from "#/views/system/user/components";
@@ -206,7 +207,20 @@
      }
      const data = modalApi.getData<{ formType: FormType; id?: number }>();
      formType.value = data.formType;
      formApi.setState({ schema: useFormSchema(formType.value) });
      // æ–°å¢ž/补录/编辑态加载未绑定批次的托盘,供「绑定托盘」多选下拉使用
      let palletOptions: PalletOption[] = [];
      if (formType.value !== "detail") {
        const pallets = await getUnboundPalletList();
        palletOptions = pallets.map((pallet) => ({
          label: `${pallet.tempCode ?? `托盘${pallet.id}`}(已装 ${pallet.bagCount ?? 0} è¢‹ï¼‰`,
          value: pallet.id as number,
        }));
      }
      formApi.setState({ schema: useFormSchema(formType.value, palletOptions) });
      if (formType.value !== "detail") {
        // æ¸…空上次打开时勾选的托盘,避免残留值误触发「批次进入生产中」
        await formApi.setFieldValue("palletIds", undefined);
      }
      // æ ¹æ®è¡¨å•态切换人员子表列
      workerGridApi.setGridOptions({
        columns: isEditable.value
src/views/mes/pro/pallet/data.ts
@@ -73,11 +73,6 @@
      slots: { default: 'batchCode' },
    },
    {
      field: 'capacity',
      title: '可装袋数(袋/托盘)',
      width: 150,
    },
    {
      field: 'bagCount',
      title: '已绑袋码数量',
      width: 120,
src/views/mes/pro/pallet/index.vue
@@ -49,8 +49,9 @@
    generateFormModalApi.open();
  }
  /** é¢„生成成功:展示生成的临时托盘码 */
  /** é¢„生成成功:刷新列表并展示生成的临时托盘码 */
  function handlePreGenerateSuccess(result: { id: number; tempCode: string }[]) {
    handleRefresh();
    Modal.success({
      title: "预生成临时托盘码成功",
      content: result.map((item) => item.tempCode).join("\n"),
@@ -216,6 +217,7 @@
      <template #code="{ row }">
        <span>{{ getDisplayCode(row) }}</span>
        <Tag v-if="!row.code" color="warning">未绑定批次</Tag>
        <Tag v-if="row.autoGenerated" color="processing">自动生成</Tag>
      </template>
      <template #batchCode="{ row }">
        <span v-if="row.batchCode">{{ row.batchCode }}</span>
src/views/mes/pro/pallet/modules/bind-batch.vue
@@ -84,5 +84,8 @@
<template>
  <Modal title="托盘码绑定生产批次" class="w-2/5">
    <Form class="mx-4" />
    <div class="mx-4 mb-2 rounded bg-gray-50 px-3 py-2 text-sm text-gray-500">
      ç»‘定后,托盘下未录入批次的袋码将自动随托盘绑定该生产批次(袋码品种须一致)
    </div>
  </Modal>
</template>
src/views/mes/pro/pallet/modules/generate-form.vue
@@ -33,7 +33,6 @@
        const values = await formApi.getValues();
        const result = await preGeneratePallet({
          count: values.count,
          capacity: values.capacity,
          remark: values.remark,
        });
        message.success(`已生成 ${result.length} ä¸ªä¸´æ—¶æ‰˜ç›˜ç `);
@@ -57,17 +56,6 @@
            precision: 0,
          },
          rules: "required",
        },
        {
          fieldName: "capacity",
          label: "可装袋数",
          component: "InputNumber",
          componentProps: {
            class: "!w-full",
            min: 1,
            placeholder: "请输入每托盘可装袋数(袋/托盘)",
            precision: 0,
          },
        },
        {
          fieldName: "remark",
src/views/wls/barcode/components/detail.vue
@@ -130,8 +130,9 @@
      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] || '-';
      // å–值与后端 BarcodeBizTypeEnum(MesBizTypeConstants ç¼–号)保持一致
      const map: Record<number, string> = { 102: '仓库', 103: '库区', 104: '库位', 105: '装箱单', 106: '库存', 107: '批次', 109: 'SN ç ', 300: '流转卡', 301: '工单', 302: '流转单', 400: '设备', 500: '工具', 600: '物料', 601: '供应商', 602: '工作站', 603: '车间', 604: '人员', 605: '客户' };
      return map[val as number] || '-';
    };
    const getStatusText = (val?: number) => val === 0 ? '禁用' : '启用';
src/views/wls/transaction/data.ts
@@ -13,9 +13,12 @@
/** ä¸šåŠ¡ç±»åž‹é€‰é¡¹ */
const bizTypeOptions = [
  { label: '种子质检', value: 5 },
  { label: '盘点调整', value: 107 },
  { label: '自定义入库', value: 126 },
  { label: '自定义出库', value: 127 },
  { label: '采购入库', value: 110 },
  { label: '采购入库取消', value: 111 },
  { label: '销售出库', value: 119 },
  { label: '调拨出库', value: 112 },
  { label: '调拨入库', value: 113 },