云
7 天以前 c069a398226e8c6489b9c5c0036bf0553cc66495
feat(crm): 添加业务模块客户选择功能

- 新增 allCustomers 属性控制客户选择范围
- 实现路由路径判断逻辑,非CRM模块默认使用全部客户
- 添加 getBusinessCustomer 和 getBusinessCustomerPage API方法
- 在MES、WLS等业务模块表单中启用全部客户选择
- 更新OQC导入提示信息,调整客户名称字段说明
- 优化客户选择对话框的tabs显示逻辑
已修改8个文件
69 ■■■■■ 文件已修改
src/api/crm/customer/index.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/crm-customer-select-dialog.vue 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/crm-customer-select.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pro/mps/data.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/qc/oqc/data.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/qc/oqc/index.vue 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/productsales/data.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/returnsales/data.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/crm/customer/index.ts
@@ -63,6 +63,21 @@
  );
}
/** 查询业务模块客户列表(不受 CRM 负责人权限过滤) */
export function getBusinessCustomerPage(params: PageParam) {
  return requestClient.get<PageResult<CrmCustomerApi.Customer>>(
    '/crm/customer/business-page',
    { params },
  );
}
/** 查询业务模块关联客户详情(不受 CRM 负责人权限过滤) */
export function getBusinessCustomer(id: number) {
  return requestClient.get<CrmCustomerApi.Customer>(
    `/crm/customer/business-get?id=${id}`,
  );
}
/** 查询客户详情 */
export function getCustomer(id: number) {
  return requestClient.get<CrmCustomerApi.Customer>(
src/components/crm-customer-select-dialog.vue
@@ -7,19 +7,25 @@
import { message, Modal, Tabs } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getCustomerPage } from '#/api/crm/customer';
import { getBusinessCustomerPage, getCustomerPage } from '#/api/crm/customer';
defineOptions({ name: 'CrmCustomerSelectDialog' });
const emit = defineEmits<{
  selected: [rows: CrmCustomerApi.Customer[]];
}>();
const props = defineProps<{ allCustomers: boolean }>();
const open = ref(false);
const multiple = ref(true);
const selectedRows = ref<CrmCustomerApi.Customer[]>([]);
const preSelectedIds = ref<number[]>([]);
const sceneType = ref('1');
function handleSceneTypeChange(key: number | string) {
  sceneType.value = key.toString();
  gridApi.query();
}
function getTableRows() {
  return gridApi.grid.getTableData().fullData as CrmCustomerApi.Customer[];
@@ -94,11 +100,6 @@
  }
}
function handleSceneTypeChange(key: number | string) {
  sceneType.value = key.toString();
  gridApi.query();
}
const gridColumns = [
  { field: 'name', title: '客户名称', minWidth: 180 },
  { field: 'mobile', title: '手机号', width: 140 },
@@ -137,12 +138,14 @@
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          return await getCustomerPage({
          const query = {
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            sceneType: sceneType.value,
            ...formValues,
          });
          };
          return props.allCustomers
            ? await getBusinessCustomerPage(query)
            : await getCustomerPage({ ...query, sceneType: sceneType.value });
        },
      },
    },
@@ -226,7 +229,7 @@
    @ok="handleConfirm"
    @cancel="closeModal"
  >
    <Tabs class="mb-2" @change="handleSceneTypeChange">
    <Tabs v-if="!allCustomers" class="mb-2" @change="handleSceneTypeChange">
      <Tabs.TabPane key="1" tab="我负责的" />
      <Tabs.TabPane key="2" tab="我参与的" />
      <Tabs.TabPane key="3" tab="下属负责的" />
src/components/crm-customer-select.vue
@@ -2,12 +2,13 @@
import type { CrmCustomerApi } from '#/api/crm/customer';
import { computed, ref, useAttrs, watch } from 'vue';
import { useRoute } from 'vue-router';
import { IconifyIcon } from '@vben/icons';
import { Input, Tooltip } from 'ant-design-vue';
import { getCustomer } from '#/api/crm/customer';
import { getBusinessCustomer, getCustomer } from '#/api/crm/customer';
import CrmCustomerSelectDialog from './crm-customer-select-dialog.vue';
@@ -16,6 +17,7 @@
const props = withDefaults(
  defineProps<{
    allowClear?: boolean;
    allCustomers?: boolean;
    disabled?: boolean;
    modelValue?: number;
    placeholder?: string;
@@ -26,6 +28,10 @@
    modelValue: undefined,
    placeholder: '请选择客户',
  },
);
const route = useRoute();
const useAllCustomers = computed(
  () => props.allCustomers ?? !String(route.path).startsWith('/crm/'),
);
const emit = defineEmits<{
  change: [item: CrmCustomerApi.Customer | undefined];
@@ -53,7 +59,9 @@
  if (selectedItem.value?.id === id) {
    return;
  }
  selectedItem.value = await getCustomer(id);
  selectedItem.value = useAllCustomers.value
    ? await getBusinessCustomer(id)
    : await getCustomer(id);
}
watch(
@@ -129,5 +137,9 @@
      </Input>
    </Tooltip>
  </div>
  <CrmCustomerSelectDialog ref="dialogRef" @selected="handleSelected" />
  <CrmCustomerSelectDialog
    ref="dialogRef"
    :all-customers="useAllCustomers"
    @selected="handleSelected"
  />
</template>
src/views/mes/pro/mps/data.ts
@@ -96,6 +96,7 @@
      label: '客户',
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        allCustomers: true,
        placeholder: '请选择客户',
        disabled: !isCreate && !isEdit,
      },
@@ -161,6 +162,7 @@
      label: '客户',
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        allCustomers: true,
        placeholder: '请选择客户',
      },
    },
src/views/mes/qc/oqc/data.ts
@@ -129,6 +129,7 @@
      dependencies: {
        triggerFields: ['sourceDocId'],
        componentProps: (values) => ({
          allCustomers: true,
          disabled: !!values.sourceDocId,
          placeholder: '请选择产品物料',
        }),
@@ -166,6 +167,7 @@
      label: '客户',
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        allCustomers: true,
        allowClear: true,
        placeholder: '请选择客户(可不填)',
      },
@@ -340,6 +342,7 @@
      label: '客户',
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        allCustomers: true,
        placeholder: '请选择客户',
      },
    },
src/views/mes/qc/oqc/index.vue
@@ -55,8 +55,8 @@
      importApi: importOqc,
      fileName: '出货检验单导入模板.xls',
      hints: [
        '仅允许导入 xls、xlsx 格式文件;必填列:检验单名称、产品物料编码、客户名称、本次出货数量、合格品数量、不合格品数量、检测日期、出货日期。',
        '「产品物料编码」需与「物料管理」中的编码完全一致;「客户名称」需与「CRM 客户管理」中的名称一致,且必须在当前登录用户的客户数据权限范围内。',
        '仅允许导入 xls、xlsx 格式文件;必填列:检验单名称、产品物料编码、本次出货数量、合格品数量、不合格品数量、检测日期、出货日期。客户名称可选。',
        '「产品物料编码」需与「物料管理」中的编码完全一致;填写客户名称时,需与「CRM 客户管理」中的客户名称一致。',
        '合格品数量 + 不合格品数量 必须等于「检测数量」(留空时自动取两者之和);「检测结果」按字典名称填写(校验通过 / 校验不通过)。',
        '「质检方案」填方案名称,留空则按物料自动匹配 OQC 方案;「检测人员」留空取当前登录用户;日期支持 2026-01-01 10:00:00 或 2026-01-01。',
        '逐行导入,失败行不影响成功行;导入后的检验单为「待检验」状态,可在列表中继续编辑、录入检测结果。',
src/views/wls/productsales/data.ts
@@ -191,6 +191,7 @@
      label: '客户',
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        allCustomers: true,
        disabled: isHeaderReadonly(formType),
        placeholder: '请选择客户',
      },
@@ -298,6 +299,7 @@
      label: '客户',
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        allCustomers: true,
        placeholder: '请选择客户',
      },
    },
src/views/wls/returnsales/data.ts
@@ -121,6 +121,7 @@
      label: '客户',
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        allCustomers: true,
        placeholder: '请选择客户',
      },
      rules: 'selectRequired',
@@ -196,6 +197,7 @@
      label: '客户',
      component: markRaw(CrmCustomerSelect),
      componentProps: {
        allCustomers: true,
        placeholder: '请选择客户',
      },
    },