zhangwencui
2 天以前 271673032ed2442822c863375e0dc78dcd51e296
Merge branch 'dev_pro2.0' of http://114.132.189.42:9002/r/mom-pro2-before into dev_pro2.0
已修改19个文件
258 ■■■■■ 文件已修改
src/api/crm/business/index.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/crm/business/status/index.ts 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/erp/sale/order/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/business/data.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/business/detail/data.ts 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/business/detail/index.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/business/detail/modules/status-form.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/business/index.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/business/modules/form.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/business/status/data.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/business/status/modules/form.vue 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/contract/components/detail-list.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/contract/data.ts 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/contract/modules/form.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/product/components/data.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/product/components/edit-table.vue 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/sale/order/data.ts 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/sale/order/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/sale/order/modules/form.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/crm/business/index.ts
@@ -21,7 +21,6 @@
    statusTypeName?: string;
    statusId: number;
    statusName?: string;
    endStatus: number;
    endRemark: string;
    dealTime: Date;
    totalProductPrice: number;
@@ -52,8 +51,7 @@
  /** 商机更新状态请求 */
  export interface BusinessUpdateStatusReqVO {
    id: number;
    statusId: number | undefined;
    endStatus: number | undefined;
    statusId: number;
  }
}
src/api/crm/business/status/index.ts
@@ -19,32 +19,9 @@
    id?: number;
    name: string;
    percent?: number;
    endStatus?: number;
    key?: string;
    sort?: number;
  }
}
/** 默认商机状态 */
export const DEFAULT_STATUSES = [
  {
    endStatus: 1,
    key: '结束',
    name: '赢单',
    percent: 100,
  },
  {
    endStatus: 2,
    key: '结束',
    name: '输单',
    percent: 0,
  },
  {
    endStatus: 3,
    key: '结束',
    name: '无效',
    percent: 0,
  },
] satisfies CrmBusinessStatusApi.BusinessStatusType[];
/** 查询商机状态组列表 */
export function getBusinessStatusPage(params: PageParam) {
src/api/erp/sale/order/index.ts
@@ -27,6 +27,8 @@
    contractId?: number; // 关联 CRM 合同编号
    contractNo?: string; // CRM 合同单号
    needProduction?: number; // 是否需要生产:0-不需要,1-需要
    productionStatus?: number; // 生产状态:0-未完成,1-已完成
    productionFinishTime?: Date; // 生产完成时间
    hasSalesNotice?: boolean; // 是否已生成发货通知单
    canCreateSalesNotice?: boolean; // 是否可以生成发货通知单
    items?: SaleOrderItem[]; // 销售订单产品明细列表
src/views/crm/business/data.ts
@@ -265,6 +265,7 @@
      title: '商机阶段',
      fixed: 'right',
      width: 120,
      slots: { default: 'statusName' },
    },
    {
      title: '操作',
src/views/crm/business/detail/data.ts
@@ -7,7 +7,6 @@
import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
import {
  DEFAULT_STATUSES,
  getBusinessStatusSimpleList,
} from '#/api/crm/business/status';
@@ -95,24 +94,6 @@
    },
    {
      fieldName: 'statusId',
      label: '商机状态',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'endStatus',
      label: '商机状态',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'status',
      label: '商机阶段',
      component: 'Select',
      dependencies: {
@@ -121,17 +102,12 @@
          const statusList = await getBusinessStatusSimpleList(
            formData.value?.statusTypeId ?? 0,
          );
          const statusOptions = statusList.map((item) => ({
          const options = statusList.map((item) => ({
            label: `${item.name}(赢单率:${item.percent}%)`,
            value: item.id,
          }));
          const options = DEFAULT_STATUSES.map((item) => ({
            label: `${item.name}(赢单率:${item.percent}%)`,
            value: item.endStatus,
          }));
          statusOptions.push(...options);
          return {
            options: statusOptions,
            options,
          };
        },
      },
src/views/crm/business/detail/index.vue
@@ -160,6 +160,9 @@
          <ContractDetailsList
            :biz-id="businessId"
            :biz-type="BizTypeEnum.CRM_BUSINESS"
            :customer-id="business.customerId"
            :customer-name="business.customerName"
            :business-name="business.name"
          />
        </Tabs.TabPane>
        <Tabs.TabPane tab="操作日志" key="6" :force-render="true">
src/views/crm/business/detail/modules/status-form.vue
@@ -40,13 +40,12 @@
    // 提交表单
    const data = (await formApi.getValues()) as CrmBusinessApi.Business;
    try {
      if (!data.status) {
      if (!data.statusId) {
        return;
      }
      await updateBusinessStatus({
        id: data.id,
        statusId: data.status > 0 ? data.status : undefined,
        endStatus: data.status < 0 ? -data.status : undefined,
        statusId: data.statusId,
      });
      // 关闭并提示
      await modalApi.close();
@@ -65,7 +64,6 @@
    if (!data || !data.id) {
      return;
    }
    data.status = data.endStatus === null ? data.statusId : -data.endStatus;
    formData.value = data;
    modalApi.lock();
    try {
src/views/crm/business/index.vue
@@ -8,7 +8,7 @@
import { Page, useVbenModal } from '@vben/common-ui';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message, Tabs } from 'ant-design-vue';
import { Button, message, Tabs, Tag } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
@@ -23,6 +23,22 @@
const { push } = useRouter();
const sceneType = ref('1');
const STATUS_COLORS: Record<string, string> = {
  拜访: 'default',
  初步接触: 'processing',
  需求确认: 'cyan',
  方案报价: 'orange',
  商务谈判: 'volcano',
  合同签订: 'success',
  赢单: 'success',
  输单: 'error',
  无效: 'default',
};
function getStatusColor(name: string): string {
  return STATUS_COLORS[name] || 'blue';
}
const [FormModal, formModalApi] = useVbenModal({
  connectedComponent: Form,
@@ -157,6 +173,9 @@
          {{ row.customerName }}
        </Button>
      </template>
      <template #statusName="{ row }">
        <Tag v-if="row.statusName" :color="getStatusColor(row.statusName)">{{ row.statusName }}</Tag>
      </template>
      <template #actions="{ row }">
        <TableAction
          :actions="[
src/views/crm/business/modules/form.vue
@@ -105,7 +105,7 @@
</script>
<template>
  <Modal :title="getTitle" class="w-1/2">
  <Modal :title="getTitle" class="w-3/4">
    <Form class="mx-4">
      <template #product="slotProps">
        <ProductEditTable
src/views/crm/business/status/data.ts
@@ -85,10 +85,9 @@
export function useFormColumns(): VxeTableGridOptions['columns'] {
  return [
    {
      field: 'endStatus',
      title: '阶段',
      minWidth: 100,
      slots: { default: 'endStatus' },
      type: 'seq',
      title: '序号',
      width: 60,
    },
    {
      field: 'name',
src/views/crm/business/status/modules/form.vue
@@ -11,7 +11,6 @@
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
  createBusinessStatus,
  DEFAULT_STATUSES,
  getBusinessStatus,
  updateBusinessStatus,
} from '#/api/crm/business/status';
@@ -53,7 +52,6 @@
    try {
      if (formData.value?.statuses && formData.value.statuses.length > 0) {
        data.statuses = formData.value.statuses;
        data.statuses.splice(-3, 3);
      }
      await (formData.value?.id
        ? updateBusinessStatus(data)
@@ -94,8 +92,7 @@
      // 设置到 values
      await formApi.setValues(formData.value as any);
      await gridApi.grid.reloadData(
        (formData.value!.statuses =
          formData.value?.statuses?.concat(DEFAULT_STATUSES)) as any,
        (formData.value!.statuses) as any,
      );
    } finally {
      modalApi.unlock();
@@ -105,7 +102,7 @@
/** 添加状态 */
async function handleAddStatus() {
  formData.value!.statuses!.splice(-3, 0, {
  formData.value!.statuses!.push({
    name: '',
    percent: undefined,
  });
@@ -128,7 +125,7 @@
      mode: 'cell',
    },
    columns: useFormColumns(),
    data: formData.value?.statuses?.concat(DEFAULT_STATUSES),
    data: formData.value?.statuses,
    border: true,
    showOverflow: true,
    autoResize: true,
@@ -152,29 +149,20 @@
    <Form class="mx-4">
      <template #statuses>
        <Grid class="w-full">
          <template #endStatus="{ row, rowIndex }">
            <span>
              {{ row.endStatus ? '结束' : `阶段${rowIndex + 1}` }}
            </span>
          </template>
          <template #name="{ row }">
            <Input
              v-if="!row.endStatus"
              v-model:value="row.name"
              placeholder="请输入状态名"
            />
            <span v-else>{{ row.name }}</span>
          </template>
          <template #percent="{ row }">
            <InputNumber
              v-if="!row.endStatus"
              v-model:value="row.percent"
              :min="0"
              :max="100"
              :precision="2"
              placeholder="请输入赢单率"
            />
            <span v-else>{{ row.percent }}</span>
          </template>
          <template #actions="{ row, rowIndex }">
            <TableAction
@@ -182,14 +170,12 @@
                {
                  label: $t('ui.actionTitle.create'),
                  type: 'link',
                  ifShow: () => !row.endStatus,
                  onClick: handleAddStatus,
                },
                {
                  label: $t('common.delete'),
                  type: 'link',
                  danger: true,
                  ifShow: () => !row.endStatus,
                  popConfirm: {
                    title: $t('ui.actionMessage.deleteConfirm', [row.name]),
                    confirm: deleteStatusArea.bind(null, row, rowIndex),
src/views/crm/contract/components/detail-list.vue
@@ -24,6 +24,9 @@
const props = defineProps<{
  bizId: number; // 业务编号
  bizType: number; // 业务类型
  customerId?: number; // 客户编号(商机/联系人详情传入)
  customerName?: string; // 客户名称(商机/联系人详情传入)
  businessName?: string; // 商机名称(商机详情传入)
}>();
const { push } = useRouter();
@@ -52,7 +55,12 @@
        ? {
            customerId: props.bizId,
          }
        : { businessId: props.bizId },
        : {
            businessId: props.bizId,
            customerId: props.customerId,
            customerName: props.customerName,
            businessName: props.businessName,
          },
    )
    .open();
}
@@ -77,7 +85,7 @@
              customerId: props.bizId,
              ...formValues,
            });
          } else if (props.bizType === BizTypeEnum.CRM_CONTACT) {
          } else if (props.bizType === BizTypeEnum.CRM_BUSINESS) {
            return await getContractPageByBusiness({
              pageNo: page.currentPage,
              pageSize: page.pageSize,
src/views/crm/contract/data.ts
@@ -66,6 +66,10 @@
      componentProps: {
        placeholder: '请选择客户',
      },
      dependencies: {
        triggerFields: ['customerId'],
        disabled: (values) => !!values.customerId,
      },
    },
    {
      fieldName: 'orderDate',
@@ -88,9 +92,15 @@
        placeholder: '请选择商机',
      },
      dependencies: {
        triggerFields: ['customerId'],
        disabled: (values) => !values.customerId,
        triggerFields: ['customerId', 'businessId'],
        disabled: (values) => !!values.businessId || !values.customerId,
        async componentProps(values) {
          if (values.businessId && values.businessName) {
            return {
              options: [{ label: values.businessName, value: values.businessId }],
              placeholder: '请选择商机',
            };
          }
          if (!values.customerId) {
            return {
              options: [],
src/views/crm/contract/modules/form.vue
@@ -90,6 +90,10 @@
    // 加载数据
    const data = modalApi.getData<CrmContractApi.Contract>();
    if (!data || !data.id) {
      // 新增时,回显从商机/客户详情传入的数据
      if (data) {
        await formApi.setValues(data);
      }
      return;
    }
    modalApi.lock();
src/views/crm/product/components/data.ts
@@ -67,11 +67,6 @@
      minWidth: 120,
    },
    {
      field: 'itemBarCode',
      title: '条码',
      minWidth: 150,
    },
    {
      field: 'itemUnitName',
      title: '单位',
      minWidth: 80,
src/views/crm/product/components/edit-table.vue
@@ -3,15 +3,15 @@
import type { CrmContractApi } from '#/api/crm/contract';
import type { MdmItemApi } from '#/api/mdm/item';
import { nextTick, onMounted, ref, watch } from 'vue';
import { nextTick, ref, watch } from 'vue';
import { erpPriceMultiply } from '@vben/utils';
import { InputNumber, Select } from 'ant-design-vue';
import { InputNumber } from 'ant-design-vue';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { BizTypeEnum } from '#/api/crm/permission';
import { getItemPage } from '#/api/mdm/item';
import { MdmItemSelect } from '#/views/basicData/mdm/components';
import { $t } from '#/locales';
import { useProductEditTableColumns } from './data';
@@ -39,12 +39,11 @@
}
/** 切换产品时同步基础信息 */
function handleProductChange(itemId: any, row: any) {
  const product = productOptions.value.find((p) => p.id === itemId);
function handleProductChange(product: MdmItemApi.Item | undefined, row: any) {
  if (!product) {
    return;
  }
  row.itemId = itemId;
  row.itemId = product.id;
  row.itemName = product.name;
  row.itemCode = product.code;
  row.itemBarCode = product.barCode;
@@ -118,23 +117,15 @@
    immediate: true,
  },
);
/** 初始化 */
const productOptions = ref<MdmItemApi.Item[]>([]); // 物料下拉选项
onMounted(async () => {
  const res = await getItemPage({ pageNo: 1, pageSize: 100, status: 0 });
  productOptions.value = res.list || [];
});
</script>
<template>
  <Grid class="w-full">
    <template #itemId="{ row }">
      <Select
        v-model:value="row.itemId"
        :options="productOptions"
        :field-names="{ label: 'name', value: 'id' }"
        class="w-full"
      <MdmItemSelect
        :model-value="row.itemId"
        placeholder="请选择产品"
        @update:model-value="row.itemId = $event"
        @change="handleProductChange($event, row)"
      />
    </template>
src/views/erp/sale/order/data.ts
@@ -36,6 +36,35 @@
      },
    },
    {
      fieldName: 'productionStatus',
      label: '生产状态',
      component: 'Input',
      dependencies: {
        triggerFields: ['needProduction', 'productionStatus'],
        show: (values) => {
          return formType === 'detail' && values.needProduction === 1;
        },
      },
      componentProps: {
        disabled: true,
      },
      slots: { default: 'productionStatus' },
    },
    {
      fieldName: 'productionFinishTime',
      label: '生产完成时间',
      component: 'Input',
      dependencies: {
        triggerFields: ['productionStatus'],
        show: (values) => {
          return formType === 'detail' && values.productionStatus === 1;
        },
      },
      componentProps: {
        disabled: true,
      },
    },
    {
      fieldName: 'orderTime',
      label: '订单时间',
      component: 'DatePicker',
@@ -375,6 +404,19 @@
      },
    },
    {
      fieldName: 'productionStatus',
      label: '生产状态',
      component: 'Select',
      componentProps: {
        options: [
          { label: '未完成', value: 0 },
          { label: '已完成', value: 1 },
        ],
        placeholder: '请选择生产状态',
        allowClear: true,
      },
    },
    {
      fieldName: 'returnStatus',
      label: '退货状态',
      component: 'Select',
@@ -441,12 +483,6 @@
      minWidth: 120,
    },
    {
      field: 'outStatus',
      title: '出库状态',
      minWidth: 100,
      slots: { default: 'outStatus' },
    },
    {
      field: 'totalProductPrice',
      title: '金额合计',
      formatter: 'formatAmount2',
@@ -465,6 +501,20 @@
      minWidth: 120,
    },
    {
      field: 'outStatus',
      title: '出库状态',
      minWidth: 100,
      fixed: 'right',
      slots: { default: 'outStatus' },
    },
    {
      field: 'productionStatus',
      title: '生产状态',
      minWidth: 100,
      fixed: 'right',
      slots: { default: 'productionStatus' },
    },
    {
      field: 'status',
      title: '状态',
      minWidth: 100,
src/views/erp/sale/order/index.vue
@@ -266,6 +266,15 @@
        <Tag v-else-if="row.outStatus === 2" color="warning">部分出库</Tag>
        <Tag v-else-if="row.outStatus === 3" color="success">全部出库</Tag>
      </template>
      <template #productionStatus="{ row }">
        <template v-if="row.needProduction !== 1">
          <Tag color="default">无需生产</Tag>
        </template>
        <template v-else>
          <Tag v-if="row.productionStatus === 0" color="warning">未完成</Tag>
          <Tag v-else-if="row.productionStatus === 1" color="success">已完成</Tag>
        </template>
      </template>
      <template #actions="{ row }">
        <TableAction
          :actions="[
src/views/erp/sale/order/modules/form.vue
@@ -7,7 +7,7 @@
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { message, Tag } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { getAccountSimpleList } from '#/api/erp/finance/account';
@@ -148,6 +148,10 @@
    :show-confirm-button="formType !== 'detail'"
  >
    <Form class="mx-3">
      <template #productionStatus="{ modelValue }">
        <Tag v-if="modelValue === 0" color="warning">未完成</Tag>
        <Tag v-else-if="modelValue === 1" color="success">已完成</Tag>
      </template>
      <template #items>
        <ItemForm
          ref="itemFormRef"