Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
ba49b285
authored
Dec 03, 2023
by
zyna
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
联系人商机功能
parent
77487046
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
404 additions
and
112 deletions
+404
-112
src/api/crm/contactbusinesslink/index.ts
+48
-0
src/views/crm/contact/detail/ContactBasicInfo.vue
+0
-24
src/views/crm/contact/detail/ContactDetailsHeader.vue
+57
-0
src/views/crm/contact/detail/ContactDetailsInfo.vue
+0
-0
src/views/crm/contact/detail/index.vue
+15
-88
src/views/crm/contactBusinessLink/components/BusinessLinkContactList.vue
+144
-0
src/views/crm/contactBusinessLink/components/BusinessListByContact.vue
+140
-0
src/views/crm/contactBusinessLink/index.vue
+0
-0
No files found.
src/api/crm/contactbusinesslink/index.ts
0 → 100644
View file @
ba49b285
import
{
getBusiness
}
from
'./../business/index'
;
import
request
from
'@/config/axios'
export
interface
ContactBusinessLinkVO
{
id
:
number
contactId
:
number
businessId
:
number
}
// 查询联系人商机关联分页
export
const
getContactBusinessLinkPage
=
async
(
params
)
=>
{
return
await
request
.
get
({
url
:
`/crm/contact-business-link/page`
,
params
})
}
// 查询联系人商机关联详情
export
const
getContactBusinessLink
=
async
(
id
:
number
)
=>
{
return
await
request
.
get
({
url
:
`/crm/contact-business-link/get?id=`
+
id
})
}
// 新增联系人商机关联
export
const
createContactBusinessLink
=
async
(
data
:
ContactBusinessLinkVO
)
=>
{
return
await
request
.
post
({
url
:
`/crm/contact-business-link/create`
,
data
})
}
// 修改联系人商机关联
export
const
updateContactBusinessLink
=
async
(
data
:
ContactBusinessLinkVO
)
=>
{
return
await
request
.
put
({
url
:
`/crm/contact-business-link/update`
,
data
})
}
// 删除联系人商机关联
export
const
deleteContactBusinessLink
=
async
(
data
:
ContactBusinessLinkVO
)
=>
{
return
await
request
.
delete
({
url
:
`/crm/contact-business-link/delete-batch`
,
data
})
}
// 导出联系人商机关联 Excel
export
const
exportContactBusinessLink
=
async
(
params
)
=>
{
return
await
request
.
download
({
url
:
`/crm/contact-business-link/export-excel`
,
params
})
}
//批量新增联系人商机关联
export
const
createContactBusinessLinkBatch
=
async
(
data
:
ContactBusinessLinkVO
[])
=>
{
return
await
request
.
post
({
url
:
`/crm/contact-business-link/create-batch`
,
data
})
}
// 查询联系人关联商机列表
export
const
getBusinessByContactPage
=
async
(
params
)
=>
{
return
await
request
.
get
({
url
:
`/crm/contact-business-link/page-by-contact`
,
params
})
}
\ No newline at end of file
src/views/crm/contact/detail/ContactBasicInfo.vue
deleted
100644 → 0
View file @
77487046
<!--
* @Author: zyna
* @Date: 2023-11-11 14:50:11
* @LastEditTime: 2023-11-11 14:52:47
* @FilePath: \yudao-ui-admin-vue3\src\views\crm\contact\detail\ContactBasicInfo.vue
* @Description:
-->
<
template
>
<el-col>
<el-row>
<span
class=
"text-xl font-bold"
>
{{
contact
.
name
}}
</span>
</el-row>
</el-col>
<el-col
class=
"mt-10px"
>
<!-- TODO 标签 -->
<!--
<Icon
icon=
"ant-design:tag-filled"
/>
-->
</el-col>
</
template
>
<
script
setup
lang=
"ts"
>
// TODO 芋艿:后面在 review 么?
import
*
as
ContactApi
from
'@/api/crm/contact'
const
{
contact
}
=
defineProps
<
{
contact
:
ContactApi
.
ContactVO
}
>
()
</
script
>
src/views/crm/contact/detail/ContactDetailsHeader.vue
0 → 100644
View file @
ba49b285
<!--
* @Author: zyna
* @Date: 2023-12-02 13:08:57
* @LastEditTime: 2023-12-03 13:47:16
* @FilePath: \yudao-ui-admin-vue3\src\views\crm\contact\detail\ContactDetailsHeader.vue
* @Description:
-->
<
template
>
<div
v-loading=
"loading"
>
<div
class=
"flex items-start justify-between"
>
<div>
<el-col>
<el-row>
<span
class=
"text-xl font-bold"
>
{{
contact
.
name
}}
</span>
</el-row>
</el-col>
</div>
<div>
<!-- 右上:按钮 -->
<el-button
@
click=
"openForm('update', contact.id)"
v-hasPermi=
"['crm:contact:update']"
>
编辑
</el-button>
</div>
</div>
</div>
<ContentWrap
class=
"mt-10px"
>
<el-descriptions
:column=
"5"
direction=
"vertical"
>
<el-descriptions-item
label=
"客户"
>
{{
contact
.
customerName
}}
</el-descriptions-item>
<el-descriptions-item
label=
"职务"
>
{{
contact
.
post
}}
</el-descriptions-item>
<el-descriptions-item
label=
"手机"
>
{{
contact
.
mobile
}}
</el-descriptions-item>
<el-descriptions-item
label=
"创建时间"
>
{{
contact
.
createTime
?
formatDate
(
contact
.
createTime
)
:
'空'
}}
</el-descriptions-item>
</el-descriptions>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<ContactForm
ref=
"formRef"
@
success=
"emit('refresh')"
/>
</
template
>
<
script
setup
lang=
"ts"
>
import
*
as
ContactApi
from
'@/api/crm/contact'
import
ContactForm
from
'@/views/crm/contact/ContactForm.vue'
import
{
formatDate
}
from
'@/utils/formatTime'
//操作修改
const
formRef
=
ref
()
const
openForm
=
(
type
:
string
,
id
?:
number
)
=>
{
formRef
.
value
.
open
(
type
,
id
)
}
const
{
contact
}
=
defineProps
<
{
contact
:
ContactApi
.
ContactVO
}
>
()
const
emit
=
defineEmits
([
'refresh'
])
// 定义 success 事件,用于操作成功后的回调
</
script
>
src/views/crm/contact/detail/ContactDetails.vue
→
src/views/crm/contact/detail/ContactDetails
Info
.vue
View file @
ba49b285
File moved
src/views/crm/contact/detail/index.vue
View file @
ba49b285
<
template
>
<!-- TODO 芋艿:要不要把 3 到 62 合并成一个组件 -->
<div
v-loading=
"loading"
>
<div
class=
"flex items-start justify-between"
>
<div>
<!-- 左上:客户基本信息 -->
<ContactBasicInfo
:contact=
"contact"
/>
</div>
<div>
<!-- 右上:按钮 -->
<el-button
@
click=
"openForm('update', contact.id)"
v-hasPermi=
"['crm:contact:update']"
>
编辑
</el-button>
</div>
</div>
<el-row
class=
"mt-10px"
>
<el-button>
<Icon
icon=
"ph:calendar-fill"
class=
"mr-5px"
/>
创建任务
</el-button>
<el-button>
<Icon
icon=
"carbon:email"
class=
"mr-5px"
/>
发送邮件
</el-button>
<el-button>
<Icon
icon=
"system-uicons:contacts"
class=
"mr-5px"
/>
创建联系人
</el-button>
<el-button>
<Icon
icon=
"ep:opportunity"
class=
"mr-5px"
/>
创建商机
</el-button>
<el-button>
<Icon
icon=
"clarity:contract-line"
class=
"mr-5px"
/>
创建合同
</el-button>
<el-button>
<Icon
icon=
"icon-park:income-one"
class=
"mr-5px"
/>
创建回款
</el-button>
<el-button>
<Icon
icon=
"fluent:people-team-add-20-filled"
class=
"mr-5px"
/>
添加团队成员
</el-button>
</el-row>
</div>
<ContentWrap
class=
"mt-10px"
>
<el-descriptions
:column=
"5"
direction=
"vertical"
>
<el-descriptions-item
label=
"客户"
>
{{
contact
.
customerName
}}
</el-descriptions-item>
<el-descriptions-item
label=
"职务"
>
{{
contact
.
post
}}
</el-descriptions-item>
<el-descriptions-item
label=
"手机"
>
{{
contact
.
mobile
}}
</el-descriptions-item>
<el-descriptions-item
label=
"创建时间"
>
{{
contact
.
createTime
?
formatDate
(
contact
.
createTime
)
:
'空'
}}
</el-descriptions-item>
</el-descriptions>
</ContentWrap>
<!-- TODO wanwan:这个 tab 拉满哈,可以更好看; -->
<el-col
:span=
"18"
>
<ContactDetailsHeader
:contact=
"contact"
:loading=
"loading"
@
refresh=
"getContactData(id)"
/>
<el-col>
<el-tabs>
<el-tab-pane
label=
"基本信息"
>
<!-- TODO wanwan:这个 ml-2 是不是可以优化下,不要整个左移,而是里面的内容有个几 px 的偏移,不顶在框里 -->
<ContactDetails
class=
"ml-2"
:contact=
"contact"
/>
<el-tab-pane
label=
"详细资料"
>
<ContactDetailsInfo
:contact=
"contact"
/>
</el-tab-pane>
<el-tab-pane
label=
"跟进记录"
lazy
>
跟进记录
</el-tab-pane>
<el-tab-pane
label=
"商机"
lazy
>
商机
</el-tab-pane>
<el-tab-pane
label=
"附件"
lazy
>
附件
</el-tab-pane>
<!-- TODO wanwan 以下标签上的数量需要接口统计返回 -->
<el-tab-pane
label=
"操作记录"
lazy
>
<template
#
label
>
操作记录
<el-badge
:value=
"12"
class=
"item"
type=
"primary"
/>
</
template
>
操作记录
<el-tab-pane
label=
"操作日志"
lazy
>
TODO 待开发
</el-tab-pane>
<el-tab-pane
label=
"团队成员"
lazy
>
<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"
/>
</el-tab-pane>
</el-tabs>
</el-col>
<!-- 表单弹窗:添加/修改 -->
<ContactForm
ref=
"formRef"
@
success=
"getContactData(id)"
/>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ElMessage
}
from
'element-plus'
import
{
useTagsViewStore
}
from
'@/store/modules/tagsView'
import
*
as
ContactApi
from
'@/api/crm/contact'
import
Contact
BasicInfo
from
'@/views/crm/contact/detail/ContactBasicInfo
.vue'
import
ContactDetails
from
'@/views/crm/contact/detail/ContactDetails
.vue'
import
ContactForm
from
'@/views/crm/contact/ContactForm.vue'
import
{
formatDate
}
from
'@/utils/formatTime'
// TODO 芋艿:后面在 review 么?
import
Contact
DetailsHeader
from
'@/views/crm/contact/detail/ContactDetailsHeader
.vue'
import
ContactDetails
Info
from
'@/views/crm/contact/detail/ContactDetailsInfo
.vue'
import
BusinessList
from
'@/views/crm/contactBusinessLink/components/BusinessListByContact.vue'
// 商机列表
import
PermissionList
from
'@/views/crm/permission/components/PermissionList.vue'
// 团队成员列表(权限)
import
{
BizTypeEnum
}
from
'@/api/crm/permission'
defineOptions
({
name
:
'CrmContactDetail'
})
const
{
delView
}
=
useTagsViewStore
()
// 视图操作
const
route
=
useRoute
()
const
{
currentRoute
}
=
useRouter
()
// 路由
...
...
@@ -114,12 +47,6 @@ const getContactData = async (id: number) => {
loading
.
value
=
false
}
}
const
formRef
=
ref
()
const
openForm
=
(
type
:
string
,
id
?:
number
)
=>
{
formRef
.
value
.
open
(
type
,
id
)
}
/**
* 初始化
*/
...
...
src/views/crm/contactBusinessLink/components/BusinessLinkContactList.vue
0 → 100644
View file @
ba49b285
<
template
>
<Dialog
:title=
"dialogTitle"
v-model=
"dialogVisible"
>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class=
"-mb-15px"
:model=
"queryParams"
ref=
"queryFormRef"
:inline=
"true"
label-width=
"68px"
>
<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=
"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>
<
template
#
footer
>
<el-button
@
click=
"submitForm"
type=
"primary"
:disabled=
"formLoading"
>
确 定
</el-button>
<el-button
@
click=
"dialogVisible = false"
>
取 消
</el-button>
</
template
>
<!-- 表单弹窗:添加 -->
<BusinessForm
ref=
"formRef"
@
success=
"getList"
/>
</Dialog>
</template>
<
script
setup
lang=
"ts"
>
import
*
as
BusinessApi
from
'@/api/crm/business'
import
BusinessForm
from
'../../business/BusinessForm.vue'
import
{
BizTypeEnum
}
from
'@/api/crm/permission'
import
{
fenToYuanFormat
}
from
'@/utils/formatter'
import
*
as
ContactbusinesslinkApi
from
'@/api/crm/contactbusinesslink'
const
message
=
useMessage
()
// 消息弹窗
defineOptions
({
name
:
'CrmBusinessLinkContactList'
})
const
dialogVisible
=
ref
(
false
)
// 弹窗的是否展示
const
dialogTitle
=
ref
(
''
)
// 弹窗的标题
const
loading
=
ref
(
true
)
// 列表的加载中
const
total
=
ref
(
0
)
// 列表的总页数
const
list
=
ref
([])
// 列表的数据
const
queryFormRef
=
ref
()
// 搜索的表单
const
formLoading
=
ref
(
false
)
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const
contactIdProp
=
ref
(
undefined
)
const
queryParams
=
reactive
({
pageNo
:
1
,
pageSize
:
10
,
name
:
undefined
as
unknown
,
// 允许 undefined + number
})
/** 打开弹窗 */
const
open
=
async
(
contactId
?:
number
)
=>
{
dialogVisible
.
value
=
true
contactIdProp
.
value
=
contactId
getList
()
}
defineExpose
({
open
})
// 提供 open 方法,用于打开弹窗
/** 查询列表 */
const
getList
=
async
()
=>
{
loading
.
value
=
true
try
{
const
data
=
await
BusinessApi
.
getBusinessPage
(
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
businessRef
=
ref
()
const
submitForm
=
async
()
=>
{
if
(
businessRef
.
value
.
getSelectionRows
().
length
===
0
){
message
.
success
(
'未选择商机'
)
}
else
{
const
postData
=
ref
<
ContactbusinesslinkApi
.
ContactBusinessLinkVO
[]
>
([])
businessRef
.
value
.
getSelectionRows
().
forEach
(
element
=>
{
let
data
=
{
id
:
undefined
,
businessId
:
element
.
id
,
contactId
:
contactIdProp
.
value
}
postData
.
value
.
push
(
data
)
});
await
ContactbusinesslinkApi
.
createContactBusinessLinkBatch
(
postData
.
value
)
dialogVisible
.
value
=
false
emit
(
'success'
)
}
}
/** 打开联系人详情 */
const
{
push
}
=
useRouter
()
const
openDetail
=
(
id
:
number
)
=>
{
push
({
name
:
'CrmBusinessDetail'
,
params
:
{
id
}
})
}
</
script
>
src/views/crm/contactBusinessLink/components/BusinessListByContact.vue
0 → 100644
View file @
ba49b285
<
template
>
<!-- 操作栏 -->
<el-row
justify=
"end"
>
<el-button
@
click=
"openForm"
>
<Icon
class=
"mr-5px"
icon=
"ep:opportunity"
/>
创建商机
</el-button>
<el-button
@
click=
"openBusinessLink"
>
关联
</el-button>
<el-button
@
click=
"deleteBusinessLink"
>
解除关联
</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"
/>
</template>
<
script
setup
lang=
"ts"
>
import
*
as
ContactBusinessLinkApi
from
'@/api/crm/contactbusinesslink'
import
BusinessForm
from
'../../business/BusinessForm.vue'
import
{
BizTypeEnum
}
from
'@/api/crm/permission'
import
{
fenToYuanFormat
}
from
'@/utils/formatter'
import
BusinessLink
from
'./BusinessLinkContactList.vue'
const
message
=
useMessage
()
// 消息弹窗
defineOptions
({
name
:
'CrmBusinessContactList'
})
const
props
=
defineProps
<
{
bizType
:
number
// 业务类型
bizId
:
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
ContactBusinessLinkApi
.
getBusinessByContactPage
(
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
=
ref
<
ContactBusinessLinkApi
.
ContactBusinessLinkVO
[]
>
([])
businessRef
.
value
.
getSelectionRows
().
forEach
(
element
=>
{
let
data
=
{
id
:
undefined
,
businessId
:
element
.
id
,
contactId
:
props
.
bizId
}
postData
.
value
.
push
(
data
)
});
await
ContactBusinessLinkApi
.
deleteContactBusinessLink
(
postData
.
value
)
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
>
src/views/crm/contactBusinessLink/index.vue
0 → 100644
View file @
ba49b285
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment