Browse Source

📖 CRM:code review 商机模块

YunaiV 1 year ago
parent
commit
0a4023ef00

+ 11 - 10
src/api/crm/contact/index.ts

@@ -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 })
+}

+ 4 - 4
src/views/crm/business/BusinessForm.vue

@@ -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 {

+ 75 - 6
src/views/crm/business/components/BusinessList.vue

@@ -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],

+ 0 - 141
src/views/crm/business/components/BusinessListByContact.vue

@@ -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>

+ 9 - 18
src/views/crm/business/components/BusinessForContactLink.vue → src/views/crm/business/components/BusinessListModal.vue

@@ -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 - 0
src/views/crm/contact/detail/ContactDetailsInfo.vue

@@ -1,4 +1,5 @@
 <template>
+  <!-- TODO @zyna:少了一个外边框 -->
   <el-collapse v-model="activeNames">
     <el-collapse-item name="basicInfo">
       <template #title>

+ 7 - 3
src/views/crm/contact/detail/index.vue

@@ -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'