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
7af2a3bc
authored
Oct 18, 2023
by
913752709@qq.com
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: CRM 线索表 crud
parent
d6b0305d
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
529 additions
and
0 deletions
+529
-0
src/api/crm/clue/index.ts
+46
-0
src/views/crm/clue/ClueForm.vue
+165
-0
src/views/crm/clue/index.vue
+318
-0
No files found.
src/api/crm/clue/index.ts
0 → 100644
View file @
7af2a3bc
import
request
from
'@/config/axios'
export
interface
ClueVO
{
id
:
number
transformStatus
:
boolean
followUpStatus
:
boolean
name
:
string
customerId
:
number
contactNextTime
:
Date
telephone
:
string
mobile
:
string
address
:
string
ownerUserId
:
number
contactLastTime
:
Date
remark
:
string
}
// 查询线索列表
export
const
getCluePage
=
async
(
params
)
=>
{
return
await
request
.
get
({
url
:
`/crm/clue/page`
,
params
})
}
// 查询线索详情
export
const
getClue
=
async
(
id
:
number
)
=>
{
return
await
request
.
get
({
url
:
`/crm/clue/get?id=`
+
id
})
}
// 新增线索
export
const
createClue
=
async
(
data
:
ClueVO
)
=>
{
return
await
request
.
post
({
url
:
`/crm/clue/create`
,
data
})
}
// 修改线索
export
const
updateClue
=
async
(
data
:
ClueVO
)
=>
{
return
await
request
.
put
({
url
:
`/crm/clue/update`
,
data
})
}
// 删除线索
export
const
deleteClue
=
async
(
id
:
number
)
=>
{
return
await
request
.
delete
({
url
:
`/crm/clue/delete?id=`
+
id
})
}
// 导出线索 Excel
export
const
exportClue
=
async
(
params
)
=>
{
return
await
request
.
download
({
url
:
`/crm/clue/export-excel`
,
params
})
}
src/views/crm/clue/ClueForm.vue
0 → 100644
View file @
7af2a3bc
<
template
>
<Dialog
:title=
"dialogTitle"
v-model=
"dialogVisible"
>
<el-form
ref=
"formRef"
:model=
"formData"
:rules=
"formRules"
label-width=
"100px"
v-loading=
"formLoading"
>
<el-form-item
label=
"转化状态"
prop=
"transformStatus"
>
<el-radio-group
v-model=
"formData.transformStatus"
>
<el-radio
v-for=
"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key=
"dict.value"
:label=
"dict.value"
>
{{
dict
.
label
}}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"跟进状态"
prop=
"followUpStatus"
>
<el-radio-group
v-model=
"formData.followUpStatus"
>
<el-radio
v-for=
"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key=
"dict.value"
:label=
"dict.value"
>
{{
dict
.
label
}}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"线索名称"
prop=
"name"
>
<el-input
v-model=
"formData.name"
placeholder=
"请输入线索名称"
/>
</el-form-item>
<!-- TODO 客户选择 -->
<el-form-item
label=
"客户"
prop=
"customerId"
>
<el-input
v-model=
"formData.customerId"
placeholder=
"请选择客户"
/>
</el-form-item>
<el-form-item
label=
"下次联系时间"
prop=
"contactNextTime"
>
<el-date-picker
v-model=
"formData.contactNextTime"
type=
"date"
value-format=
"x"
placeholder=
"选择下次联系时间"
/>
</el-form-item>
<el-form-item
label=
"电话"
prop=
"telephone"
>
<el-input
v-model=
"formData.telephone"
placeholder=
"请输入电话"
/>
</el-form-item>
<el-form-item
label=
"手机号"
prop=
"mobile"
>
<el-input
v-model=
"formData.mobile"
placeholder=
"请输入手机号"
/>
</el-form-item>
<el-form-item
label=
"地址"
prop=
"address"
>
<el-input
v-model=
"formData.address"
placeholder=
"请输入地址"
/>
</el-form-item>
<!-- TODO 负责人选择 -->
<el-form-item
label=
"负责人"
prop=
"ownerUserId"
>
<el-input
v-model=
"formData.ownerUserId"
placeholder=
"请输入负责人"
/>
</el-form-item>
<el-form-item
label=
"备注"
prop=
"remark"
>
<el-input
v-model=
"formData.remark"
placeholder=
"请输入备注"
/>
</el-form-item>
</el-form>
<template
#
footer
>
<el-button
@
click=
"submitForm"
type=
"primary"
:disabled=
"formLoading"
>
确 定
</el-button>
<el-button
@
click=
"dialogVisible = false"
>
取 消
</el-button>
</
template
>
</Dialog>
</template>
<
script
setup
lang=
"ts"
>
import
{
DICT_TYPE
,
getBoolDictOptions
}
from
'@/utils/dict'
import
*
as
ClueApi
from
'@/api/crm/clue'
const
{
t
}
=
useI18n
()
// 国际化
const
message
=
useMessage
()
// 消息弹窗
const
dialogVisible
=
ref
(
false
)
// 弹窗的是否展示
const
dialogTitle
=
ref
(
''
)
// 弹窗的标题
const
formLoading
=
ref
(
false
)
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const
formType
=
ref
(
''
)
// 表单的类型:create - 新增;update - 修改
const
formData
=
ref
({
id
:
undefined
,
transformStatus
:
undefined
,
followUpStatus
:
undefined
,
name
:
undefined
,
customerId
:
undefined
,
contactNextTime
:
undefined
,
telephone
:
undefined
,
mobile
:
undefined
,
address
:
undefined
,
ownerUserId
:
undefined
,
contactLastTime
:
undefined
,
remark
:
undefined
})
const
formRules
=
reactive
({
transformStatus
:
[{
required
:
true
,
message
:
'转化状态不能为空'
,
trigger
:
'blur'
}],
followUpStatus
:
[{
required
:
true
,
message
:
'跟进状态不能为空'
,
trigger
:
'blur'
}],
name
:
[{
required
:
true
,
message
:
'线索名称不能为空'
,
trigger
:
'blur'
}],
customerId
:
[{
required
:
true
,
message
:
'客户id不能为空'
,
trigger
:
'blur'
}]
})
const
formRef
=
ref
()
// 表单 Ref
/** 打开弹窗 */
const
open
=
async
(
type
:
string
,
id
?:
number
)
=>
{
dialogVisible
.
value
=
true
dialogTitle
.
value
=
t
(
'action.'
+
type
)
formType
.
value
=
type
resetForm
()
// 修改时,设置数据
if
(
id
)
{
formLoading
.
value
=
true
try
{
formData
.
value
=
await
ClueApi
.
getClue
(
id
)
}
finally
{
formLoading
.
value
=
false
}
}
}
defineExpose
({
open
})
// 提供 open 方法,用于打开弹窗
/** 提交表单 */
const
emit
=
defineEmits
([
'success'
])
// 定义 success 事件,用于操作成功后的回调
const
submitForm
=
async
()
=>
{
// 校验表单
if
(
!
formRef
)
return
const
valid
=
await
formRef
.
value
.
validate
()
if
(
!
valid
)
return
// 提交请求
formLoading
.
value
=
true
try
{
const
data
=
formData
.
value
as
unknown
as
ClueApi
.
ClueVO
if
(
formType
.
value
===
'create'
)
{
await
ClueApi
.
createClue
(
data
)
message
.
success
(
t
(
'common.createSuccess'
))
}
else
{
await
ClueApi
.
updateClue
(
data
)
message
.
success
(
t
(
'common.updateSuccess'
))
}
dialogVisible
.
value
=
false
// 发送操作成功的事件
emit
(
'success'
)
}
finally
{
formLoading
.
value
=
false
}
}
/** 重置表单 */
const
resetForm
=
()
=>
{
formData
.
value
=
{
id
:
undefined
,
transformStatus
:
undefined
,
followUpStatus
:
undefined
,
name
:
undefined
,
customerId
:
undefined
,
contactNextTime
:
undefined
,
telephone
:
undefined
,
mobile
:
undefined
,
address
:
undefined
,
ownerUserId
:
undefined
,
contactLastTime
:
undefined
,
remark
:
undefined
}
formRef
.
value
?.
resetFields
()
}
</
script
>
src/views/crm/clue/index.vue
0 → 100644
View file @
7af2a3bc
<
template
>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class=
"-mb-15px"
:model=
"queryParams"
ref=
"queryFormRef"
:inline=
"true"
label-width=
"68px"
>
<el-form-item
label=
"转化状态"
prop=
"transformStatus"
>
<el-select
v-model=
"queryParams.transformStatus"
placeholder=
"请选择转化状态"
clearable
class=
"!w-240px"
>
<el-option
v-for=
"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key=
"dict.value"
:label=
"dict.label"
:value=
"dict.value"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"跟进状态"
prop=
"followUpStatus"
>
<el-select
v-model=
"queryParams.followUpStatus"
placeholder=
"请选择跟进状态"
clearable
class=
"!w-240px"
>
<el-option
v-for=
"dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key=
"dict.value"
:label=
"dict.label"
:value=
"dict.value"
/>
</el-select>
</el-form-item>
<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
label=
"客户id"
prop=
"customerId"
>
<el-input
v-model=
"queryParams.customerId"
placeholder=
"请输入客户id"
clearable
@
keyup
.
enter=
"handleQuery"
class=
"!w-240px"
/>
</el-form-item>
<el-form-item
label=
"下次联系时间"
prop=
"contactNextTime"
>
<el-date-picker
v-model=
"queryParams.contactNextTime"
value-format=
"YYYY-MM-DD HH:mm:ss"
type=
"daterange"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
:default-time=
"[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class=
"!w-240px"
/>
</el-form-item>
<el-form-item
label=
"电话"
prop=
"telephone"
>
<el-input
v-model=
"queryParams.telephone"
placeholder=
"请输入电话"
clearable
@
keyup
.
enter=
"handleQuery"
class=
"!w-240px"
/>
</el-form-item>
<el-form-item
label=
"手机号"
prop=
"mobile"
>
<el-input
v-model=
"queryParams.mobile"
placeholder=
"请输入手机号"
clearable
@
keyup
.
enter=
"handleQuery"
class=
"!w-240px"
/>
</el-form-item>
<el-form-item
label=
"地址"
prop=
"address"
>
<el-input
v-model=
"queryParams.address"
placeholder=
"请输入地址"
clearable
@
keyup
.
enter=
"handleQuery"
class=
"!w-240px"
/>
</el-form-item>
<el-form-item
label=
"负责人"
prop=
"ownerUserId"
>
<el-input
v-model=
"queryParams.ownerUserId"
placeholder=
"请输入负责人"
clearable
@
keyup
.
enter=
"handleQuery"
class=
"!w-240px"
/>
</el-form-item>
<el-form-item
label=
"最后跟进时间"
prop=
"contactLastTime"
>
<el-date-picker
v-model=
"queryParams.contactLastTime"
value-format=
"YYYY-MM-DD HH:mm:ss"
type=
"daterange"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
:default-time=
"[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class=
"!w-240px"
/>
</el-form-item>
<el-form-item
label=
"创建时间"
prop=
"createTime"
>
<el-date-picker
v-model=
"queryParams.createTime"
value-format=
"YYYY-MM-DD HH:mm:ss"
type=
"daterange"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
:default-time=
"[new Date('1 00:00:00'), new Date('1 23:59:59')]"
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('create')"
v-hasPermi=
"['crm:clue:create']"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
新增
</el-button>
<el-button
type=
"success"
plain
@
click=
"handleExport"
:loading=
"exportLoading"
v-hasPermi=
"['crm:clue:export']"
>
<Icon
icon=
"ep:download"
class=
"mr-5px"
/>
导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table
v-loading=
"loading"
:data=
"list"
:stripe=
"true"
:show-overflow-tooltip=
"true"
>
<el-table-column
label=
"编号"
align=
"center"
prop=
"id"
/>
<el-table-column
label=
"转化状态"
align=
"center"
prop=
"transformStatus"
>
<template
#
default=
"scope"
>
<dict-tag
:type=
"DICT_TYPE.INFRA_BOOLEAN_STRING"
:value=
"scope.row.transformStatus"
/>
</
template
>
</el-table-column>
<el-table-column
label=
"跟进状态"
align=
"center"
prop=
"followUpStatus"
>
<
template
#
default=
"scope"
>
<dict-tag
:type=
"DICT_TYPE.INFRA_BOOLEAN_STRING"
:value=
"scope.row.followUpStatus"
/>
</
template
>
</el-table-column>
<el-table-column
label=
"线索名称"
align=
"center"
prop=
"name"
/>
<el-table-column
label=
"客户id"
align=
"center"
prop=
"customerId"
/>
<el-table-column
label=
"下次联系时间"
align=
"center"
prop=
"contactNextTime"
:formatter=
"dateFormatter"
width=
"180px"
/>
<el-table-column
label=
"电话"
align=
"center"
prop=
"telephone"
/>
<el-table-column
label=
"手机号"
align=
"center"
prop=
"mobile"
/>
<el-table-column
label=
"地址"
align=
"center"
prop=
"address"
/>
<el-table-column
label=
"负责人"
align=
"center"
prop=
"ownerUserId"
/>
<el-table-column
label=
"最后跟进时间"
align=
"center"
prop=
"contactLastTime"
:formatter=
"dateFormatter"
width=
"180px"
/>
<el-table-column
label=
"备注"
align=
"center"
prop=
"remark"
/>
<el-table-column
label=
"创建时间"
align=
"center"
prop=
"createTime"
:formatter=
"dateFormatter"
width=
"180px"
/>
<el-table-column
label=
"操作"
align=
"center"
>
<
template
#
default=
"scope"
>
<el-button
link
type=
"primary"
@
click=
"openForm('update', scope.row.id)"
v-hasPermi=
"['crm:clue:update']"
>
编辑
</el-button>
<el-button
link
type=
"danger"
@
click=
"handleDelete(scope.row.id)"
v-hasPermi=
"['crm:clue:delete']"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total=
"total"
v-model:page=
"queryParams.pageNo"
v-model:limit=
"queryParams.pageSize"
@
pagination=
"getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<ClueForm
ref=
"formRef"
@
success=
"getList"
/>
</template>
<
script
setup
lang=
"ts"
>
import
{
DICT_TYPE
,
getBoolDictOptions
}
from
'@/utils/dict'
import
{
dateFormatter
}
from
'@/utils/formatTime'
import
download
from
'@/utils/download'
import
*
as
ClueApi
from
'@/api/crm/clue'
import
ClueForm
from
'./ClueForm.vue'
defineOptions
({
name
:
'CrmClue'
})
const
message
=
useMessage
()
// 消息弹窗
const
{
t
}
=
useI18n
()
// 国际化
const
loading
=
ref
(
true
)
// 列表的加载中
const
total
=
ref
(
0
)
// 列表的总页数
const
list
=
ref
([])
// 列表的数据
const
queryParams
=
reactive
({
pageNo
:
1
,
pageSize
:
10
,
transformStatus
:
null
,
followUpStatus
:
null
,
name
:
null
,
customerId
:
null
,
contactNextTime
:
[],
telephone
:
null
,
mobile
:
null
,
address
:
null
,
ownerUserId
:
null
,
contactLastTime
:
[],
createTime
:
[]
})
const
queryFormRef
=
ref
()
// 搜索的表单
const
exportLoading
=
ref
(
false
)
// 导出的加载中
/** 查询列表 */
const
getList
=
async
()
=>
{
loading
.
value
=
true
try
{
const
data
=
await
ClueApi
.
getCluePage
(
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
=
(
type
:
string
,
id
?:
number
)
=>
{
formRef
.
value
.
open
(
type
,
id
)
}
/** 删除按钮操作 */
const
handleDelete
=
async
(
id
:
number
)
=>
{
try
{
// 删除的二次确认
await
message
.
delConfirm
()
// 发起删除
await
ClueApi
.
deleteClue
(
id
)
message
.
success
(
t
(
'common.delSuccess'
))
// 刷新列表
await
getList
()
}
catch
{}
}
/** 导出按钮操作 */
const
handleExport
=
async
()
=>
{
try
{
// 导出的二次确认
await
message
.
exportConfirm
()
// 发起导出
exportLoading
.
value
=
true
const
data
=
await
ClueApi
.
exportClue
(
queryParams
)
download
.
excel
(
data
,
'线索.xls'
)
}
catch
{
}
finally
{
exportLoading
.
value
=
false
}
}
/** 初始化 **/
onMounted
(()
=>
{
getList
()
})
</
script
>
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