已修改25个文件
517 ■■■■■ 文件已修改
src/api/erp/sale/order/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/pro/route/process/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/wm/salesnotice/index.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/mdm/components/data.ts 82 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/mdm/components/select-dialog.vue 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/mdm/index.vue 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/saleQuotation/data.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/saleQuotation/modules/item-form.vue 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/sale/order/data.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/sale/order/index.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/sale/order/modules/item-form.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/data.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/task/modules/schedule-form.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/workorder/data.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/process-design/route/data.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/process-design/route/modules/process-list.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/qc/oqc/data.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/productsales/data.ts 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/returnsales/data.ts 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/salesnotice/components/select-dialog.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/salesnotice/components/select.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/salesnotice/data.ts 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/salesnotice/index.vue 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/salesnotice/modules/form.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/transfer/data.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/erp/sale/order/index.ts
@@ -16,6 +16,7 @@
    status: number; // 状态
    remark: string; // 备注
    outCount: number; // 销售出库数量
    outStatus?: number; // 出库状态:0-未出库,1-部分出库,2-全部出库
    fileUrl?: string; // 附件地址
    inCount?: number; // 采购入库数量
    returnCount: number; // 销售退货数量
@@ -26,6 +27,8 @@
    contractId?: number; // 关联 CRM 合同编号
    contractNo?: string; // CRM 合同单号
    needProduction?: number; // 是否需要生产:0-不需要,1-需要
    hasSalesNotice?: boolean; // 是否已生成发货通知单
    canCreateSalesNotice?: boolean; // 是否可以生成发货通知单
    items?: SaleOrderItem[]; // 销售订单产品明细列表
  }
src/api/mes/pro/route/process/index.ts
@@ -18,6 +18,9 @@
    keyFlag?: boolean;
    checkFlag?: boolean;
    backflushFlag?: boolean;
    outputItemId?: number;
    outputItemCode?: string;
    outputItemName?: string;
    remark?: string;
    createTime?: Date;
  }
src/api/mes/wm/salesnotice/index.ts
@@ -9,7 +9,7 @@
    code?: string; // 通知单编号
    name?: string; // 通知单名称
    saleOrderId?: number; // 销售订单编号
    salesOrderCode?: string; // 销售订单编号
    saleOrderCode?: string; // 销售订单编号
    clientId?: number; // 客户编号
    clientCode?: string; // 客户编码
    clientName?: string; // 客户名称
src/views/basicData/mdm/components/data.ts
@@ -2,8 +2,10 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MdmItemApi } from '#/api/mdm/item';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { getItemTypeSimpleList } from '#/api/mes/md/item/type';
/** 物料选择弹窗搜索表单 */
export function useMdmItemSelectGridFormSchema(): VbenFormSchema[] {
@@ -27,30 +29,32 @@
      },
    },
    {
      fieldName: 'itemType',
      label: '物料类型',
      fieldName: 'categoryId',
      label: '物料分类',
      component: 'ApiSelect',
      componentProps: {
        placeholder: '请选择物料分类',
        allowClear: true,
        api: async () => {
          const res = await getItemTypeSimpleList();
          return res || [];
        },
        labelField: 'name',
        valueField: 'id',
      },
    },
    {
      fieldName: 'status',
      label: '状态',
      component: 'Select',
      componentProps: {
        placeholder: '请选择状态',
        allowClear: true,
        placeholder: '请选择物料类型',
        options: [
          { label: '原料', value: 1 },
          { label: '半成品', value: 2 },
          { label: '成品', value: 3 },
          { label: '辅料', value: 4 },
        ],
        options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
      },
    },
  ];
}
/** 物料类型颜色映射 */
const itemTypeMap: Record<number, { label: string; color: string }> = {
  1: { label: '原料', color: 'green' },
  2: { label: '半成品', color: 'orange' },
  3: { label: '成品', color: 'blue' },
  4: { label: '辅料', color: 'purple' },
};
/** 物料选择弹窗列表字段 */
export function useMdmItemSelectGridColumns(
@@ -61,51 +65,45 @@
    {
      field: 'code',
      title: '物料编码',
      width: 180,
      minWidth: 120,
    },
    {
      field: 'name',
      title: '物料名称',
      minWidth: 160,
      minWidth: 180,
      align: 'left',
    },
    {
      field: 'specification',
      title: '规格型号',
      minWidth: 140,
      minWidth: 150,
      align: 'left',
    },
    {
      field: 'categoryId',
      title: '物料分类',
      minWidth: 120,
      align: 'center',
      slots: { default: 'categoryId' },
    },
    {
      field: 'unitMeasureName',
      title: '单位',
      width: 90,
      minWidth: 80,
      align: 'center',
    },
    {
      field: 'itemType',
      title: '物料类型',
      width: 110,
      align: 'center',
      formatter: ({ cellValue }: { cellValue: number }) => {
        const type = itemTypeMap[cellValue];
        return type ? type.label : '-';
      },
    },
    {
      field: 'isBatchManaged',
      title: '批次管理',
      width: 110,
      minWidth: 80,
      align: 'center',
      cellRender: {
        name: 'CellDict',
        props: { type: 'infra_boolean_string' },
      },
      slots: { default: 'isBatchManaged' },
    },
    {
      field: 'createTime',
      title: '创建时间',
      width: 180,
      formatter: 'formatDateTime',
      field: 'warehouseName',
      title: '默认仓库',
      minWidth: 100,
      align: 'center',
    },
  ];
}
src/views/basicData/mdm/components/select-dialog.vue
@@ -1,13 +1,15 @@
<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';
import { message, Modal } from 'ant-design-vue';
import { message, Modal, Tag } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getItemPage } from '#/api/mdm/item';
import { getItemTypeSimpleList } from '#/api/mes/md/item/type';
import {
  useMdmItemSelectGridColumns,
@@ -24,6 +26,21 @@
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() {
@@ -121,6 +138,13 @@
    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,
@@ -204,6 +228,15 @@
    @ok="handleConfirm"
    @cancel="closeModal"
  >
    <Grid table-title="物料列表" />
    <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>
      </template>
    </Grid>
  </Modal>
</template>
src/views/basicData/mdm/index.vue
@@ -3,7 +3,7 @@
import type { MdmItemApi } from '#/api/mdm/item';
import type { MesMdItemTypeApi } from '#/api/mes/md/item/type';
import { ref } from 'vue';
import { computed, ref } from 'vue';
import { confirm, Page, useVbenModal } from '#/packages/effects/common-ui/src';
import { downloadFileFromBlobPart, isEmpty } from '#/packages/utils/src';
@@ -33,6 +33,34 @@
// 分类列表缓存
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) {
@@ -196,11 +224,7 @@
        />
      </template>
      <template #categoryName="{ row }">
        <Tag v-if="row.categoryId === 1" color="green">{{ getCategoryName(row.categoryId) }}</Tag>
        <Tag v-else-if="row.categoryId === 2" color="orange">{{ getCategoryName(row.categoryId) }}</Tag>
        <Tag v-else-if="row.categoryId === 3" color="blue">{{ getCategoryName(row.categoryId) }}</Tag>
        <Tag v-else-if="row.categoryId === 4" color="purple">{{ getCategoryName(row.categoryId) }}</Tag>
        <Tag v-else-if="row.categoryId === 5" color="cyan">{{ getCategoryName(row.categoryId) }}</Tag>
        <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 }">
src/views/crm/saleQuotation/data.ts
@@ -124,6 +124,7 @@
      fieldName: 'quotationTime',
      label: '报价日期',
      component: 'DatePicker',
      rules: 'required',
      componentProps: {
        showTime: false,
        format: 'YYYY-MM-DD',
@@ -165,7 +166,7 @@
    },
    {
      fieldName: 'items',
      label: '物料清单',
      label: '产品清单',
      component: 'Input',
      formItemClass: 'col-span-3',
    },
@@ -340,24 +341,19 @@
  ];
}
/** 物料明细表格列 */
/** 产品明细表格列 */
export function useItemColumns(): VxeTableGridOptions['columns'] {
  return [
    { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' },
    {
      field: 'itemId',
      title: '物料名称',
      title: '产品名称',
      minWidth: 200,
      slots: { default: 'itemId' },
    },
    {
      field: 'itemSpecification',
      title: '规格型号',
      minWidth: 120,
    },
    {
      field: 'itemBarCode',
      title: '条码',
      minWidth: 120,
    },
    {
src/views/crm/saleQuotation/modules/item-form.vue
@@ -2,14 +2,14 @@
import type { MdmItemApi } from '#/api/mdm/item';
import type { CrmSaleQuotationApi } from '#/api/crm/saleQuotation';
import { computed, onMounted, ref } from 'vue';
import { computed, ref } from 'vue';
import { erpPriceMultiply } from '#/packages/utils/src';
import { InputNumber, Select } from 'ant-design-vue';
import { InputNumber } from 'ant-design-vue';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getItemPage } from '#/api/mdm/item';
import MdmItemSelect from '#/views/basicData/mdm/components/select.vue';
import { useItemColumns } from '../data';
@@ -24,7 +24,7 @@
const emit = defineEmits(['change']);
const tableData = ref<CrmSaleQuotationApi.SaleQuotationItem[]>([]);
const itemOptions = ref<MdmItemApi.Item[]>([]);
const selectedItem = ref<MdmItemApi.Item>();
/** 获取表格合计数据 */
const summaries = computed(() => {
@@ -93,13 +93,23 @@
  }
}
/** 处理物料变更 */
function handleItemChange(itemId: number, row: any) {
  const item = itemOptions.value.find((p) => p.id === itemId);
/** 处理产品变更 */
function handleItemChange(item: MdmItemApi.Item | undefined, row: any) {
  if (!item) {
    row.itemId = undefined;
    row.itemName = undefined;
    row.itemCode = undefined;
    row.itemBarCode = undefined;
    row.itemSpecification = undefined;
    row.itemUnitName = undefined;
    row.itemUnitName2 = undefined;
    row.itemUnitName3 = undefined;
    row.itemPrice = undefined;
    row.quotationPrice = undefined;
    row.totalPrice = 0;
    return;
  }
  row.itemId = itemId;
  row.itemId = item.id;
  row.itemName = item.name;
  row.itemCode = item.code;
  row.itemBarCode = item.barCode;
@@ -133,7 +143,7 @@
    const item = tableData.value[i];
    if (item) {
      if (!item.itemId) {
        throw new Error(`第 ${i + 1} 行:物料不能为空`);
        throw new Error(`第 ${i + 1} 行:产品不能为空`);
      }
      if (!item.count || item.count <= 0) {
        throw new Error(`第 ${i + 1} 行:数量不能为空`);
@@ -173,25 +183,16 @@
  getData,
  resetData,
});
/** 初始化 */
onMounted(async () => {
  const res = await getItemPage({ pageNo: 1, pageSize: 100, status: 0 });
  itemOptions.value = res.list || [];
});
</script>
<template>
  <Grid class="w-full">
    <template #itemId="{ row }">
      <Select
      <MdmItemSelect
        v-if="!disabled"
        v-model:value="row.itemId"
        :options="itemOptions"
        :field-names="{ label: 'name', value: 'id' }"
        class="w-full"
        placeholder="请选择物料"
        @change="(val) => handleItemChange(val, row)"
        v-model:model-value="row.itemId"
        placeholder="请选择产品"
        @change="(item) => handleItemChange(item, row)"
      />
      <span v-else>{{ row.itemName || '-' }}</span>
    </template>
@@ -224,7 +225,7 @@
            type: 'link',
            danger: true,
            popConfirm: {
              title: '确认删除该物料吗?',
              title: '确认删除该产品吗?',
              confirm: handleDelete.bind(null, row),
            },
          },
@@ -247,7 +248,7 @@
        class="mt-2 flex justify-center"
        :actions="[
          {
            label: '添加物料',
            label: '添加产品',
            type: 'default',
            onClick: handleAdd,
          },
src/views/erp/sale/order/data.ts
@@ -199,11 +199,6 @@
      formatter: 'formatAmount3',
    },
    {
      field: 'productBarCode',
      title: '条码',
      minWidth: 120,
    },
    {
      field: 'productUnitName',
      title: '单位',
      minWidth: 80,
@@ -274,7 +269,7 @@
    },
    {
      title: '操作',
      width: 320,
      width: 80,
      fixed: 'right',
      slots: { default: 'actions' },
      visible: !disabled,
@@ -371,8 +366,9 @@
      componentProps: {
        options: [
          { label: '未出库', value: 0 },
          { label: '部分出库', value: 1 },
          { label: '全部出库', value: 2 },
          { label: '已预留', value: 1 },
          { label: '部分出库', value: 2 },
          { label: '全部出库', value: 3 },
        ],
        placeholder: '请选择出库状态',
        allowClear: true,
@@ -445,16 +441,10 @@
      minWidth: 120,
    },
    {
      field: 'outCount',
      title: '出库数量',
      formatter: 'formatAmount3',
      minWidth: 120,
    },
    {
      field: 'returnCount',
      title: '退货数量',
      formatter: 'formatAmount3',
      minWidth: 120,
      field: 'outStatus',
      title: '出库状态',
      minWidth: 100,
      slots: { default: 'outStatus' },
    },
    {
      field: 'totalProductPrice',
src/views/erp/sale/order/index.vue
@@ -24,6 +24,7 @@
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
import SalesNoticeForm from '#/views/wls/salesnotice/modules/form.vue';
/** ERP 销售订单列表 */
defineOptions({ name: 'ErpSaleOrder' });
@@ -32,6 +33,11 @@
const [FormModal, formModalApi] = useVbenModal({
  connectedComponent: Form,
  destroyOnClose: true,
});
const [SalesNoticeFormModal, salesNoticeFormModalApi] = useVbenModal({
  connectedComponent: SalesNoticeForm,
  destroyOnClose: true,
});
@@ -147,11 +153,11 @@
  try {
    const noticeId = await generateFromSaleOrder(row.id!);
    message.success('生成发货通知单成功');
    // 跳转到发货通知单页面
    router.push({
      path: '/wls/sales-notice',
      query: { id: noticeId },
    });
    handleRefresh();
    // 直接打开发货通知单详情弹框
    salesNoticeFormModalApi
      .setData({ formType: 'detail', id: noticeId })
      .open();
  } catch (e) {
    console.error(e);
  }
@@ -193,7 +199,9 @@
</script>
<template>
  <Page auto-content-height><FormModal @success="handleRefresh" />
  <Page auto-content-height>
    <FormModal @success="handleRefresh" />
    <SalesNoticeFormModal />
    <Grid table-title="销售订单列表">
      <template #toolbar-tools>
        <TableAction
@@ -251,6 +259,12 @@
      <template #needProduction="{ row }">
        <Tag v-if="row.needProduction === 1" color="success">是</Tag>
        <Tag v-else color="default">否</Tag>
      </template>
      <template #outStatus="{ row }">
        <Tag v-if="row.outStatus === 0" color="default">未出库</Tag>
        <Tag v-else-if="row.outStatus === 1" color="processing">已预留</Tag>
        <Tag v-else-if="row.outStatus === 2" color="warning">部分出库</Tag>
        <Tag v-else-if="row.outStatus === 3" color="success">全部出库</Tag>
      </template>
      <template #actions="{ row }">
        <TableAction
@@ -319,7 +333,7 @@
              type: 'link',
              icon: 'ant-design:file-protect-outlined',
              auth: ['mes:wm-sales-notice:create'],
              ifShow: () => row.status === 20,
              ifShow: () => row.status === 20 && row.canCreateSalesNotice,
              onClick: handleGenerateNotice.bind(null, row),
            },
            {
src/views/erp/sale/order/modules/item-form.vue
@@ -1,5 +1,4 @@
<script lang="ts" setup>
import type { MdmItemApi } from '#/api/mdm/item';
import type { ErpSaleOrderApi } from '#/api/erp/sale/order';
import { computed, nextTick, onMounted, ref, watch } from 'vue';
@@ -13,8 +12,7 @@
import { Input, InputNumber, Select, Tag } from 'ant-design-vue';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getItemPage } from '#/api/mdm/item';
import { getStockCount } from '#/api/erp/stock/stock';
import { MdmItemSelect } from '#/views/basicData/mdm/components';
import { useFormItemColumns } from '../data';
@@ -36,8 +34,7 @@
  'update:totalPrice',
]);
const tableData = ref<ErpSaleOrderApi.SaleOrderItem[]>([]); // 表格数据
const productOptions = ref<MdmItemApi.Item[]>([]); // 物料下拉选项
const tableData = ref<ErpSaleOrderApi.SaleOrderItem[]>([]);
/** 获取表格合计数据 */
const summaries = computed(() => {
@@ -124,8 +121,7 @@
  const newRow = {
    id: undefined,
    productId: undefined,
    productUnitName: undefined, // 产品单位
    productBarCode: undefined, // 产品条码
    productUnitName: undefined,
    productPrice: undefined,
    stockCount: undefined,
    count: 1,
@@ -134,7 +130,7 @@
    taxPrice: undefined,
    totalPrice: undefined,
    remark: undefined,
    needProduction: 1, // 默认需要生产
    needProduction: 1,
  };
  tableData.value.push(newRow);
  // 通知父组件更新
@@ -153,12 +149,11 @@
}
/** 处理产品变更 */
async function handleProductChange(productId: any, row: any) {
  const product = productOptions.value.find((p) => p.id === productId);
function handleProductChange(product: any, row: any) {
  if (!product) {
    return;
  }
  row.productId = productId;
  row.productId = product.id;
  row.productUnitId = product.unitMeasureId;
  row.productBarCode = product.barCode;
  row.productUnitName = product.unitMeasureName;
@@ -166,7 +161,6 @@
  row.productUnitName3 = product.unitMeasureName3;
  row.productSpecification = product.specification;
  row.productName = product.name;
  row.stockCount = (await getStockCount(productId)) || 0;
  row.productPrice = product.salesPrice || 0;
  row.count = row.count || 1;
  handleRowChange(row);
@@ -221,8 +215,6 @@
/** 初始化 */
onMounted(async () => {
  const res = await getItemPage({ pageNo: 1, pageSize: 100, status: 0 });
  productOptions.value = res.list || [];
  // 目的:新增时,默认添加一行
  if (tableData.value.length === 0) {
    handleAdd();
@@ -233,14 +225,11 @@
<template>
  <Grid class="w-full">
    <template #productId="{ row }">
      <Select
      <MdmItemSelect
        v-if="!disabled"
        v-model:value="row.productId"
        :options="productOptions"
        :field-names="{ label: 'name', value: 'id' }"
        class="w-full"
        :model-value="row.productId"
        placeholder="请选择产品"
        show-search
        @update:model-value="row.productId = $event"
        @change="handleProductChange($event, row)"
      />
      <span v-else>{{ row.productName || '-' }}</span>
src/views/mes/pro/task/data.ts
@@ -8,8 +8,8 @@
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import CrmCustomerSelect from '#/components/crm-customer-select.vue';
import { getRangePickerDefaultProps } from '#/utils';
import { MdClientSelect } from '#/views/mes/md/client/components';
import { MdItemSelect } from '#/views/mes/md/item/components';
import { MdWorkstationSelect } from '#/views/mes/md/workstation/components';
import { ProProcessSelect } from '#/views/mes/process-design/process/components';
@@ -57,7 +57,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
      },
@@ -258,7 +258,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        disabled: true,
      },
src/views/mes/pro/task/modules/schedule-form.vue
@@ -129,7 +129,7 @@
        <TaskList
          :color-code="rp.colorCode"
          :disabled="isReadonly"
          :item-id="workOrder.productId"
          :item-id="rp.outputItemId || workOrder.productId"
          :process-id="rp.processId!"
          :route-id="currentRouteId"
          :work-order-id="workOrder.id!"
src/views/mes/pro/workorder/data.ts
@@ -17,8 +17,8 @@
import { Button } from 'ant-design-vue';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import CrmCustomerSelect from '#/components/crm-customer-select.vue';
import { getRangePickerDefaultProps } from '#/utils';
import { MdClientSelect } from '#/views/mes/md/client/components';
import {
  MdItemSelect,
  MdProductBomSelect,
@@ -189,7 +189,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        disabled: headerReadonly,
        placeholder: '请选择客户',
@@ -292,7 +292,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
      },
@@ -591,7 +591,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
      },
src/views/mes/process-design/route/data.ts
@@ -248,6 +248,15 @@
      rules: z.boolean().default(true),
    },
    {
      fieldName: "outputItemId",
      label: "产出产品",
      component: markRaw(MdItemSelect),
      componentProps: {
        placeholder: "留空则默认为工单成品",
      },
      formItemClass: "col-span-2",
    },
    {
      fieldName: "prepareTime",
      label: "准备时间(分)",
      component: "InputNumber",
@@ -326,6 +335,12 @@
        props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
      },
    },
    {
      field: "outputItemName",
      title: "产出产品",
      width: 140,
      slots: { default: "outputItemName" },
    },
    { field: "prepareTime", title: "准备时间(分)", width: 110 },
    { field: "waitTime", title: "等待时间(分)", width: 110 },
    {
src/views/mes/process-design/route/modules/process-list.vue
@@ -110,6 +110,10 @@
        <span>{{ row.colorCode }}</span>
      </div>
    </template>
    <template #outputItemName="{ row }">
      <span v-if="row.outputItemName">{{ row.outputItemName }}</span>
      <span v-else class="text-gray-400">-</span>
    </template>
    <template #actions="{ row }">
      <TableAction
        :actions="[
src/views/mes/qc/oqc/data.ts
@@ -12,7 +12,7 @@
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { getSimpleUserList } from '#/api/system/user';
import { MdClientSelect } from '#/views/mes/md/client/components';
import CrmCustomerSelect from '#/components/crm-customer-select.vue';
import { MdmItemSelect } from '#/views/basicData/mdm/components';
/** 表单类型 */
@@ -135,7 +135,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
      },
@@ -307,7 +307,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
      },
src/views/wls/productsales/data.ts
@@ -19,9 +19,10 @@
import { Button } from 'ant-design-vue';
import { z } from '#/adapter/form';
import CrmCustomerSelect from '#/components/crm-customer-select.vue';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components';
import { getRangePickerDefaultProps } from '#/utils';
import { MdClientSelect } from '#/views/mes/md/client/components';
import { MdItemSelect } from '#/views/mes/md/item/components';
import { WmMaterialStockSelect } from '#/views/wls/materialstock/components';
import {
@@ -139,12 +140,29 @@
    },
    {
      fieldName: 'salesOrderCode',
      label: '销售订单编号',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'saleOrderId',
      label: '销售订单编号',
      component: markRaw(ErpSaleOrderSelect),
      componentProps: {
        disabled: isHeaderReadonly(formType),
        placeholder: '请输入销售订单编号',
        placeholder: '请选择销售订单',
        onChange: (item: any) => {
          if (formApi) {
            formApi.setFieldValue('salesOrderCode', item?.no ?? '');
            if (item?.customerId) {
              formApi.setFieldValue('clientId', item.customerId);
            }
          }
        },
      },
      rules: 'selectRequired',
    },
    {
      fieldName: 'salesDate',
@@ -162,7 +180,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        disabled: isHeaderReadonly(formType),
        placeholder: '请选择客户',
@@ -269,7 +287,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
      },
src/views/wls/returnsales/data.ts
@@ -12,8 +12,9 @@
import { Button } from 'ant-design-vue';
import { z } from '#/adapter/form';
import CrmCustomerSelect from '#/components/crm-customer-select.vue';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { MdClientSelect } from '#/views/mes/md/client/components';
import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components';
import { MdItemSelect } from '#/views/mes/md/item/components';
import { WmBatchSelect } from '#/views/wls/batch/components';
import {
@@ -88,16 +89,33 @@
    },
    {
      fieldName: 'salesOrderCode',
      label: '销售订单号',
      component: 'Input',
      componentProps: {
        placeholder: '请输入销售订单号',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'saleOrderId',
      label: '销售订单号',
      component: markRaw(ErpSaleOrderSelect),
      componentProps: {
        placeholder: '请选择销售订单',
        onChange: (item: any) => {
          if (formApi) {
            formApi.setFieldValue('salesOrderCode', item?.no ?? '');
            if (item?.customerId) {
              formApi.setFieldValue('clientId', item.customerId);
            }
          }
        },
      },
      rules: 'selectRequired',
    },
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
      },
@@ -172,7 +190,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
      },
src/views/wls/salesnotice/components/select-dialog.vue
@@ -12,7 +12,7 @@
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getSalesNoticePage } from '#/api/mes/wm/salesnotice';
import { MdClientSelect } from '#/views/mes/md/client/components';
import CrmCustomerSelect from '#/components/crm-customer-select.vue';
const emit = defineEmits<{
  selected: [rows: MesWmSalesNoticeApi.SalesNotice[]];
@@ -46,7 +46,7 @@
      },
    },
    {
      fieldName: 'salesOrderCode',
      fieldName: 'saleOrderCode',
      label: '销售订单编号',
      component: 'Input',
      componentProps: {
@@ -57,7 +57,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
      },
@@ -102,7 +102,7 @@
      minWidth: 150,
    },
    {
      field: 'salesOrderCode',
      field: 'saleOrderCode',
      title: '销售订单编号',
      minWidth: 140,
    },
src/views/wls/salesnotice/components/select.vue
@@ -119,7 +119,7 @@
          <div>编号:{{ selectedItem.code || '-' }}</div>
          <div>名称:{{ selectedItem.name || '-' }}</div>
          <div>客户:{{ selectedItem.clientName || '-' }}</div>
          <div>销售订单:{{ selectedItem.salesOrderCode || '-' }}</div>
          <div>销售订单:{{ selectedItem.saleOrderCode || '-' }}</div>
        </div>
      </template>
      <Input
src/views/wls/salesnotice/data.ts
@@ -8,23 +8,22 @@
import {
  DICT_TYPE,
  MesAutoCodeRuleCode,
  MesWmSalesNoticeOutStatusEnum,
} from '@vben/constants';
import { Button, Tag } from 'ant-design-vue';
import { Button } from 'ant-design-vue';
import { z } from '#/adapter/form';
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import CrmCustomerSelect from '#/components/crm-customer-select.vue';
import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components';
import { MdClientSelect } from '#/views/mes/md/client/components';
import { MdItemSelect } from '#/views/mes/md/item/components';
/** 表单类型 */
export type FormType = 'create' | 'detail' | 'finish' | 'update';
export type FormType = 'create' | 'detail' | 'update';
/** 表单头部是否只读(详情、执行出库态) */
/** 表单头部是否只读(详情态) */
function isHeaderReadonly(formType: FormType): boolean {
  return formType === 'detail' || formType === 'finish';
  return formType === 'detail';
}
/** 新增/修改的表单 */
@@ -93,35 +92,17 @@
        onChange: (item: any) => {
          if (item?.customerId && formApi) {
            formApi.setFieldValue('clientId', item.customerId);
            formApi.setFieldValue('clientName', item.customerName);
          }
        },
      },
    },
    {
      fieldName: 'clientName',
      label: '',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
        disabled: true,
      },
      dependencies: {
        triggerFields: ['clientName'],
        componentProps: (values) => ({
          placeholder: '请选择客户',
          disabled: true,
          clientName: values.clientName as string,
        }),
      },
      rules: 'selectRequired',
    },
@@ -196,7 +177,7 @@
      },
    },
    {
      fieldName: 'salesOrderCode',
      fieldName: 'saleOrderCode',
      label: '销售订单编号',
      component: 'Input',
      componentProps: {
@@ -207,7 +188,7 @@
    {
      fieldName: 'clientId',
      label: '客户',
      component: markRaw(MdClientSelect),
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        placeholder: '请选择客户',
      },
@@ -230,7 +211,7 @@
      minWidth: 150,
    },
    {
      field: 'salesOrderCode',
      field: 'saleOrderCode',
      title: '销售订单编号',
      minWidth: 140,
    },
@@ -273,20 +254,7 @@
      field: 'outStatus',
      title: '出库状态',
      minWidth: 100,
      cellRender: {
        name: 'CellTag',
        props: {
          formatter: ({ row }: { row: MesWmSalesNoticeApi.SalesNotice }) => {
            const statusMap: Record<number, { color: string; label: string }> = {
              [MesWmSalesNoticeOutStatusEnum.NONE]: { color: 'default', label: '未出库' },
              [MesWmSalesNoticeOutStatusEnum.PARTIAL]: { color: 'warning', label: '部分出库' },
              [MesWmSalesNoticeOutStatusEnum.ALL]: { color: 'success', label: '全部出库' },
            };
            const status = statusMap[row.outStatus ?? MesWmSalesNoticeOutStatusEnum.NONE];
            return { color: status?.color, label: status?.label ?? '未出库' };
          },
        },
      },
      slots: { default: 'outStatus' },
    },
    {
      title: '操作',
src/views/wls/salesnotice/index.vue
@@ -3,10 +3,10 @@
import type { MesWmSalesNoticeApi } from '#/api/mes/wm/salesnotice';
import { Page, useVbenModal } from '@vben/common-ui';
import { MesWmSalesNoticeStatusEnum } from '@vben/constants';
import { MesWmSalesNoticeOutStatusEnum, MesWmSalesNoticeStatusEnum } from '@vben/constants';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { Button, message, Tag } from 'ant-design-vue';
import { useRouter } from 'vue-router';
@@ -29,6 +29,17 @@
const router = useRouter();
const outStatusLabelMap: Record<number, string> = {
  [MesWmSalesNoticeOutStatusEnum.NONE]: '未出库',
  [MesWmSalesNoticeOutStatusEnum.PARTIAL]: '部分出库',
  [MesWmSalesNoticeOutStatusEnum.ALL]: '全部出库',
};
const outStatusColorMap: Record<number, string> = {
  [MesWmSalesNoticeOutStatusEnum.NONE]: 'default',
  [MesWmSalesNoticeOutStatusEnum.PARTIAL]: 'warning',
  [MesWmSalesNoticeOutStatusEnum.ALL]: 'success',
};
/** 刷新表格 */
function handleRefresh() {
  gridApi.query();
@@ -49,20 +60,14 @@
  formModalApi.setData({ formType: 'update', id: row.id }).open();
}
/** 执行出库 */
function handleFinish(row: MesWmSalesNoticeApi.SalesNotice) {
  formModalApi.setData({ formType: 'finish', id: row.id }).open();
}
/** 生成销售出库单 */
async function handleGenerateProductSales(row: MesWmSalesNoticeApi.SalesNotice) {
  try {
    const productSalesId = await generateProductSales(row.id!);
    message.success('生成销售出库单成功');
    // 跳转到销售出库单页面
    router.push({
      path: '/wls/product-sales',
      query: { id: productSalesId },
      name: 'MesWmProductSales',
      query: { id: String(productSalesId) },
    });
  } catch (e) {
    console.error(e);
@@ -150,6 +155,9 @@
          {{ row.code }}
        </Button>
      </template>
      <template #outStatus="{ row }">
        <Tag :color="outStatusColorMap[row.outStatus ?? 0]">{{ outStatusLabelMap[row.outStatus ?? 0] }}</Tag>
      </template>
      <template #actions="{ row }">
        <TableAction
          :actions="[
@@ -180,13 +188,6 @@
              auth: ['mes:wm-product-sales:create'],
              ifShow: row.status === MesWmSalesNoticeStatusEnum.APPROVED,
              onClick: handleGenerateProductSales.bind(null, row),
            },
            {
              label: '执行出库',
              type: 'link',
              auth: ['mes:wm-sales-notice:update'],
              ifShow: row.status === MesWmSalesNoticeStatusEnum.APPROVED,
              onClick: handleFinish.bind(null, row),
            },
          ]"
        />
src/views/wls/salesnotice/modules/form.vue
@@ -30,7 +30,6 @@
  // 是否为编辑模式(可保存)
  ['create', 'update'].includes(formType.value),
);
const isFinish = computed(() => formType.value === 'finish'); // 是否为执行出库模式
const canSubmit = computed(
  () =>
    // 是否可提交
@@ -40,9 +39,6 @@
const getTitle = computed(() => {
  if (formType.value === 'detail') {
    return $t('ui.actionTitle.view', ['发货通知单']);
  }
  if (formType.value === 'finish') {
    return '执行出库';
  }
  return formType.value === 'update'
    ? $t('ui.actionTitle.edit', ['发货通知单'])
@@ -85,11 +81,6 @@
  } finally {
    modalApi.unlock();
  }
}
/** 执行出库(后端暂未实现,提示用户) */
function handleFinish() {
  message.info('执行出库功能暂时不支持,敬请期待!');
}
const [Modal, modalApi] = useVbenModal({
@@ -173,9 +164,6 @@
        >
          <Button type="primary">提交</Button>
        </Popconfirm>
        <Button v-if="isFinish" type="primary" @click="handleFinish">
          执行出库
        </Button>
      </div>
    </template>
  </Modal>
src/views/wls/transfer/data.ts
@@ -394,7 +394,7 @@
      ? [
          {
            title: '操作',
            width: 180,
            width: 240,
            fixed: 'right',
            slots: { default: 'actions' },
          } as const,