Переглянути джерело

✨ CRM:完善 CRM 跟进记录(联系人的展示、添加)

YunaiV 1 рік тому
батько
коміт
7164ae5d49

+ 0 - 5
src/api/crm/contact/index.ts

@@ -82,11 +82,6 @@ export const getSimpleContactList = async () => {
   return await request.get({ url: `/crm/contact/simple-all-list` })
 }
 
-// 获得 CRM 联系人列表
-export const getContactListByIds = async (val: number[]) => {
-  return await request.get({ url: '/crm/contact/list-by-ids', params: { ids: val.join(',') } })
-}
-
 // 批量新增联系人商机关联
 export const createContactBusinessList = async (data: ContactBusinessReqVO) => {
   return await request.post({ url: `/crm/contact/create-business-list`, data })

+ 1 - 1
src/views/crm/business/components/BusinessListModal.vue

@@ -148,7 +148,7 @@ const submitForm = async () => {
   emit('success', businessIds, businessRef.value.getSelectionRows())
 }
 
-/** 打开联系人详情 */
+/** 打开商机详情 */
 const { push } = useRouter()
 const openDetail = (id: number) => {
   push({ name: 'CrmBusinessDetail', params: { id } })

+ 1 - 1
src/views/crm/contact/components/ContactList.vue

@@ -20,7 +20,7 @@
       <el-table-column label="手机号" align="center" prop="mobile" />
       <el-table-column label="职位" align="center" prop="post" />
       <el-table-column label="直属上级" align="center" prop="parentName" />
-      <el-table-column label="是否关键决策人" align="center" prop="master">
+      <el-table-column label="是否关键决策人" align="center" prop="master" min-width="100">
         <template #default="scope">
           <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
         </template>

+ 154 - 0
src/views/crm/contact/components/ContactListModal.vue

@@ -0,0 +1,154 @@
+<template>
+  <Dialog title="关联联系人" v-model="dialogVisible">
+    <!-- 搜索工作栏 -->
+    <ContentWrap>
+      <el-form
+        class="-mb-15px"
+        :model="queryParams"
+        ref="queryFormRef"
+        :inline="true"
+        label-width="90px"
+      >
+        <el-form-item label="联系人名称" prop="name">
+          <el-input
+            v-model="queryParams.name"
+            placeholder="请输入联系人名称"
+            clearable
+            @keyup.enter="handleQuery"
+            class="!w-240px"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+          <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+          <el-button type="primary" @click="openForm()" v-hasPermi="['crm:business:create']">
+            <Icon icon="ep:plus" class="mr-5px" /> 新增
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </ContentWrap>
+
+    <!-- 列表 -->
+    <ContentWrap class="mt-10px">
+      <el-table
+        v-loading="loading"
+        ref="contactRef"
+        :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="mobile" />
+        <el-table-column label="职位" align="center" prop="post" />
+        <el-table-column label="直属上级" align="center" prop="parentName" />
+        <el-table-column label="是否关键决策人" align="center" prop="master" min-width="100">
+          <template #default="scope">
+            <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页 -->
+      <Pagination
+        :total="total"
+        v-model:page="queryParams.pageNo"
+        v-model:limit="queryParams.pageSize"
+        @pagination="getList"
+      />
+    </ContentWrap>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+
+    <!-- 表单弹窗:添加 -->
+    <ContactForm ref="formRef" @success="getList" />
+  </Dialog>
+</template>
+<script setup lang="ts">
+import * as ContactApi from '@/api/crm/contact'
+import ContactForm from '../ContactForm.vue'
+import { erpPriceTableColumnFormatter } from '@/utils'
+import { DICT_TYPE } from '@/utils/dict'
+
+const message = useMessage() // 消息弹窗
+const props = defineProps<{
+  customerId: number
+}>()
+defineOptions({ name: 'ContactListModal' })
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryFormRef = ref() // 搜索的表单
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  name: undefined,
+  customerId: props.customerId
+})
+
+/** 打开弹窗 */
+const open = async () => {
+  dialogVisible.value = true
+  queryParams.customerId = props.customerId // 解决 props.customerId 没更新到 queryParams 上的问题
+  await getList()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await ContactApi.getContactPageByCustomer(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 添加操作 */
+const formRef = ref()
+const openForm = () => {
+  formRef.value.open('create')
+}
+
+/** 关联联系人提交 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const contactRef = ref()
+const submitForm = async () => {
+  const contactIds = contactRef.value.getSelectionRows().map((row: ContactApi.ContactVO) => row.id)
+  if (contactIds.length === 0) {
+    return message.error('未选择联系人')
+  }
+  dialogVisible.value = false
+  emit('success', contactIds, contactRef.value.getSelectionRows())
+}
+
+/** 打开联系人详情 */
+const { push } = useRouter()
+const openDetail = (id: number) => {
+  push({ name: 'CrmContactDetail', params: { id } })
+}
+</script>

+ 24 - 7
src/views/crm/followup/FollowUpRecordForm.vue

@@ -48,11 +48,11 @@
         </el-col>
         <el-col :span="24" v-if="formData.bizType == BizTypeEnum.CRM_CUSTOMER">
           <el-form-item label="关联联系人" prop="contactIds">
-            <el-button @click="handleAddContact">
+            <el-button @click="handleOpenContact">
               <Icon class="mr-5px" icon="ep:plus" />
               添加联系人
             </el-button>
-            <contact-list v-model:contactIds="formData.contactIds" />
+            <FollowUpRecordContactForm :contacts="formData.contacts" />
           </el-form-item>
         </el-col>
         <el-col :span="24" v-if="formData.bizType == BizTypeEnum.CRM_CUSTOMER">
@@ -73,7 +73,11 @@
   </Dialog>
 
   <!-- 弹窗 -->
-  <ContactTableSelect ref="contactTableSelectRef" v-model="formData.contactIds" />
+  <ContactListModal
+    ref="contactTableSelectRef"
+    :customer-id="formData.bizId"
+    @success="handleAddContact"
+  />
   <BusinessListModal
     ref="businessTableSelectRef"
     :customer-id="formData.bizId"
@@ -83,11 +87,13 @@
 <script lang="ts" setup>
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 import { FollowUpRecordApi, FollowUpRecordVO } from '@/api/crm/followup'
-import { ContactList, ContactTableSelect } from './components'
 import { BizTypeEnum } from '@/api/crm/permission'
 import FollowUpRecordBusinessForm from './components/FollowUpRecordBusinessForm.vue'
+import FollowUpRecordContactForm from './components/FollowUpRecordContactForm.vue'
 import BusinessListModal from '@/views/crm/business/components/BusinessListModal.vue'
 import * as BusinessApi from '@/api/crm/business'
+import ContactListModal from '@/views/crm/contact/components/ContactListModal.vue'
+import * as ContactApi from '@/api/crm/contact'
 
 defineOptions({ name: 'FollowUpRecordForm' })
 
@@ -128,7 +134,11 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
-    const data = formData.value as unknown as FollowUpRecordVO
+    const data = {
+      ...formData.value,
+      contactIds: formData.value.contacts.map((item) => item.id),
+      businessIds: formData.value.businesses.map((item) => item.id)
+    } as unknown as FollowUpRecordVO
     await FollowUpRecordApi.createFollowUpRecord(data)
     message.success(t('common.createSuccess'))
     dialogVisible.value = false
@@ -140,10 +150,17 @@ const submitForm = async () => {
 }
 
 /** 关联联系人 */
-const contactTableSelectRef = ref<InstanceType<typeof ContactTableSelect>>()
-const handleAddContact = () => {
+const contactTableSelectRef = ref<InstanceType<typeof ContactListModal>>()
+const handleOpenContact = () => {
   contactTableSelectRef.value?.open()
 }
+const handleAddContact = (contactId: [], newContacts: ContactApi.ContactVO[]) => {
+  newContacts.forEach((contact) => {
+    if (!formData.value.contacts.some((item) => item.id === contact.id)) {
+      formData.value.contacts.push(contact)
+    }
+  })
+}
 
 /** 关联商机 */
 const businessTableSelectRef = ref<InstanceType<typeof BusinessListModal>>()

+ 0 - 97
src/views/crm/followup/components/ContactList.vue

@@ -1,97 +0,0 @@
-<template>
-  <el-table :data="list" :show-overflow-tooltip="true" :stripe="true" height="200">
-    <el-table-column align="center" fixed="left" label="姓名" prop="name" width="140" />
-    <el-table-column align="center" fixed="left" label="客户名称" prop="customerName" width="120" />
-    <el-table-column align="center" label="手机" prop="mobile" width="120" />
-    <el-table-column align="center" label="电话" prop="telephone" width="120" />
-    <el-table-column align="center" label="邮箱" prop="email" width="120" />
-    <el-table-column align="center" label="职位" prop="post" width="120" />
-    <el-table-column align="center" label="地址" prop="detailAddress" width="120" />
-    <el-table-column
-      :formatter="dateFormatter"
-      align="center"
-      label="下次联系时间"
-      prop="contactNextTime"
-      width="180px"
-    />
-    <el-table-column align="center" label="关键决策人" prop="master" width="100">
-      <template #default="scope">
-        <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
-      </template>
-    </el-table-column>
-    <el-table-column align="center" label="直属上级" prop="parentName" width="140" />
-    <el-table-column
-      :formatter="dateFormatter"
-      align="center"
-      label="最后跟进时间"
-      prop="contactLastTime"
-      width="180px"
-    />
-    <el-table-column align="center" label="性别" prop="sex">
-      <template #default="scope">
-        <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
-      </template>
-    </el-table-column>
-    <el-table-column align="center" label="负责人" prop="ownerUserName" width="120" />
-    <el-table-column align="center" label="创建人" prop="creatorName" width="120" />
-    <el-table-column
-      :formatter="dateFormatter"
-      align="center"
-      label="更新时间"
-      prop="updateTime"
-      width="180px"
-    />
-    <el-table-column
-      :formatter="dateFormatter"
-      align="center"
-      label="创建时间"
-      prop="createTime"
-      width="180px"
-    />
-    <el-table-column align="center" label="备注" prop="remark" />
-    <el-table-column align="center" fixed="right" label="操作" width="130">
-      <template #default="scope">
-        <el-button link type="danger" @click="handleDelete(scope.row.id)"> 移除</el-button>
-      </template>
-    </el-table-column>
-  </el-table>
-</template>
-
-<script lang="ts" setup>
-import { dateFormatter } from '@/utils/formatTime'
-import { DICT_TYPE } from '@/utils/dict'
-import * as ContactApi from '@/api/crm/contact'
-
-defineOptions({ name: 'ContactList' })
-const props = withDefaults(defineProps<{ contactIds: number[] }>(), {
-  contactIds: () => []
-})
-const list = ref<ContactApi.ContactVO[]>([] as ContactApi.ContactVO[])
-const getContactList = async () => {
-  list.value = (await ContactApi.getContactListByIds(
-    unref(props.contactIds)
-  )) as unknown as ContactApi.ContactVO[]
-}
-watch(
-  () => props.contactIds,
-  (val) => {
-    if (!val || val.length === 0) {
-      return
-    }
-    getContactList()
-  }
-)
-const emits = defineEmits<{
-  (e: 'update:contactIds', contactIds: number[]): void
-}>()
-const handleDelete = (id: number) => {
-  const index = list.value.findIndex((item) => item.id === id)
-  if (index !== -1) {
-    list.value.splice(index, 1)
-  }
-  emits(
-    'update:contactIds',
-    list.value.map((item) => item.id)
-  )
-}
-</script>

+ 0 - 87
src/views/crm/followup/components/ContactTableSelect.vue

@@ -1,87 +0,0 @@
-<!-- 联系人的选择列表 TODO 芋艿:后面看看要不要搞到统一封装里 -->
-<template>
-  <Dialog v-model="dialogVisible" :appendToBody="true" title="选择联系人" width="700">
-    <el-table
-      ref="multipleTableRef"
-      v-loading="loading"
-      :data="list"
-      :show-overflow-tooltip="true"
-      :stripe="true"
-      @selection-change="handleSelectionChange"
-    >
-      <el-table-column type="selection" width="55" />
-      <el-table-column align="center" fixed="left" label="姓名" prop="name" width="140" />
-      <el-table-column
-        align="center"
-        fixed="left"
-        label="客户名称"
-        prop="customerName"
-        width="120"
-      />
-      <el-table-column align="center" label="手机" prop="mobile" width="120" />
-      <el-table-column align="center" label="电话" prop="telephone" width="120" />
-      <el-table-column align="center" label="邮箱" prop="email" width="120" />
-      <el-table-column align="center" label="职位" prop="post" width="120" />
-    </el-table>
-    <template #footer>
-      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
-      <el-button @click="dialogVisible = false">取 消</el-button>
-    </template>
-  </Dialog>
-</template>
-
-<script lang="ts" setup>
-import * as ContactApi from '@/api/crm/contact'
-import { ElTable } from 'element-plus'
-
-defineOptions({ name: 'ContactTableSelect' })
-withDefaults(defineProps<{ modelValue: number[] }>(), { modelValue: () => [] })
-
-const list = ref<ContactApi.ContactVO[]>([]) // 列表的数据
-const loading = ref(false) // 列表的加载中
-const dialogVisible = ref(false) // 弹窗的是否展示
-const formLoading = ref(false)
-
-// 确认选择时的触发事件
-const emits = defineEmits<{
-  (e: 'update:modelValue', v: number[]): void
-}>()
-const multipleTableRef = ref<InstanceType<typeof ElTable>>()
-const multipleSelection = ref<ContactApi.ContactVO[]>([])
-const handleSelectionChange = (val: ContactApi.ContactVO[]) => {
-  multipleSelection.value = val
-}
-/** 触发 */
-const submitForm = () => {
-  formLoading.value = true
-  try {
-    emits(
-      'update:modelValue',
-      multipleSelection.value.map((item) => item.id)
-    )
-  } finally {
-    formLoading.value = false
-    // 关闭弹窗
-    dialogVisible.value = false
-  }
-}
-const getList = async () => {
-  loading.value = true
-  try {
-    list.value = await ContactApi.getSimpleContactList()
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 打开弹窗 */
-const open = async () => {
-  dialogVisible.value = true
-  await nextTick()
-  if (multipleSelection.value.length > 0) {
-    multipleTableRef.value!.clearSelection()
-  }
-  await getList()
-}
-defineExpose({ open }) // 提供 open 方法,用于打开弹窗
-</script>

+ 47 - 0
src/views/crm/followup/components/FollowUpRecordContactForm.vue

@@ -0,0 +1,47 @@
+<template>
+  <el-table :data="contacts" :show-overflow-tooltip="true" :stripe="true" height="150">
+    <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="mobile" />
+    <el-table-column label="职位" align="center" prop="post" />
+    <el-table-column label="直属上级" align="center" prop="parentName" />
+    <el-table-column label="是否关键决策人" align="center" prop="master" min-width="100">
+      <template #default="scope">
+        <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
+      </template>
+    </el-table-column>
+    <el-table-column align="center" fixed="right" label="操作" width="130">
+      <template #default="scope">
+        <el-button link type="danger" @click="handleDelete(scope.row.id)"> 移除</el-button>
+      </template>
+    </el-table-column>
+  </el-table>
+</template>
+
+<script lang="ts" setup>
+import { DICT_TYPE } from '@/utils/dict'
+
+const props = defineProps<{
+  contacts: undefined
+}>()
+const formData = ref([])
+
+/** 初始化联系人列表 */
+watch(
+  () => props.contacts,
+  async (val) => {
+    formData.value = val
+  },
+  { immediate: true }
+)
+
+/** 删除按钮操作 */
+const handleDelete = (index: number) => {
+  formData.value.splice(index, 1)
+}
+</script>

+ 0 - 4
src/views/crm/followup/components/index.ts

@@ -1,4 +0,0 @@
-import ContactList from './ContactList.vue'
-import ContactTableSelect from './ContactTableSelect.vue'
-
-export { ContactList, ContactTableSelect }

+ 1 - 8
src/views/crm/followup/index.vue

@@ -71,14 +71,7 @@
       </el-table-column>
       <el-table-column align="center" label="操作">
         <template #default="scope">
-          <el-button
-            v-hasPermi="['crm:follow-up-record:delete']"
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-          >
-            删除
-          </el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row.id)"> 删除 </el-button>
         </template>
       </el-table-column>
     </el-table>