Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
B
biz.qingxiao.com
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wangxuelai
biz.qingxiao.com
Commits
585967c2
Commit
585967c2
authored
Jan 14, 2020
by
wangxuelai
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
'获客导入添加'
parent
1516015b
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
680 additions
and
288 deletions
+680
-288
api.js
src/common/api.js
+2
-0
crm.js
src/models/crm.js
+252
-0
BindSjdAccount.js
src/pages/crm/BindSjdAccount.js
+135
-0
ClueImport.js
src/pages/crm/ClueImport.js
+71
-116
ClueImport.less
src/pages/crm/ClueImport.less
+24
-169
index.js
src/pages/crm/index.js
+161
-3
crm.js
src/services/crm.js
+23
-0
uploader.js
src/services/uploader.js
+12
-0
No files found.
src/common/api.js
View file @
585967c2
...
...
@@ -223,5 +223,7 @@ export default {
},
crm
:
{
crmList
:
`
${
dakaapi
}
member/crm/clues`
,
uploadTasks
:
`
${
dakaapi
}
member/erp/upload_tasks`
,
bindSjdAccount
:
`
${
dakaapi
}
member/sjd/bind`
,
},
};
src/models/crm.js
View file @
585967c2
...
...
@@ -11,6 +11,8 @@ import {
}
from
'../utils/index'
;
import
*
as
crmAjax
from
'../services/crm'
;
import
errorcode
from
'../common/errorcode'
;
import
*
as
newregister
from
'../services/newregister'
;
import
*
as
uploader
from
'../services/uploader'
;
import
*
as
courseAjax
from
'../services/course'
;
export
default
{
namespace
:
'crm'
,
...
...
@@ -29,6 +31,18 @@ export default {
},
crmTotal
:
0
,
crmList
:
[],
clueImportShow
:
false
,
bindSjdAccountShow
:
false
,
loadingUploadTask
:
false
,
uploadTaskList
:
[],
taskListQueryParams
:
{
page
:
1
,
perPage
:
20
,
},
uploadTaskTotal
:
0
,
countdown
:
60
,
counting
:
false
,
gettingVerifyCoding
:
false
,
},
subscriptions
:
{
setup
({
dispatch
,
history
})
{
// eslint-disable-line
...
...
@@ -45,6 +59,53 @@ export default {
},
});
},
*
queryUploadTasks
({
payload
},
{
call
,
put
,
select
})
{
const
{
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
const
{
params
}
=
payload
;
const
{
loadingUploadTask
,
taskListQueryParams
}
=
yield
select
(
state
=>
state
.
crm
);
const
newTaskListQueryParams
=
Object
.
assign
(
taskListQueryParams
,
params
,
);
yield
put
({
type
:
'updateState'
,
payload
:
{
loadingUploadTask
:
true
,
},
});
const
uploadTasksList
=
yield
call
(
crmAjax
.
uploadTasks
,
{
type
:
1
,
school_id
:
sid
,
page
:
taskListQueryParams
.
page
,
perPage
:
taskListQueryParams
.
perPage
,
});
yield
put
({
type
:
'updateState'
,
payload
:
{
loadingUploadTask
:
false
,
},
});
const
{
code
,
data
}
=
uploadTasksList
;
if
(
code
==
200
)
{
yield
put
({
type
:
'updateState'
,
payload
:
{
uploadTaskTotal
:
data
.
total
,
uploadTaskList
:
data
.
list
,
taskListQueryParams
:
{
...
newTaskListQueryParams
,
},
},
});
}
else
{
yield
put
({
type
:
'webapp/errorrequestresolve'
,
payload
:
{
data
:
uploadTasksList
,
},
});
}
},
*
queryCrmList
({
payload
},
{
call
,
put
,
select
})
{
const
{
params
}
=
payload
;
const
{
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
...
...
@@ -79,6 +140,111 @@ export default {
});
}
},
*
getverifycode
({
payload
},
{
call
,
put
,
select
})
{
const
{
countdown
,
counting
,
gettingVerifyCoding
}
=
yield
select
(
state
=>
state
.
crm
);
// const { timer } = yield select(state => state.usersetting);
const
{
mobile
}
=
payload
;
let
newCountdowm
=
countdown
;
let
newCounting
=
counting
;
yield
put
({
type
:
'updateState'
,
payload
:
{
gettingVerifyCoding
:
true
,
counting
:
false
,
},
});
const
verifycodehide
=
message
.
loading
(
'正在获取验证码....'
,
0
);
const
data
=
yield
call
(
newregister
.
getVerifyCode
,
{
mobile
,
sms_type
:
10
});
if
(
data
.
code
===
200
)
{
setTimeout
(
verifycodehide
);
message
.
success
(
'验证码获取成功'
,
1
);
yield
put
({
type
:
'updateState'
,
payload
:
{
gettingVerifyCoding
:
false
,
},
});
newCounting
=
true
;
yield
put
({
type
:
'updateState'
,
payload
:
{
counting
:
newCounting
,
},
});
const
setTimer
=
setInterval
(()
=>
{
newCountdowm
--
;
if
(
newCountdowm
<=
0
)
{
newCountdowm
=
60
;
newCounting
=
false
;
clearInterval
(
setTimer
);
}
payload
.
dispatch
({
type
:
'crm/updateState'
,
payload
:
{
countdown
:
newCountdowm
,
counting
:
newCounting
,
timer
:
setTimer
,
},
});
},
1000
);
}
else
{
setTimeout
(
verifycodehide
);
yield
put
({
type
:
'updateState'
,
payload
:
{
gettingVerifyCoding
:
false
,
counting
:
false
,
countdown
:
60
,
},
});
message
.
error
(
data
.
msg
,
1
);
}
},
*
sureBindSjdAccount
({
payload
},
{
call
,
put
,
select
})
{
const
{
bindSjdAccountSubmitting
}
=
yield
select
(
state
=>
state
.
crm
);
const
{
mobile
,
code
,
callback
}
=
payload
;
if
(
bindSjdAccountSubmitting
)
{
return
;
}
yield
put
({
type
:
'updateState'
,
payload
:
{
bindSjdAccountSubmitting
:
true
,
},
});
const
loading
=
message
.
loading
(
'商家岛账号绑定中...'
);
const
bindData
=
yield
call
(
crmAjax
.
bindSjdAccount
,
{
mobile
,
code
,
});
setTimeout
(
loading
);
yield
put
({
type
:
'updateState'
,
payload
:
{
bindSjdAccountSubmitting
:
false
,
},
});
const
{
code
:
ajaxcode
,
data
}
=
bindData
;
if
(
ajaxcode
==
200
)
{
message
.
success
(
'绑定成功'
,
1
);
if
(
callback
&&
(
typeof
callback
==
'function'
))
{
callback
();
}
yield
put
({
type
:
'updateState'
,
payload
:
{
bindSjdAccountShow
:
false
,
},
});
}
else
{
yield
put
({
type
:
'webapp/errorrequestresolve'
,
payload
:
{
data
:
bindData
,
},
});
}
},
*
reset
({
payload
},
{
call
,
put
,
select
})
{
const
{
copyClassListQueryParams
}
=
yield
select
(
state
=>
state
.
classmgt
);
yield
put
({
...
...
@@ -109,6 +275,92 @@ export default {
pathname
:
`/sjd/crmdetail/
${
id
}
`
,
}));
},
*
querysignature
({
payload
},
{
call
,
put
,
select
})
{
const
{
userInfo
,
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
const
{
files
,
}
=
payload
;
const
file
=
files
?
files
[
0
]
:
null
;
const
REGEXP_IMAGE
=
/^image
\/\w
+/
;
const
params
=
{
prefix
:
'excel'
,
token
:
userInfo
.
token
,
schoolId
:
sid
};
let
signature
=
{};
if
(
file
)
{
const
uploaderLoading
=
message
.
loading
(
'正在上传学员'
);
const
uploadSignature
=
yield
call
(
uploader
.
uploadExcelSignature
,
params
);
signature
=
uploadSignature
.
data
;
yield
put
({
type
:
'uploadexcel'
,
payload
:
{
signature
,
files
,
},
});
}
},
*
uploadexcel
({
payload
},
{
call
,
put
})
{
const
{
signature
,
files
,
}
=
payload
;
const
file
=
files
?
files
[
0
]
:
null
;
const
filename
=
`
${
signature
.
dir
}${
getRandomFilename
(
file
.
name
)}
`
;
const
params
=
{
key
:
filename
,
policy
:
signature
.
policy
,
OSSAccessKeyId
:
signature
.
accessid
,
signature
:
signature
.
signature
,
file
,
url
:
signature
.
host
,
};
const
uploadExcel
=
yield
call
(
uploader
.
uploadImg
,
params
);
const
fileName
=
`https://shangjiadao.oss-cn-hangzhou.aliyuncs.com/
${
filename
}
`
;
console
.
log
(
file
,
'file'
);
yield
put
({
type
:
'uploadStudents'
,
payload
:
{
file_name
:
fileName
,
file_info
:
JSON
.
stringify
({
name
:
file
.
name
||
''
,
size
:
file
.
size
||
''
,
type
:
file
.
type
||
''
,
}),
},
});
},
*
uploadStudents
({
payload
},
{
call
,
put
,
select
})
{
const
{
file_name
,
file_info
}
=
payload
;
const
{
sid
}
=
yield
select
(
state
=>
state
.
webapp
);
const
importData
=
yield
call
(
crmAjax
.
uploadStudents
,
{
file_name
,
file_info
,
school_id
:
sid
,
type
:
1
,
});
const
{
code
,
data
}
=
importData
;
if
(
code
==
200
)
{
yield
put
({
type
:
'queryUploadTasks'
,
payload
:
{
params
:
{},
},
});
yield
put
({
type
:
'queryCrmList'
,
payload
:
{
params
:
{},
},
});
}
else
{
yield
put
({
type
:
'webapp/errorrequestresolve'
,
payload
:
{
data
:
importData
,
},
});
}
yield
put
({
type
:
'updateState'
,
});
},
*
pageInit
({
payload
},
{
call
,
put
,
select
})
{
yield
put
({
type
:
'updateState'
,
...
...
src/pages/crm/BindSjdAccount.js
0 → 100644
View file @
585967c2
import
React
from
'react'
;
import
{
connect
}
from
'dva'
;
import
{
Row
,
Col
,
Modal
,
Form
,
Input
,
Button
,
message
}
from
'antd'
;
import
pageStyles
from
'./index.less'
;
import
Cropper
from
'../../components/Cropper'
;
import
{
pageIn
,
imagify
}
from
'../../utils/index'
;
const
{
Search
}
=
Input
;
const
FormItem
=
Form
.
Item
;
class
BindSjdAccount
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
};
}
componentDidMount
()
{
}
componentWillUnmount
()
{
const
{
dispatch
}
=
this
.
props
;
}
sureBindWx
=
()
=>
{
const
{
form
,
submit
}
=
this
.
props
;
form
.
validateFields
((
err
,
values
)
=>
{
if
(
!
err
)
{
submit
(
values
,
()
=>
{
form
.
resetFields
();
});
}
});
}
close
=
()
=>
{
const
{
form
,
close
}
=
this
.
props
;
form
.
resetFields
();
close
();
}
sendVerifyCode
=
()
=>
{
const
{
form
,
sendVerifyCode
,
counting
}
=
this
.
props
;
if
(
counting
)
{
return
;
}
form
.
validateFields
([
'mobile'
],
{},
(
err
,
values
)
=>
{
if
(
!
err
)
{
sendVerifyCode
(
values
,
()
=>
{
form
.
resetFields
();
});
}
});
}
render
()
{
const
{
nickname
,
visible
,
nicknameSubmitting
,
countdown
,
counting
,
gettingVerifyCoding
,
submitting
,
}
=
this
.
props
;
const
{
getFieldDecorator
}
=
this
.
props
.
form
;
return
(
<
div
>
<
Modal
visible
=
{
visible
}
title
=
"关联账户"
cancelText
=
"关闭"
zIndex
=
{
110
}
onOk
=
{
this
.
sureBindWx
}
onCancel
=
{
this
.
close
}
maskClosable
=
{
false
}
okText
=
"立即关联"
okButtonProps
=
{{
size
:
'small'
}}
cancelButtonProps
=
{{
size
:
'small'
}}
confirmLoading
=
{
submitting
}
width
=
{
390
}
>
<
Form
className
=
"modalform"
labelAlign
=
"left"
>
<
Form
.
Item
label
=
"手机号"
hasFeedback
labelCol
=
{{
xs
:
{
span
:
24
},
sm
:
{
span
:
5
},
}}
wrapperCol
=
{{
xs
:
{
span
:
24
},
sm
:
{
span
:
19
},
}}
>
{
getFieldDecorator
(
'mobile'
,
{
rules
:
[
{
required
:
true
,
message
:
'请输入手机号码'
,
},
{
pattern
:
/^1
[
3456789
]{1}[
0-9
]{9}
$/
,
message
:
'请输入正确的手机号码!'
},
],
})(
<
Input
type
=
"text"
maxLength
=
{
20
}
placeholder
=
"请输入商家岛账号"
/>
)}
<
/Form.Item
>
<
Form
.
Item
label
=
"验证码"
labelCol
=
{{
xs
:
{
span
:
24
},
sm
:
{
span
:
5
},
}}
wrapperCol
=
{{
xs
:
{
span
:
24
},
sm
:
{
span
:
19
},
}}
>
<
Row
gutter
=
{
8
}
>
<
Col
span
=
{
14
}
>
{
getFieldDecorator
(
'code'
,
{
rules
:
[
{
required
:
true
,
message
:
'请输入四位数字验证码'
},
{
pattern
:
/^
[
0-9
]{4}
$/
,
message
:
'验证码是4位数字验证码'
},
],
})(
<
Input
type
=
"text"
maxLength
=
{
4
}
placeholder
=
"请输入验证码"
/>
)}
<
/Col
>
<
Col
span
=
{
10
}
style
=
{{
color
:
'#1890FF'
,
cursor
:
'pointer'
,
fontSize
:
'14px'
}}
>
<
div
className
=
{
`
${
pageStyles
.
verycodebtn
}
${
counting
?
pageStyles
.
verycodebtndisable
:
''
}
`
}
onClick
=
{
this
.
sendVerifyCode
}
>
{
counting
?
`
${
countdown
}
秒后重新获取`
:
'发送验证码'
}
<
/div
>
<
/Col
>
<
/Row
>
<
/Form.Item
>
<
/Form
>
<
/Modal
>
<
/div
>
);
}
}
BindSjdAccount
.
propTypes
=
{
};
const
UpdateNickname
=
Form
.
create
()(
BindSjdAccount
);
export
default
UpdateNickname
;
src/pages/crm/ClueImport.js
View file @
585967c2
...
...
@@ -20,9 +20,10 @@ import {
}
from
'antd'
;
import
moment
from
'moment'
;
import
{
routerRedux
}
from
'dva/router'
;
import
pageStyle
from
'./
index
.less'
;
import
pageStyle
from
'./
ClueImport
.less'
;
import
{
pageIn
,
hasBtnPower
,
btnPermission
}
from
'../../utils/index'
;
import
BtnPermission
from
'../../components/BtnPermission'
;
import
{
download
}
from
'../../utils/download'
;
const
{
Option
}
=
Select
;
const
{
TextArea
}
=
Input
;
const
{
Search
}
=
Input
;
...
...
@@ -33,123 +34,68 @@ class ClueImport extends React.Component {
this
.
state
=
{
};
}
downLoadExcelTemp
=
()
=>
{
window
.
open
(
'https://shangjiadao.oss-cn-hangzhou.aliyuncs.com/qingxiao/biz/exceltemp/潜客管理导入模板.xlsx'
);
// download('https://shangjiadao.oss-cn-hangzhou.aliyuncs.com/qingxiao/biz/exceltemp/%E6%BD%9C%E5%AE%A2%E7%AE%A1%E7%90%86%E5%AF%BC%E5%85%A5%E6%A8%A1%E6%9D%BF.xlsx', '潜客管理导入模板.xlsx');
// download('https://shangjiadao.oss-cn-hangzhou.aliyuncs.com/qingxiao/biz/exceltemp/%E6%BD%9C%E5%AE%A2%E7%AE%A1%E7%90%86%E5%AF%BC%E5%85%A5%E6%A8%A1%E6%9D%BF.xlsx');
}
render
()
{
const
{
visible
,
close
,
goBindSjdAccount
,
loading
,
list
,
query
,
total
,
sizeChange
,
changePagination
,
upload
,
refresh
,
}
=
this
.
props
;
const
columns
=
[
{
title
:
'手机号'
,
dataIndex
:
'mobile'
,
key
:
'mobile'
,
render
:
(
text
,
record
)
=>
{
return
(
<
span
className
=
"hreflink"
>
{
text
}
<
/span
>
);
},
},
{
title
:
'意向课程'
,
dataIndex
:
'intent_course.title'
,
key
:
'intent_course.title'
,
title
:
'文件名'
,
dataIndex
:
'file_info'
,
key
:
'file_info'
,
render
:
(
text
,
record
)
=>
{
let
fileInfo
=
{};
try
{
fileInfo
=
JSON
.
parse
(
record
.
file_info
);
}
catch
(
e
)
{
fileInfo
=
{};
}
return
(
<
span
>
{
text
||
'-'
}
<
/span
>
<
span
className
=
"hreflink"
>
{
fileInfo
.
name
||
record
.
file_name
}
<
/span
>
);
},
},
{
title
:
'来源渠道'
,
dataIndex
:
'source_type'
,
key
:
'source_type'
,
render
:
(
text
,
record
)
=>
{
switch
(
text
)
{
case
1
:
return
'在线购买'
;
case
2
:
return
'在线招生'
;
case
3
:
return
'来电咨询'
;
case
4
:
return
'地推'
;
case
5
:
return
'外呼电话'
;
case
6
:
return
'转介绍'
;
case
7
:
return
'客户直访'
;
case
8
:
return
'其他'
;
default
:
return
'-'
;
}
},
},
{
title
:
'录入时间'
,
title
:
'上传时间'
,
dataIndex
:
'created_at'
,
key
:
'created_at'
,
render
:
(
text
,
record
)
=>
{
return
(
<
span
>
{
text
||
'-'
}
<
/span
>
);
},
},
{
title
:
'
活动标题
'
,
dataIndex
:
'
customers
'
,
key
:
'
customers
'
,
title
:
'
操作人
'
,
dataIndex
:
'
final_follow
'
,
key
:
'
final_follow
'
,
render
:
(
text
,
record
)
=>
{
return
(
<
div
>
{
record
.
customers
.
activity_title
?
record
.
customers
.
activity_title
:
'-'
}
<
/div
>
<
span
>
{(
record
.
school_teacher
&&
record
.
school_teacher
.
nickname
)
||
''
}
<
/span
>
);
},
},
{
title
:
'
跟进
状态'
,
title
:
'状态'
,
dataIndex
:
'status'
,
key
:
'status'
,
render
:
(
text
,
record
)
=>
{
switch
(
text
)
{
case
1
:
return
'待跟进'
;
case
2
:
return
'跟进中'
;
case
3
:
return
'已完成'
;
case
4
:
return
'无效'
;
case
5
:
return
'已成功'
;
default
:
return
'-'
;
}
},
},
{
title
:
'最后跟进人'
,
dataIndex
:
'final_follow.nickname'
,
key
:
'nickname'
,
render
:
(
text
,
record
)
=>
{
return
(
<
span
>
{
text
||
'-'
}
<
/span
>
);
},
},
{
title
:
'操作'
,
dataIndex
:
'c'
,
key
:
'c'
,
render
:
(
text
,
record
)
=>
{
return
(
<
div
className
=
{
pageStyle
.
tableoperatebox
}
>
<
span
className
=
"hreflink"
>
购课
<
/span
>
<
Divider
type
=
"vertical"
/>
<
span
className
=
"hreflink"
>
跟进
<
/span
>
<
/div
>
<
span
>
{
record
.
status
==
1
&&
'正在上传'
}
{
record
.
status
==
2
&&
'上传成功'
}
{
record
.
status
==
3
&&
'上传失败'
}
<
/span
>
);
},
},
...
...
@@ -159,44 +105,53 @@ class ClueImport extends React.Component {
title
=
"潜在学员信息导入"
visible
=
{
visible
}
onCancel
=
{
close
}
zIndex
=
{
109
}
width
=
{
887
}
bodyStyle
=
{{
paddingTop
:
18
,
paddingBottom
:
18
,
paddingBottom
:
0
,
}}
className
=
"clueimportmodal"
footer
=
{
<
Button
size
=
"small"
type
=
"primary"
style
=
{{
width
:
75
}}
onClick
=
{
close
}
>
关闭
<
/Button>
}
>
<
div
>
<
Button
size
=
"small"
type
=
"primary"
style
=
{{
marginRight
:
20
}}
>
关联商家岛账号
<
/Button
>
<
Button
size
=
"small"
type
=
"primary"
style
=
{{
marginRight
:
20
}}
>
上传表格
<
/Button
>
<
Button
size
=
"small"
type
=
"primary"
style
=
{{
marginRight
:
5
}}
>
下载模板
<
/Button
>
<
Tooltip
placement
=
"top"
title
=
"下载规定模板,按照顺序要求填写潜在学员信息,即可上传"
>
<
Icon
type
=
"question-circle"
theme
=
"filled"
style
=
{{
opacity
:
0.5
,
fontSize
:
18
,
cursor
:
'pointer'
}}
/
>
<
/Tooltip
>
<
div
style
=
{{
marginBottom
:
20
,
display
:
'flex'
,
justifyContent
:
'space-between'
}}
>
<
div
>
<
Button
size
=
"small"
type
=
"primary"
style
=
{{
marginRight
:
20
}}
onClick
=
{
goBindSjdAccount
}
>
关联商家岛账号
<
/Button
>
<
Button
size
=
"small"
type
=
"primary"
style
=
{{
marginRight
:
20
,
position
:
'relative'
}}
><
input
onChange
=
{
upload
}
type
=
"file"
className
=
{
pageStyle
.
uploadExcel
}
accept
=
".xls, .xlsx"
/>
上传表格
<
/Button
>
<
Button
size
=
"small"
type
=
"primary"
style
=
{{
marginRight
:
5
}}
onClick
=
{
this
.
downLoadExcelTemp
}
>
下载模板
<
/Button
>
<
Tooltip
placement
=
"top"
title
=
"下载规定模板,按照顺序要求填写潜在学员信息,即可上传"
>
<
Icon
type
=
"question-circle"
theme
=
"filled"
style
=
{{
opacity
:
0.5
,
fontSize
:
18
,
cursor
:
'pointer'
}}
/
>
<
/Tooltip
>
<
/div
>
<
div
>
<
Tooltip
placement
=
"top"
title
=
"点击更新文件上传进度"
>
<
Icon
type
=
"sync"
style
=
{{
marginLeft
:
150
,
fontSize
:
18
,
cursor
:
'pointer'
}}
onClick
=
{
refresh
}
/
>
<
/Tooltip
>
<
/div
>
<
/div
>
{
/* <div className={pageStyle.tablebox}>
<Table
rowKey="id"
dataSource={crmList
}
columns={columns
}
scroll={{ x: 'max-content' }
}
pagination={false
}
bordered
footer={null}
/
>
<
Table
rowKey
=
"id"
dataSource
=
{
list
}
columns
=
{
columns
}
scroll
=
{{
x
:
'max-content'
}
}
pagination
=
{
false
}
loading
=
{
loading
}
footer
=
{
null
}
/
>
<
div
className
=
{
pageStyle
.
tablebox
}
>
<
div
className
=
{
pageStyle
.
tablefooterbox
}
>
<span className={pageStyle.tablefooterstatic}>共{
crmT
otal}条数据</span>
<
span
className
=
{
pageStyle
.
tablefooterstatic
}
>
共
{
t
otal
}
条数据
<
/span
>
<
Pagination
showSizeChanger
showQuickJumper
onShowSizeChange={
this.
sizeChange}
total={
Number(crmTotal)
}
onChange={
this.
changePagination}
current={Number(
crmParams
.page) || 1}
pageSize={
crmParams.perPage
}
onShowSizeChange
=
{
sizeChange
}
total
=
{
total
}
onChange
=
{
changePagination
}
current
=
{
Number
(
query
.
page
)
||
1
}
pageSize
=
{
Number
(
query
.
perPage
)
}
/
>
<
/div
>
</div>
*/
}
<
/div
>
<
/Modal
>
);
}
...
...
src/pages/crm/ClueImport.less
View file @
585967c2
@import '../../less/variables.less';
.container {
background-color: #fff;
border-radius: 2px;
padding: 20px;
}
.headerbox{
background-color: #fff;
.headerbtn {
margin: 0 16px 0 0;
}
padding-bottom: 18px;
}
.searchrow {
.formitem {
display: flex;
align-items: center;
margin-bottom: 16px;
.formitemlabel {
color: #000000;
font-size: 14px;
line-height: 1;
min-width: 70px;
white-space: nowrap;
}
}
}
.selectitem {
display: block;
}
.searchbtnbox {
height: 54px;
display: flex;
align-items: flex-end;
}
.resetbtn {
margin-right: 16px;
}
.tablebox {
background: #fff;
:global {
.ant-table-fixed {
border-bottom: 1px solid #E8E8E8 !important;
}
.ant-table tbody tr:nth-child(2n) {
background-color: #FBFBFB;
}
.ant-table-thead > tr > th, .ant-table-tbody > tr > td {
padding: 11px 10px;
}
.ant-table-bordered .ant-table-tbody tr td {
border-bottom: none;
border-right: none!important;
}
.ant-table-bordered .ant-table-thead tr th {
border-bottom: none;
border-right: none!important;
}
.ant-table-bordered .ant-table-thead tr th:last-child {
border-right: 1px solid #e8e8e8!important;
:global {
.clueimportmodal {
.ant-table {
.ant-table-tbody {
.ant-table-row:last-child {
// td {
// border-bottom: none;
// }
}
}
.ant-table-placeholder {
border-bottom: none;
}
}
.ant-table-bordered .ant-table-tbody tr td:last-child {
border-right: 1px solid #e8e8e8!important;
}
.ant-table-bordered .ant-table-tbody tr:last-child {
border-bottom: 1px solid #e8e8e8!important;
}
}
}
.divideline {
color: #E9E9E9;
padding: 0 8px;
}
.alink {
color: #1890FF;
}
.classNamebox {
max-width: 250px;
word-break: break-all;
color: #1890FF;
cursor: pointer;
// background-color: #fff;
}
.tableoperatebox {
min-width: 160px;
line-height: 30px;
}
.endsearchcol {
margin-bottom: 18px;
}
.endclassfooter {
display: flex;
align-items: center;
justify-content: space-between;
}
.classroomitem {
margin-bottom: 5px;
.classroom {
color:rgba(0,0,0,0.85);
}
.aLink {
color: #1890FF;
padding-left: 21px;
}
}
.activeamount{
background-color: #cccccc;
color: #666666;
text-align: center;
line-height: 50px;
font-size: 20px;
}
.rightList {
display: flex;
align-items: center;
height: 32px;
.resetIcon {
width: 14px;
height: 14px;
cursor: pointer;
margin-right: 15px;
}
.expend {
font-size:13px;
font-family:PingFangSC-Regular,PingFang SC;
font-weight:400;
color:rgba(102,102,102,1);
display: inline-block;
cursor: pointer;
}
}
.tablefooterbox {
color: rgba(0,0,0,0.6);
...
...
@@ -138,49 +23,19 @@
background-color: #fff;
align-items: center;
justify-content: space-between;
padding-left: 14px;
padding-right: 14px;
margin-top: 10px;
//
padding-left: 14px;
//
padding-right: 14px;
//
margin-top: 10px;
.tablefooterstatic {
color:rgba(0,0,0,0.65);
font-size: 14px;
}
}
.tabList {
display: flex;
border-bottom: 1px solid #E8E8E8;
margin-bottom: 18px;
.tabItem {
font-size:13px;
font-weight:400;
color:rgba(102,102,102,1);
line-height:18px;
padding: 0 30px 10px;
cursor: pointer;
position: relative;
&:hover {
font-weight:600;
color:#1890FF;
}
}
.tabItemActive {
font-size:13px;
font-weight:600;
color:#1890FF;
line-height:18px;
padding: 0 30px 10px;
cursor: pointer;
position: relative;
font-family:PingFangSC-Semibold,PingFang SC;
&:after {
content: '';
display: block;
width: 30%;
height: 2px;
background-color: #1890FF;
position: absolute;
bottom: 0;
left: 35%;
}
}
}
.uploadExcel {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
opacity: 0;
}
\ No newline at end of file
src/pages/crm/index.js
View file @
585967c2
...
...
@@ -24,6 +24,7 @@ import pageStyle from './index.less';
import
{
pageIn
,
hasBtnPower
,
btnPermission
}
from
'../../utils/index'
;
import
BtnPermission
from
'../../components/BtnPermission'
;
import
ClueImport
from
'./ClueImport'
;
import
BindSjdAccount
from
'./BindSjdAccount'
;
const
{
Option
}
=
Select
;
const
{
TextArea
}
=
Input
;
const
{
Search
}
=
Input
;
...
...
@@ -123,7 +124,28 @@ class Crm extends React.Component {
});
}
closeClueImport
=
()
=>
{
console
.
log
(
'哈哈哈'
);
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'crm/updateState'
,
payload
:
{
clueImportShow
:
false
,
},
});
}
toImportClue
=
()
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'crm/updateState'
,
payload
:
{
clueImportShow
:
true
,
},
});
dispatch
({
type
:
'crm/queryUploadTasks'
,
payload
:
{
params
:
{},
},
});
}
goDetail
=
(
record
)
=>
{
const
{
dispatch
}
=
this
.
props
;
...
...
@@ -134,12 +156,109 @@ class Crm extends React.Component {
},
});
}
closeBindSjdAccount
=
()
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'crm/updateState'
,
payload
:
{
bindSjdAccountShow
:
false
,
},
});
}
goBindSjdAccount
=
()
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'crm/updateState'
,
payload
:
{
bindSjdAccountShow
:
true
,
},
});
}
taskListSizeChange
=
(
page
,
perPage
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'crm/queryUploadTasks'
,
payload
:
{
params
:
{
page
:
1
,
perPage
,
},
},
});
}
taskListChangePagination
=
(
page
,
perPage
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'crm/queryUploadTasks'
,
payload
:
{
params
:
{
page
,
perPage
,
},
},
});
}
sureBindSjdAccount
=
(
val
,
callback
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'crm/sureBindSjdAccount'
,
payload
:
{
...
val
,
callback
,
},
});
}
sendVerifyCode
=
(
value
)
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'crm/getverifycode'
,
payload
:
{
mobile
:
value
.
mobile
,
dispatch
,
},
});
}
uploadExcel
=
(
e
)
=>
{
const
{
files
}
=
e
.
target
;
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'crm/querysignature'
,
payload
:
{
files
,
},
});
}
refreshUploadStatus
=
()
=>
{
const
{
dispatch
}
=
this
.
props
;
dispatch
({
type
:
'crm/queryUploadTasks'
,
payload
:
{
params
:
{},
},
});
dispatch
({
type
:
'crm/queryCrmList'
,
payload
:
{
params
:
{},
},
});
}
render
()
{
const
{
userPermission
,
crmList
,
crmTotal
,
crmParams
,
clueImportShow
,
bindSjdAccountShow
,
loadingUploadTask
,
uploadTaskList
,
taskListQueryParams
,
uploadTaskTotal
,
countdown
,
counting
,
gettingVerifyCoding
,
bindSjdAccountSubmitting
,
}
=
this
.
props
;
const
{
isExpendMore
}
=
this
.
state
;
const
columns
=
[
...
...
@@ -262,7 +381,7 @@ class Crm extends React.Component {
<
div
className
=
{
pageStyle
.
container
}
>
<
div
className
=
{
pageStyle
.
headerbox
}
>
<
Button
size
=
"small"
className
=
{
pageStyle
.
headerbtn
}
type
=
"primary"
onClick
=
{
this
.
goAddCrm
}
>
添加潜在学员
<
/Button
>
<
Button
size
=
"small"
className
=
{
pageStyle
.
headerbtn
}
>
一键导入
<
/Button
>
<
Button
size
=
"small"
className
=
{
pageStyle
.
headerbtn
}
onClick
=
{
this
.
toImportClue
}
>
一键导入
<
/Button
>
<
/div
>
<
div
className
=
{
pageStyle
.
searchbox
}
>
<
Row
...
...
@@ -366,8 +485,27 @@ class Crm extends React.Component {
<
/div
>
<
/div
>
<
ClueImport
visible
visible
=
{
clueImportShow
}
close
=
{
this
.
closeClueImport
}
goBindSjdAccount
=
{
this
.
goBindSjdAccount
}
loading
=
{
loadingUploadTask
}
list
=
{
uploadTaskList
}
query
=
{
taskListQueryParams
}
total
=
{
uploadTaskTotal
}
sizeChange
=
{
this
.
taskListSizeChange
}
changePagination
=
{
this
.
taskListChangePagination
}
upload
=
{
this
.
uploadExcel
}
refresh
=
{
this
.
refreshUploadStatus
}
/
>
<
BindSjdAccount
visible
=
{
bindSjdAccountShow
}
submit
=
{
this
.
sureBindSjdAccount
}
close
=
{
this
.
closeBindSjdAccount
}
sendVerifyCode
=
{
this
.
sendVerifyCode
}
countdown
=
{
countdown
}
counting
=
{
counting
}
submitting
=
{
bindSjdAccountSubmitting
}
gettingVerifyCoding
=
{
gettingVerifyCoding
}
/
>
<
/div
>
);
...
...
@@ -383,6 +521,16 @@ function mapStateToProps(state) {
crmList
,
crmTotal
,
crmParams
,
clueImportShow
,
bindSjdAccountShow
,
loadingUploadTask
,
uploadTaskList
,
taskListQueryParams
,
uploadTaskTotal
,
countdown
,
counting
,
gettingVerifyCoding
,
bindSjdAccountSubmitting
,
}
=
state
.
crm
;
const
{
guideStep
,
...
...
@@ -398,6 +546,16 @@ function mapStateToProps(state) {
crmList
,
crmTotal
,
crmParams
,
clueImportShow
,
bindSjdAccountShow
,
loadingUploadTask
,
uploadTaskList
,
taskListQueryParams
,
uploadTaskTotal
,
countdown
,
counting
,
gettingVerifyCoding
,
bindSjdAccountSubmitting
,
};
}
export
default
connect
(
mapStateToProps
)(
CrmForm
);
...
...
src/services/crm.js
View file @
585967c2
...
...
@@ -24,3 +24,26 @@ export function crmEdit(params) {
data
,
});
}
export
function
uploadTasks
(
params
)
{
const
data
=
qs
.
stringify
(
params
);
return
request
({
url
:
`
${
api
.
crm
.
uploadTasks
}
?
${
data
}
`
,
method
:
'GET'
,
});
}
export
function
uploadStudents
(
params
)
{
const
data
=
qs
.
stringify
(
params
);
return
request
({
url
:
`
${
api
.
crm
.
uploadTasks
}
`
,
method
:
'POST'
,
data
,
});
}
export
function
bindSjdAccount
(
params
)
{
const
data
=
qs
.
stringify
(
params
);
return
request
({
url
:
`
${
api
.
crm
.
bindSjdAccount
}
`
,
method
:
'POST'
,
data
,
});
}
src/services/uploader.js
View file @
585967c2
...
...
@@ -25,6 +25,7 @@ export function uploadVideoSignature({ type, token, schoolId }) {
},
});
}
export
function
uploadVideo
(
params
)
{
const
{
OSSAccessKeyId
,
...
...
@@ -61,6 +62,17 @@ export function uploadImageSignature({ type, token, schoolId }) {
},
});
}
export
function
uploadExcelSignature
({
prefix
,
token
,
schoolId
})
{
return
request
({
url
:
`
${
api
.
imgupload
}
`
,
method
:
'GET'
,
params
:
{
prefix
,
token
,
school_id
:
schoolId
,
},
});
}
export
function
uploadImg
(
params
,
progressCallback
,
cancelToken
)
{
const
{
OSSAccessKeyId
,
...
...
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