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="">&nbsp;</el-descriptions-item>
+          <el-descriptions-item label="">&nbsp;</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>