📖 CRM:code review 商机模块
							parent
							
								
									45521c139c
								
							
						
					
					
						commit
						0a4023ef00
					
				|  | @ -25,11 +25,12 @@ export interface ContactVO { | |||
|   areaName: string | ||||
|   ownerUserName: string | ||||
| } | ||||
| export interface ContactBusinessLinkVO { | ||||
|   id: number | ||||
| 
 | ||||
| export interface ContactBusinessReqVO { | ||||
|   contactId: number | ||||
|   businessId: number | ||||
|   businessIds: number[] | ||||
| } | ||||
| 
 | ||||
| // 查询 CRM 联系人列表
 | ||||
| export const getContactPage = async (params) => { | ||||
|   return await request.get({ url: `/crm/contact/page`, params }) | ||||
|  | @ -70,12 +71,12 @@ export const getSimpleContactList = async () => { | |||
|   return await request.get({ url: `/crm/contact/simple-all-list` }) | ||||
| } | ||||
| 
 | ||||
| //批量新增联系人商机关联
 | ||||
| export const createContactBusinessLinkBatch = async (data: ContactBusinessLinkVO[]) => { | ||||
|   return await request.post({ url: `/crm/contact/create-batch-business`, data }) | ||||
| // 批量新增联系人商机关联
 | ||||
| export const createContactBusinessList = async (data: ContactBusinessReqVO) => { | ||||
|   return await request.post({ url: `/crm/contact/create-business-list`, data }) | ||||
| } | ||||
| 
 | ||||
| //解除联系人商机关联
 | ||||
| export const deleteContactBusinessLink = async (data: ContactBusinessLinkVO) => { | ||||
|   return await request.delete({ url: `/crm/contact/delete-batch-business`, data }) | ||||
| // 解除联系人商机关联
 | ||||
| export const deleteContactBusinessList = async (data: ContactBusinessReqVO) => { | ||||
|   return await request.delete({ url: `/crm/contact/delete-business-list`, data }) | ||||
| } | ||||
|  | @ -62,12 +62,12 @@ | |||
|           </el-row> | ||||
|         </el-popover> | ||||
|       </el-form-item> | ||||
|       <!-- TODO @ljlleo:idea 红色的报错,可以解决下 --> | ||||
|       <el-form-item label="商机状态类型" prop="statusTypeId"> | ||||
|         <el-select | ||||
|           v-model="formData.statusTypeId" | ||||
|           placeholder="请选择商机状态类型" | ||||
|           clearable | ||||
|           size="small" | ||||
|           @change="changeBusinessStatusType" | ||||
|         > | ||||
|           <el-option | ||||
|  | @ -79,7 +79,7 @@ | |||
|         </el-select> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="商机状态" prop="statusId"> | ||||
|         <el-select v-model="formData.statusId" placeholder="请选择商机状态" clearable size="small"> | ||||
|         <el-select v-model="formData.statusId" placeholder="请选择商机状态" clearable> | ||||
|           <el-option | ||||
|             v-for="item in businessStatusList" | ||||
|             :key="item.id" | ||||
|  | @ -243,7 +243,7 @@ const queryParams = reactive({ | |||
|   industryId: null, | ||||
|   level: null, | ||||
|   source: null, | ||||
|   pool:false | ||||
|   pool: false | ||||
| }) | ||||
| // 选择客户 | ||||
| const showCustomer = ref(false) | ||||
|  | @ -252,12 +252,12 @@ const openCustomerSelect = () => { | |||
|   queryParams.pageNo = 1 | ||||
|   getCustomerList() | ||||
| } | ||||
| 
 | ||||
| /** 查询客户列表 */ | ||||
| const getCustomerList = async () => { | ||||
|   loading.value = true | ||||
|   try { | ||||
|     const data = await CustomerApi.getCustomerPage(queryParams) | ||||
|     console.log(JSON.stringify(data)) | ||||
|     customerList.value = data.list | ||||
|     total.value = data.total | ||||
|   } finally { | ||||
|  |  | |||
|  | @ -5,11 +5,32 @@ | |||
|       <Icon class="mr-5px" icon="ep:opportunity" /> | ||||
|       创建商机 | ||||
|     </el-button> | ||||
|     <el-button | ||||
|       @click="openBusinessModal" | ||||
|       v-hasPermi="['crm:contact:create-business']" | ||||
|       v-if="queryParams.contactId" | ||||
|     > | ||||
|       <Icon class="mr-5px" icon="ep:circle-plus" />关联 | ||||
|     </el-button> | ||||
|     <el-button | ||||
|       @click="deleteContactBusinessList" | ||||
|       v-hasPermi="['crm:contact:delete-business']" | ||||
|       v-if="queryParams.contactId" | ||||
|     > | ||||
|       <Icon class="mr-5px" icon="ep:remove" />解除关联 | ||||
|     </el-button> | ||||
|   </el-row> | ||||
| 
 | ||||
|   <!-- 列表 --> | ||||
|   <ContentWrap class="mt-10px"> | ||||
|     <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> | ||||
|     <el-table | ||||
|       ref="businessRef" | ||||
|       v-loading="loading" | ||||
|       :data="list" | ||||
|       :stripe="true" | ||||
|       :show-overflow-tooltip="true" | ||||
|     > | ||||
|       <el-table-column type="selection" width="55" v-if="queryParams.contactId" /> | ||||
|       <el-table-column label="商机名称" fixed="left" align="center" prop="name"> | ||||
|         <template #default="scope"> | ||||
|           <el-link type="primary" :underline="false" @click="openDetail(scope.row.id)"> | ||||
|  | @ -33,18 +54,28 @@ | |||
| 
 | ||||
|   <!-- 表单弹窗:添加 --> | ||||
|   <BusinessForm ref="formRef" @success="getList" /> | ||||
|   <!-- 关联商机选择弹框 --> | ||||
|   <BusinessListModal | ||||
|     ref="businessModalRef" | ||||
|     :customer-id="props.customerId" | ||||
|     @success="createContactBusinessList" | ||||
|   /> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import * as BusinessApi from '@/api/crm/business' | ||||
| import * as ContactApi from '@/api/crm/contact' | ||||
| import BusinessForm from './../BusinessForm.vue' | ||||
| import { BizTypeEnum } from '@/api/crm/permission' | ||||
| import { fenToYuanFormat } from '@/utils/formatter' | ||||
| import * as ContactApi from '@/api/crm/contact' | ||||
| import BusinessListModal from './BusinessListModal.vue' | ||||
| 
 | ||||
| const message = useMessage() // 消息 | ||||
| 
 | ||||
| defineOptions({ name: 'CrmBusinessList' }) | ||||
| const props = defineProps<{ | ||||
|   bizType: number // 业务类型 | ||||
|   bizId: number // 业务编号 | ||||
|   customerId: number // 关联联系人与商机时,需要传入 customerId 进行筛选 | ||||
| }>() | ||||
| 
 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
|  | @ -53,7 +84,8 @@ const list = ref([]) // 列表的数据 | |||
| const queryParams = reactive({ | ||||
|   pageNo: 1, | ||||
|   pageSize: 10, | ||||
|   customerId: undefined as unknown // 允许 undefined + number | ||||
|   customerId: undefined as unknown, // 允许 undefined + number | ||||
|   contactId: undefined as unknown // 允许 undefined + number | ||||
| }) | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
|  | @ -62,21 +94,23 @@ const getList = async () => { | |||
|   try { | ||||
|     // 置空参数 | ||||
|     queryParams.customerId = undefined | ||||
|     queryParams.contactId = undefined | ||||
|     // 执行查询 | ||||
|     let data = { list: [], total: 0 } | ||||
|     switch (props.bizType) { | ||||
|       case BizTypeEnum.CRM_CUSTOMER: | ||||
|         queryParams.customerId = props.bizId | ||||
|         data = await BusinessApi.getBusinessPageByCustomer(queryParams) | ||||
|          | ||||
|         console.log(data) | ||||
|         break | ||||
|       case BizTypeEnum.CRM_CONTACT: | ||||
|         queryParams.contactId = props.bizId | ||||
|         data = await BusinessApi.getBusinessPageByContact(queryParams) | ||||
|         break | ||||
|       default: | ||||
|         return | ||||
|     } | ||||
|     list.value = data.list | ||||
|     total.value = data.total | ||||
|     console.log(list.value) | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
|  | @ -100,6 +134,41 @@ const openDetail = (id: number) => { | |||
|   push({ name: 'CrmBusinessDetail', params: { id } }) | ||||
| } | ||||
| 
 | ||||
| /** 打开联系人与商机的关联弹窗 */ | ||||
| const businessModalRef = ref() | ||||
| const openBusinessModal = () => { | ||||
|   businessModalRef.value.open() | ||||
| } | ||||
| const createContactBusinessList = async (businessIds: number[]) => { | ||||
|   const data = { | ||||
|     contactId: props.bizId, | ||||
|     businessIds: businessIds | ||||
|   } as ContactApi.ContactBusinessReqVO | ||||
|   businessRef.value.getSelectionRows().forEach((row: BusinessApi.BusinessVO) => { | ||||
|     data.businessIds.push(row.id) | ||||
|   }) | ||||
|   await ContactApi.createContactBusinessList(data) | ||||
|   // 刷新列表 | ||||
|   message.success('关联商机成功') | ||||
|   handleQuery() | ||||
| } | ||||
| 
 | ||||
| /** 解除联系人与商机的关联 */ | ||||
| const businessRef = ref() | ||||
| const deleteContactBusinessList = async () => { | ||||
|   const data = { | ||||
|     contactId: props.bizId, | ||||
|     businessIds: businessRef.value.getSelectionRows().map((row: BusinessApi.BusinessVO) => row.id) | ||||
|   } as ContactApi.ContactBusinessReqVO | ||||
|   if (data.businessIds.length === 0) { | ||||
|     return message.error('未选择商机') | ||||
|   } | ||||
|   await ContactApi.deleteContactBusinessList(data) | ||||
|   // 刷新列表 | ||||
|   message.success('取关商机成功') | ||||
|   handleQuery() | ||||
| } | ||||
| 
 | ||||
| /** 监听打开的 bizId + bizType,从而加载最新的列表 */ | ||||
| watch( | ||||
|   () => [props.bizId, props.bizType], | ||||
|  |  | |||
|  | @ -1,141 +0,0 @@ | |||
| <template> | ||||
|   <!-- 操作栏 --> | ||||
|   <el-row justify="end"> | ||||
|     <el-button @click="openForm"> | ||||
|       <Icon class="mr-5px" icon="ep:opportunity" /> | ||||
|       创建商机 | ||||
|     </el-button> | ||||
|     <el-button @click="openBusinessLink">  <Icon class="mr-5px" icon="ep:circle-plus"/>关联 </el-button> | ||||
|     <el-button @click="deleteBusinessLink">  <Icon class="mr-5px" icon="ep:remove"  />解除关联 </el-button> | ||||
|   </el-row> | ||||
| 
 | ||||
|   <!-- 列表 --> | ||||
|   <ContentWrap class="mt-10px"> | ||||
|     <el-table | ||||
|       v-loading="loading" | ||||
|       ref="businessRef" | ||||
|       :data="list" | ||||
|       :stripe="true" | ||||
|       :show-overflow-tooltip="true" | ||||
|     > | ||||
|       <el-table-column type="selection" width="55" /> | ||||
|       <el-table-column label="商机名称" fixed="left" align="center" prop="name"> | ||||
|         <template #default="scope"> | ||||
|           <el-link type="primary" :underline="false" @click="openDetail(scope.row.id)"> | ||||
|             {{ scope.row.name }} | ||||
|           </el-link> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="商机金额" align="center" prop="price" :formatter="fenToYuanFormat" /> | ||||
|       <el-table-column label="客户名称" align="center" prop="customerName" /> | ||||
|       <el-table-column label="商机组" align="center" prop="statusTypeName" /> | ||||
|       <el-table-column label="商机阶段" align="center" prop="statusName" /> | ||||
|     </el-table> | ||||
|     <!-- 分页 --> | ||||
|     <Pagination | ||||
|       :total="total" | ||||
|       v-model:page="queryParams.pageNo" | ||||
|       v-model:limit="queryParams.pageSize" | ||||
|       @pagination="getList" | ||||
|     /> | ||||
|   </ContentWrap> | ||||
| 
 | ||||
|   <!-- 表单弹窗:添加 --> | ||||
|   <BusinessForm ref="formRef" @success="getList"/> | ||||
|   <!--关联商机选择弹框--> | ||||
|   <BusinessLink ref="businessLinkRef" @success="getList" :customer-id="props.customerId"/> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import * as BusinessApi from '@/api/crm/business' | ||||
| import BusinessForm from '../BusinessForm.vue' | ||||
| import { BizTypeEnum } from '@/api/crm/permission' | ||||
| import { fenToYuanFormat } from '@/utils/formatter' | ||||
| import BusinessLink from './BusinessForContactLink.vue' | ||||
| import * as ContactApi from '@/api/crm/contact' | ||||
| import { el } from 'element-plus/es/locale' | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| defineOptions({ name: 'CrmBusinessContactList' }) | ||||
| const props = defineProps<{ | ||||
|   bizType: number // 业务类型 | ||||
|   bizId: number // 业务编号 | ||||
|   customerId: number  | ||||
| }>() | ||||
| 
 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref([]) // 列表的数据 | ||||
| const queryParams = reactive({ | ||||
|   pageNo: 1, | ||||
|   pageSize: 10, | ||||
|   contactId: undefined as unknown // 允许 undefined + number | ||||
| }) | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true | ||||
|   try { | ||||
|     // 置空参数 | ||||
|     queryParams.contactId = undefined | ||||
|     // 执行查询 | ||||
|     let data = { list: [], total: 0 } | ||||
|     switch (props.bizType) { | ||||
|       case BizTypeEnum.CRM_CONTACT: | ||||
|         queryParams.contactId = props.bizId | ||||
|         data = await BusinessApi.getBusinessPageByContact(queryParams) | ||||
|         break | ||||
|       default: | ||||
|         return | ||||
|     } | ||||
|     list.value = data.list | ||||
|     total.value = data.total | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.pageNo = 1 | ||||
|   getList() | ||||
| } | ||||
| 
 | ||||
| /** 添加操作 */ | ||||
| const formRef = ref() | ||||
| const openForm = () => { | ||||
|   formRef.value.open('create') | ||||
| } | ||||
| /** 关联操作 */ | ||||
| const businessLinkRef = ref() | ||||
| const openBusinessLink = () => { | ||||
|   businessLinkRef.value.open(props.bizId) | ||||
| } | ||||
| /**解除关联 */ | ||||
| const businessRef = ref() | ||||
| const deleteBusinessLink = async () => { | ||||
|   if (businessRef.value.getSelectionRows().length === 0) { | ||||
|     message.success('未选择商机') | ||||
|   } else { | ||||
|     const postData = [] | ||||
|     businessRef.value.getSelectionRows().forEach((element) => { | ||||
|       postData.push(element.businessContactId) | ||||
|     }) | ||||
|     await ContactApi.deleteContactBusinessLink(postData) | ||||
|     handleQuery() | ||||
|   } | ||||
| } | ||||
| /** 打开联系人详情 */ | ||||
| const { push } = useRouter() | ||||
| const openDetail = (id: number) => { | ||||
|   push({ name: 'CrmBusinessDetail', params: { id } }) | ||||
| } | ||||
| 
 | ||||
| /** 监听打开的 bizId + bizType,从而加载最新的列表 */ | ||||
| watch( | ||||
|   () => [props.bizId, props.bizType], | ||||
|   () => { | ||||
|     handleQuery() | ||||
|   }, | ||||
|   { immediate: true, deep: true } | ||||
| ) | ||||
| </script> | ||||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
|   <Dialog :title="dialogTitle" v-model="dialogVisible"> | ||||
|   <Dialog title="关联商机" v-model="dialogVisible"> | ||||
|     <!-- 搜索工作栏 --> | ||||
|     <ContentWrap> | ||||
|       <el-form | ||||
|  | @ -76,16 +76,14 @@ | |||
| import * as BusinessApi from '@/api/crm/business' | ||||
| import BusinessForm from '../BusinessForm.vue' | ||||
| import { fenToYuanFormat } from '@/utils/formatter' | ||||
| import * as ContactApi from '@/api/crm/contact' | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const props = defineProps<{ | ||||
|   customerId: number | ||||
| }>() | ||||
| defineOptions({ name: 'CrmBusinessLinkContactList' }) | ||||
| defineOptions({ name: 'BusinessListModal' }) | ||||
| 
 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const dialogTitle = ref('') // 弹窗的标题 TODO @zyna:是不是搞个标题? | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref([]) // 列表的数据 | ||||
|  | @ -97,12 +95,10 @@ const queryParams = reactive({ | |||
|   name: undefined, | ||||
|   customerId: props.customerId | ||||
| }) | ||||
| const contactIdProp = ref(0) // 联系人编号 | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (contactId: number) => { | ||||
| const open = async () => { | ||||
|   dialogVisible.value = true | ||||
|   contactIdProp.value = contactId | ||||
|   await getList() | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
|  | @ -141,19 +137,14 @@ const openForm = () => { | |||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const businessRef = ref() | ||||
| const submitForm = async () => { | ||||
|   if (businessRef.value.getSelectionRows().length === 0) { | ||||
|     return message.success('未选择商机') | ||||
|   const businessIds = businessRef.value | ||||
|     .getSelectionRows() | ||||
|     .map((row: BusinessApi.BusinessVO) => row.id) | ||||
|   if (businessIds.length === 0) { | ||||
|     return message.error('未选择商机') | ||||
|   } | ||||
|   const postData = [] | ||||
|   businessRef.value.getSelectionRows().forEach((element) => { | ||||
|     postData.push({ | ||||
|       businessId: element.id, | ||||
|       contactId: contactIdProp.value | ||||
|     }) | ||||
|   }) | ||||
|   await ContactApi.createContactBusinessLinkBatch(postData) | ||||
|   dialogVisible.value = false | ||||
|   emit('success') | ||||
|   emit('success', businessIds) | ||||
| } | ||||
| 
 | ||||
| /** 打开联系人详情 */ | ||||
|  | @ -1,4 +1,5 @@ | |||
| <template> | ||||
|   <!-- TODO @zyna:少了一个外边框 --> | ||||
|   <el-collapse v-model="activeNames"> | ||||
|     <el-collapse-item name="basicInfo"> | ||||
|       <template #title> | ||||
|  |  | |||
|  | @ -10,18 +10,22 @@ | |||
|         <PermissionList :biz-id="contact.id!" :biz-type="BizTypeEnum.CRM_CONTACT" /> | ||||
|       </el-tab-pane> | ||||
|       <el-tab-pane label="商机" lazy> | ||||
|         <BusinessList :biz-id="contact.id!" :biz-type="BizTypeEnum.CRM_CONTACT" :customer-id="contact.customerId" /> | ||||
|         <BusinessList | ||||
|           :biz-id="contact.id!" | ||||
|           :biz-type="BizTypeEnum.CRM_CONTACT" | ||||
|           :customer-id="contact.customerId" | ||||
|         /> | ||||
|       </el-tab-pane> | ||||
|     </el-tabs> | ||||
|   </el-col> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import { ElMessage } from 'element-plus' | ||||
| import { ElMessage } from 'element-plus' // TODO @zyna:使用 hook 引入 message | ||||
| import { useTagsViewStore } from '@/store/modules/tagsView' | ||||
| import * as ContactApi from '@/api/crm/contact' | ||||
| import ContactDetailsHeader from '@/views/crm/contact/detail/ContactDetailsHeader.vue' | ||||
| import ContactDetailsInfo from '@/views/crm/contact/detail/ContactDetailsInfo.vue' | ||||
| import BusinessList from '@/views/crm/business/components/BusinessListByContact.vue' // 商机列表 | ||||
| import BusinessList from '@/views/crm/business/components/BusinessList.vue' // 商机列表 | ||||
| import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限) | ||||
| import { BizTypeEnum } from '@/api/crm/permission' | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 YunaiV
						YunaiV