Skip to content

Commit 01684aa

Browse files
committed
✨ CRM:完善商机的详情、跟进记录、操作日志、团队成员
1 parent 2960580 commit 01684aa

File tree

5 files changed

+248
-12
lines changed

5 files changed

+248
-12
lines changed

src/api/crm/business/index.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,29 @@ import { TransferReqVO } from '@/api/crm/customer'
44
export interface BusinessVO {
55
id: number
66
name: string
7+
customerId: number
8+
customerName?: string
9+
followUpStatus: boolean
10+
contactLastTime: Date
11+
contactNextTime: Date
12+
ownerUserId: number
13+
ownerUserName?: string // 负责人的用户名称
14+
ownerUserDept?: string // 负责人的部门名称
715
statusTypeId: number
16+
statusTypeName?: string
817
statusId: number
9-
contactNextTime: Date
10-
customerId: number
18+
statusName?: string
19+
endStatus: number
20+
endRemark: string
1121
dealTime: Date
12-
price: number
22+
totalProductPrice: number
23+
totalPrice: number
1324
discountPercent: number
14-
productPrice: number
1525
remark: string
16-
ownerUserId: number
17-
roUserIds: string
18-
rwUserIds: string
19-
endStatus: number
20-
endRemark: string
21-
contactLastTime: Date
22-
followUpStatus: number
26+
creator: string // 创建人
27+
creatorName?: string // 创建人名称
28+
createTime: Date // 创建时间
29+
updateTime: Date // 更新时间
2330
}
2431

2532
// 查询 CRM 商机列表

src/router/modules/remaining.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ const remainingRouter: AppRouteRecordRaw[] = [
104104
}
105105
]
106106
},
107-
108107
{
109108
path: '/dict',
110109
component: Layout,
@@ -518,6 +517,17 @@ const remainingRouter: AppRouteRecordRaw[] = [
518517
},
519518
component: () => import('@/views/crm/customer/detail/index.vue')
520519
},
520+
{
521+
path: 'business/detail/:id',
522+
name: 'CrmBusinessDetail',
523+
meta: {
524+
title: '商机详情',
525+
noCache: true,
526+
hidden: true,
527+
activeMenu: '/crm/business'
528+
},
529+
component: () => import('@/views/crm/business/detail/index.vue')
530+
},
521531
{
522532
path: 'contract/detail/:id',
523533
name: 'CrmContractDetail',
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<template>
2+
<div>
3+
<div class="flex items-start justify-between">
4+
<div>
5+
<el-col>
6+
<el-row>
7+
<span class="text-xl font-bold">{{ business.name }}</span>
8+
</el-row>
9+
</el-col>
10+
</div>
11+
<div>
12+
<!-- 右上:按钮 -->
13+
<slot></slot>
14+
</div>
15+
</div>
16+
</div>
17+
<ContentWrap class="mt-10px">
18+
<el-descriptions :column="5" direction="vertical">
19+
<el-descriptions-item label="客户名称">{{ business.customerName }}</el-descriptions-item>
20+
<el-descriptions-item label="商机金额(元)">
21+
{{ erpPriceInputFormatter(business.totalPrice) }}
22+
</el-descriptions-item>
23+
<el-descriptions-item label="商机组">{{ business.statusTypeName }}</el-descriptions-item>
24+
<el-descriptions-item label="负责人">{{ business.ownerUserName }}</el-descriptions-item>
25+
<el-descriptions-item label="创建时间">
26+
{{ formatDate(business.createTime) }}
27+
</el-descriptions-item>
28+
</el-descriptions>
29+
</ContentWrap>
30+
</template>
31+
<script lang="ts" setup>
32+
import * as BusinessApi from '@/api/crm/business'
33+
import { formatDate } from '@/utils/formatTime'
34+
import { erpPriceInputFormatter } from '@/utils'
35+
36+
const { business } = defineProps<{ business: BusinessApi.BusinessVO }>()
37+
</script>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<template>
2+
<ContentWrap>
3+
<el-collapse v-model="activeNames">
4+
<el-collapse-item name="basicInfo">
5+
<template #title>
6+
<span class="text-base font-bold">基本信息</span>
7+
</template>
8+
<el-descriptions :column="4">
9+
<el-descriptions-item label="商机姓名">{{ business.name }}</el-descriptions-item>
10+
<el-descriptions-item label="客户名称">{{ business.customerName }}</el-descriptions-item>
11+
<el-descriptions-item label="商机金额(元)">
12+
{{ erpPriceInputFormatter(business.totalPrice) }}
13+
</el-descriptions-item>
14+
<el-descriptions-item label="预计成交日期">
15+
{{ formatDate(business.dealTime) }}
16+
</el-descriptions-item>
17+
<el-descriptions-item label="下次联系时间">
18+
{{ formatDate(business.contactNextTime) }}
19+
</el-descriptions-item>
20+
<el-descriptions-item label="商机状态组">
21+
{{ business.statusTypeName }}
22+
</el-descriptions-item>
23+
<el-descriptions-item label="商机阶段">{{ business.statusName }}</el-descriptions-item>
24+
<el-descriptions-item label="备注">{{ business.remark }}</el-descriptions-item>
25+
</el-descriptions>
26+
</el-collapse-item>
27+
<el-collapse-item name="systemInfo">
28+
<template #title>
29+
<span class="text-base font-bold">系统信息</span>
30+
</template>
31+
<el-descriptions :column="4">
32+
<el-descriptions-item label="负责人">{{ business.ownerUserName }}</el-descriptions-item>
33+
<el-descriptions-item label="最后跟进时间">
34+
{{ formatDate(business.contactLastTime) }}
35+
</el-descriptions-item>
36+
<el-descriptions-item label="">&nbsp;</el-descriptions-item>
37+
<el-descriptions-item label="">&nbsp;</el-descriptions-item>
38+
<el-descriptions-item label="创建人">{{ business.creatorName }}</el-descriptions-item>
39+
<el-descriptions-item label="创建时间">
40+
{{ formatDate(business.createTime) }}
41+
</el-descriptions-item>
42+
<el-descriptions-item label="更新时间">
43+
{{ formatDate(business.updateTime) }}
44+
</el-descriptions-item>
45+
</el-descriptions>
46+
</el-collapse-item>
47+
</el-collapse>
48+
</ContentWrap>
49+
</template>
50+
<script setup lang="ts">
51+
import * as BusinessApi from '@/api/crm/business'
52+
import { formatDate } from '@/utils/formatTime'
53+
import { erpPriceInputFormatter } from '@/utils'
54+
55+
const { business } = defineProps<{
56+
business: BusinessApi.BusinessVO
57+
}>()
58+
59+
// 展示的折叠面板
60+
const activeNames = ref(['basicInfo', 'systemInfo'])
61+
</script>
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<template>
2+
<BusinessDetailsHeader v-loading="loading" :business="business">
3+
<el-button v-if="permissionListRef?.validateWrite" @click="openForm('update', business.id)">
4+
编辑
5+
</el-button>
6+
<el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
7+
转移
8+
</el-button>
9+
</BusinessDetailsHeader>
10+
<el-col>
11+
<el-tabs>
12+
<el-tab-pane label="跟进记录">
13+
<FollowUpList :biz-id="businessId" :biz-type="BizTypeEnum.CRM_BUSINESS" />
14+
</el-tab-pane>
15+
<el-tab-pane label="详细资料">
16+
<BusinessDetailsInfo :business="business" />
17+
</el-tab-pane>
18+
<el-tab-pane label="操作日志">
19+
<OperateLogV2 :log-list="logList" />
20+
</el-tab-pane>
21+
<el-tab-pane label="团队成员">
22+
<PermissionList
23+
ref="permissionListRef"
24+
:biz-id="business.id!"
25+
:biz-type="BizTypeEnum.CRM_BUSINESS"
26+
:show-action="true"
27+
@quit-team="close"
28+
/>
29+
</el-tab-pane>
30+
<el-tab-pane label="商机" lazy>
31+
<BusinessList
32+
:biz-id="business.id!"
33+
:biz-type="BizTypeEnum.CRM_CONTACT"
34+
:customer-id="business.customerId"
35+
/>
36+
</el-tab-pane>
37+
</el-tabs>
38+
</el-col>
39+
<!-- 表单弹窗:添加/修改 -->
40+
<ContactForm ref="formRef" @success="getContact(business.id)" />
41+
<CrmTransferForm ref="transferFormRef" @success="close" />
42+
</template>
43+
<script lang="ts" setup>
44+
import { useTagsViewStore } from '@/store/modules/tagsView'
45+
import * as ContactApi from '@/api/crm/contact'
46+
import * as BusinessApi from '@/api/crm/business'
47+
import BusinessDetailsHeader from './BusinessDetailsHeader.vue'
48+
import BusinessDetailsInfo from './BusinessDetailsInfo.vue'
49+
import BusinessList from '@/views/crm/business/components/BusinessList.vue' // 商机列表
50+
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
51+
import { BizTypeEnum } from '@/api/crm/permission'
52+
import { OperateLogV2VO } from '@/api/system/operatelog'
53+
import { getOperateLogPage } from '@/api/crm/operateLog'
54+
import ContactForm from '@/views/crm/contact/ContactForm.vue'
55+
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
56+
import FollowUpList from '@/views/crm/followup/index.vue'
57+
58+
defineOptions({ name: 'CrmBusinessDetail' })
59+
60+
const message = useMessage()
61+
62+
const businessId = ref(0) // 线索编号
63+
const loading = ref(true) // 加载中
64+
const business = ref<ContactApi.ContactVO>({} as ContactApi.ContactVO) // 联系人详情
65+
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
66+
67+
/** 获取详情 */
68+
const getContact = async (id: number) => {
69+
loading.value = true
70+
try {
71+
business.value = await BusinessApi.getBusiness(id)
72+
await getOperateLog(id)
73+
} finally {
74+
loading.value = false
75+
}
76+
}
77+
78+
/** 编辑 */
79+
const formRef = ref()
80+
const openForm = (type: string, id?: number) => {
81+
formRef.value.open(type, id)
82+
}
83+
84+
/** 联系人转移 */
85+
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 联系人转移表单 ref
86+
const transfer = () => {
87+
transferFormRef.value?.open('商机转移', business.value.id, BusinessApi.transferBusiness)
88+
}
89+
90+
/** 获取操作日志 */
91+
const logList = ref<OperateLogV2VO[]>([]) // 操作日志列表
92+
const getOperateLog = async (contactId: number) => {
93+
if (!contactId) {
94+
return
95+
}
96+
const data = await getOperateLogPage({
97+
bizType: BizTypeEnum.CRM_BUSINESS,
98+
bizId: contactId
99+
})
100+
logList.value = data.list
101+
}
102+
103+
/** 关闭窗口 */
104+
const { delView } = useTagsViewStore() // 视图操作
105+
const { currentRoute } = useRouter() // 路由
106+
const close = () => {
107+
delView(unref(currentRoute))
108+
}
109+
110+
/** 初始化 */
111+
const { params } = useRoute()
112+
onMounted(async () => {
113+
if (!params.id) {
114+
message.warning('参数错误,商机不能为空!')
115+
close()
116+
return
117+
}
118+
businessId.value = params.id as unknown as number
119+
await getContact(businessId.value)
120+
})
121+
</script>

0 commit comments

Comments
 (0)