From 01684aa6e855ecec8d6581787d9ba7b809e42d1a Mon Sep 17 00:00:00 2001 From: YunaiV <zhijiantianya@gmail.com> Date: Thu, 22 Feb 2024 08:32:07 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20CRM=EF=BC=9A=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E5=95=86=E6=9C=BA=E7=9A=84=E8=AF=A6=E6=83=85=E3=80=81=E8=B7=9F?= =?UTF-8?q?=E8=BF=9B=E8=AE=B0=E5=BD=95=E3=80=81=E6=93=8D=E4=BD=9C=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E3=80=81=E5=9B=A2=E9=98=9F=E6=88=90=E5=91=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/crm/business/index.ts | 31 +++-- src/router/modules/remaining.ts | 12 +- .../business/detail/BusinessDetailsHeader.vue | 37 ++++++ .../business/detail/BusinessDetailsInfo.vue | 61 +++++++++ src/views/crm/business/detail/index.vue | 121 ++++++++++++++++++ 5 files changed, 249 insertions(+), 13 deletions(-) create mode 100644 src/views/crm/business/detail/BusinessDetailsHeader.vue create mode 100644 src/views/crm/business/detail/BusinessDetailsInfo.vue create mode 100644 src/views/crm/business/detail/index.vue diff --git a/src/api/crm/business/index.ts b/src/api/crm/business/index.ts index 37304f5a..8dd06563 100644 --- a/src/api/crm/business/index.ts +++ b/src/api/crm/business/index.ts @@ -4,22 +4,29 @@ import { TransferReqVO } from '@/api/crm/customer' export interface BusinessVO { id: number name: string - statusTypeId: number - statusId: number - contactNextTime: Date customerId: number - dealTime: Date - price: number - discountPercent: number - productPrice: number - remark: string + customerName?: string + followUpStatus: boolean + contactLastTime: Date + contactNextTime: Date ownerUserId: number - roUserIds: string - rwUserIds: string + ownerUserName?: string // 负责人的用户名称 + ownerUserDept?: string // 负责人的部门名称 + statusTypeId: number + statusTypeName?: string + statusId: number + statusName?: string endStatus: number endRemark: string - contactLastTime: Date - followUpStatus: number + dealTime: Date + totalProductPrice: number + totalPrice: number + discountPercent: number + remark: string + creator: string // 创建人 + creatorName?: string // 创建人名称 + createTime: Date // 创建时间 + updateTime: Date // 更新时间 } // 查询 CRM 商机列表 diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts index 4f845d0d..08d5db98 100644 --- a/src/router/modules/remaining.ts +++ b/src/router/modules/remaining.ts @@ -104,7 +104,6 @@ const remainingRouter: AppRouteRecordRaw[] = [ } ] }, - { path: '/dict', component: Layout, @@ -518,6 +517,17 @@ const remainingRouter: AppRouteRecordRaw[] = [ }, component: () => import('@/views/crm/customer/detail/index.vue') }, + { + path: 'business/detail/:id', + name: 'CrmBusinessDetail', + meta: { + title: '商机详情', + noCache: true, + hidden: true, + activeMenu: '/crm/business' + }, + component: () => import('@/views/crm/business/detail/index.vue') + }, { path: 'contract/detail/:id', name: 'CrmContractDetail', diff --git a/src/views/crm/business/detail/BusinessDetailsHeader.vue b/src/views/crm/business/detail/BusinessDetailsHeader.vue new file mode 100644 index 00000000..50d1efea --- /dev/null +++ b/src/views/crm/business/detail/BusinessDetailsHeader.vue @@ -0,0 +1,37 @@ +<template> + <div> + <div class="flex items-start justify-between"> + <div> + <el-col> + <el-row> + <span class="text-xl font-bold">{{ business.name }}</span> + </el-row> + </el-col> + </div> + <div> + <!-- 右上:按钮 --> + <slot></slot> + </div> + </div> + </div> + <ContentWrap class="mt-10px"> + <el-descriptions :column="5" direction="vertical"> + <el-descriptions-item label="客户名称">{{ business.customerName }}</el-descriptions-item> + <el-descriptions-item label="商机金额(元)"> + {{ erpPriceInputFormatter(business.totalPrice) }} + </el-descriptions-item> + <el-descriptions-item label="商机组">{{ business.statusTypeName }}</el-descriptions-item> + <el-descriptions-item label="负责人">{{ business.ownerUserName }}</el-descriptions-item> + <el-descriptions-item label="创建时间"> + {{ formatDate(business.createTime) }} + </el-descriptions-item> + </el-descriptions> + </ContentWrap> +</template> +<script lang="ts" setup> +import * as BusinessApi from '@/api/crm/business' +import { formatDate } from '@/utils/formatTime' +import { erpPriceInputFormatter } from '@/utils' + +const { business } = defineProps<{ business: BusinessApi.BusinessVO }>() +</script> diff --git a/src/views/crm/business/detail/BusinessDetailsInfo.vue b/src/views/crm/business/detail/BusinessDetailsInfo.vue new file mode 100644 index 00000000..a2c9ce18 --- /dev/null +++ b/src/views/crm/business/detail/BusinessDetailsInfo.vue @@ -0,0 +1,61 @@ +<template> + <ContentWrap> + <el-collapse v-model="activeNames"> + <el-collapse-item name="basicInfo"> + <template #title> + <span class="text-base font-bold">基本信息</span> + </template> + <el-descriptions :column="4"> + <el-descriptions-item label="商机姓名">{{ business.name }}</el-descriptions-item> + <el-descriptions-item label="客户名称">{{ business.customerName }}</el-descriptions-item> + <el-descriptions-item label="商机金额(元)"> + {{ erpPriceInputFormatter(business.totalPrice) }} + </el-descriptions-item> + <el-descriptions-item label="预计成交日期"> + {{ formatDate(business.dealTime) }} + </el-descriptions-item> + <el-descriptions-item label="下次联系时间"> + {{ formatDate(business.contactNextTime) }} + </el-descriptions-item> + <el-descriptions-item label="商机状态组"> + {{ business.statusTypeName }} + </el-descriptions-item> + <el-descriptions-item label="商机阶段">{{ business.statusName }}</el-descriptions-item> + <el-descriptions-item label="备注">{{ business.remark }}</el-descriptions-item> + </el-descriptions> + </el-collapse-item> + <el-collapse-item name="systemInfo"> + <template #title> + <span class="text-base font-bold">系统信息</span> + </template> + <el-descriptions :column="4"> + <el-descriptions-item label="负责人">{{ business.ownerUserName }}</el-descriptions-item> + <el-descriptions-item label="最后跟进时间"> + {{ formatDate(business.contactLastTime) }} + </el-descriptions-item> + <el-descriptions-item label=""> </el-descriptions-item> + <el-descriptions-item label=""> </el-descriptions-item> + <el-descriptions-item label="创建人">{{ business.creatorName }}</el-descriptions-item> + <el-descriptions-item label="创建时间"> + {{ formatDate(business.createTime) }} + </el-descriptions-item> + <el-descriptions-item label="更新时间"> + {{ formatDate(business.updateTime) }} + </el-descriptions-item> + </el-descriptions> + </el-collapse-item> + </el-collapse> + </ContentWrap> +</template> +<script setup lang="ts"> +import * as BusinessApi from '@/api/crm/business' +import { formatDate } from '@/utils/formatTime' +import { erpPriceInputFormatter } from '@/utils' + +const { business } = defineProps<{ + business: BusinessApi.BusinessVO +}>() + +// 展示的折叠面板 +const activeNames = ref(['basicInfo', 'systemInfo']) +</script> diff --git a/src/views/crm/business/detail/index.vue b/src/views/crm/business/detail/index.vue new file mode 100644 index 00000000..a0aa89ee --- /dev/null +++ b/src/views/crm/business/detail/index.vue @@ -0,0 +1,121 @@ +<template> + <BusinessDetailsHeader v-loading="loading" :business="business"> + <el-button v-if="permissionListRef?.validateWrite" @click="openForm('update', business.id)"> + 编辑 + </el-button> + <el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer"> + 转移 + </el-button> + </BusinessDetailsHeader> + <el-col> + <el-tabs> + <el-tab-pane label="跟进记录"> + <FollowUpList :biz-id="businessId" :biz-type="BizTypeEnum.CRM_BUSINESS" /> + </el-tab-pane> + <el-tab-pane label="详细资料"> + <BusinessDetailsInfo :business="business" /> + </el-tab-pane> + <el-tab-pane label="操作日志"> + <OperateLogV2 :log-list="logList" /> + </el-tab-pane> + <el-tab-pane label="团队成员"> + <PermissionList + ref="permissionListRef" + :biz-id="business.id!" + :biz-type="BizTypeEnum.CRM_BUSINESS" + :show-action="true" + @quit-team="close" + /> + </el-tab-pane> + <el-tab-pane label="商机" lazy> + <BusinessList + :biz-id="business.id!" + :biz-type="BizTypeEnum.CRM_CONTACT" + :customer-id="business.customerId" + /> + </el-tab-pane> + </el-tabs> + </el-col> + <!-- 表单弹窗:添加/修改 --> + <ContactForm ref="formRef" @success="getContact(business.id)" /> + <CrmTransferForm ref="transferFormRef" @success="close" /> +</template> +<script lang="ts" setup> +import { useTagsViewStore } from '@/store/modules/tagsView' +import * as ContactApi from '@/api/crm/contact' +import * as BusinessApi from '@/api/crm/business' +import BusinessDetailsHeader from './BusinessDetailsHeader.vue' +import BusinessDetailsInfo from './BusinessDetailsInfo.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' +import { OperateLogV2VO } from '@/api/system/operatelog' +import { getOperateLogPage } from '@/api/crm/operateLog' +import ContactForm from '@/views/crm/contact/ContactForm.vue' +import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue' +import FollowUpList from '@/views/crm/followup/index.vue' + +defineOptions({ name: 'CrmBusinessDetail' }) + +const message = useMessage() + +const businessId = ref(0) // 线索编号 +const loading = ref(true) // 加载中 +const business = ref<ContactApi.ContactVO>({} as ContactApi.ContactVO) // 联系人详情 +const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref + +/** 获取详情 */ +const getContact = async (id: number) => { + loading.value = true + try { + business.value = await BusinessApi.getBusiness(id) + await getOperateLog(id) + } finally { + loading.value = false + } +} + +/** 编辑 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 联系人转移 */ +const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 联系人转移表单 ref +const transfer = () => { + transferFormRef.value?.open('商机转移', business.value.id, BusinessApi.transferBusiness) +} + +/** 获取操作日志 */ +const logList = ref<OperateLogV2VO[]>([]) // 操作日志列表 +const getOperateLog = async (contactId: number) => { + if (!contactId) { + return + } + const data = await getOperateLogPage({ + bizType: BizTypeEnum.CRM_BUSINESS, + bizId: contactId + }) + logList.value = data.list +} + +/** 关闭窗口 */ +const { delView } = useTagsViewStore() // 视图操作 +const { currentRoute } = useRouter() // 路由 +const close = () => { + delView(unref(currentRoute)) +} + +/** 初始化 */ +const { params } = useRoute() +onMounted(async () => { + if (!params.id) { + message.warning('参数错误,商机不能为空!') + close() + return + } + businessId.value = params.id as unknown as number + await getContact(businessId.value) +}) +</script>