feat(crm): 添加业务模块客户选择功能
- 新增 allCustomers 属性控制客户选择范围
- 实现路由路径判断逻辑,非CRM模块默认使用全部客户
- 添加 getBusinessCustomer 和 getBusinessCustomerPage API方法
- 在MES、WLS等业务模块表单中启用全部客户选择
- 更新OQC导入提示信息,调整客户名称字段说明
- 优化客户选择对话框的tabs显示逻辑
| | |
| | | ); |
| | | } |
| | | |
| | | /** 查询业务模块客户列表(不受 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>( |
| | |
| | | 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[]; |
| | |
| | | } |
| | | } |
| | | |
| | | function handleSceneTypeChange(key: number | string) { |
| | | sceneType.value = key.toString(); |
| | | gridApi.query(); |
| | | } |
| | | |
| | | const gridColumns = [ |
| | | { field: 'name', title: '客户名称', minWidth: 180 }, |
| | | { field: 'mobile', title: '手机号', width: 140 }, |
| | |
| | | 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 }); |
| | | }, |
| | | }, |
| | | }, |
| | |
| | | @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="下属负责的" /> |
| | |
| | | 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'; |
| | | |
| | |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | | allowClear?: boolean; |
| | | allCustomers?: boolean; |
| | | disabled?: boolean; |
| | | modelValue?: number; |
| | | placeholder?: string; |
| | |
| | | modelValue: undefined, |
| | | placeholder: '请选择客户', |
| | | }, |
| | | ); |
| | | const route = useRoute(); |
| | | const useAllCustomers = computed( |
| | | () => props.allCustomers ?? !String(route.path).startsWith('/crm/'), |
| | | ); |
| | | const emit = defineEmits<{ |
| | | change: [item: CrmCustomerApi.Customer | undefined]; |
| | |
| | | if (selectedItem.value?.id === id) { |
| | | return; |
| | | } |
| | | selectedItem.value = await getCustomer(id); |
| | | selectedItem.value = useAllCustomers.value |
| | | ? await getBusinessCustomer(id) |
| | | : await getCustomer(id); |
| | | } |
| | | |
| | | watch( |
| | |
| | | </Input> |
| | | </Tooltip> |
| | | </div> |
| | | <CrmCustomerSelectDialog ref="dialogRef" @selected="handleSelected" /> |
| | | <CrmCustomerSelectDialog |
| | | ref="dialogRef" |
| | | :all-customers="useAllCustomers" |
| | | @selected="handleSelected" |
| | | /> |
| | | </template> |
| | |
| | | label: '客户', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | allCustomers: true, |
| | | placeholder: '请选择客户', |
| | | disabled: !isCreate && !isEdit, |
| | | }, |
| | |
| | | label: '客户', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | allCustomers: true, |
| | | placeholder: '请选择客户', |
| | | }, |
| | | }, |
| | |
| | | dependencies: { |
| | | triggerFields: ['sourceDocId'], |
| | | componentProps: (values) => ({ |
| | | allCustomers: true, |
| | | disabled: !!values.sourceDocId, |
| | | placeholder: '请选择产品物料', |
| | | }), |
| | |
| | | label: '客户', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | allCustomers: true, |
| | | allowClear: true, |
| | | placeholder: '请选择客户(可不填)', |
| | | }, |
| | |
| | | label: '客户', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | allCustomers: true, |
| | | placeholder: '请选择客户', |
| | | }, |
| | | }, |
| | |
| | | importApi: importOqc, |
| | | fileName: '出货检验单导入模板.xls', |
| | | hints: [ |
| | | '仅允许导入 xls、xlsx 格式文件;必填列:检验单名称、产品物料编码、客户名称、本次出货数量、合格品数量、不合格品数量、检测日期、出货日期。', |
| | | '「产品物料编码」需与「物料管理」中的编码完全一致;「客户名称」需与「CRM 客户管理」中的名称一致,且必须在当前登录用户的客户数据权限范围内。', |
| | | '仅允许导入 xls、xlsx 格式文件;必填列:检验单名称、产品物料编码、本次出货数量、合格品数量、不合格品数量、检测日期、出货日期。客户名称可选。', |
| | | '「产品物料编码」需与「物料管理」中的编码完全一致;填写客户名称时,需与「CRM 客户管理」中的客户名称一致。', |
| | | '合格品数量 + 不合格品数量 必须等于「检测数量」(留空时自动取两者之和);「检测结果」按字典名称填写(校验通过 / 校验不通过)。', |
| | | '「质检方案」填方案名称,留空则按物料自动匹配 OQC 方案;「检测人员」留空取当前登录用户;日期支持 2026-01-01 10:00:00 或 2026-01-01。', |
| | | '逐行导入,失败行不影响成功行;导入后的检验单为「待检验」状态,可在列表中继续编辑、录入检测结果。', |
| | |
| | | label: '客户', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | allCustomers: true, |
| | | disabled: isHeaderReadonly(formType), |
| | | placeholder: '请选择客户', |
| | | }, |
| | |
| | | label: '客户', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | allCustomers: true, |
| | | placeholder: '请选择客户', |
| | | }, |
| | | }, |
| | |
| | | label: '客户', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | allCustomers: true, |
| | | placeholder: '请选择客户', |
| | | }, |
| | | rules: 'selectRequired', |
| | |
| | | label: '客户', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | allCustomers: true, |
| | | placeholder: '请选择客户', |
| | | }, |
| | | }, |