Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
M
merchant-manage-ui
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
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
ui
merchant-manage-ui
Commits
d8defddc
Commit
d8defddc
authored
Nov 09, 2022
by
武广
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of git.quantgroup.cn:ui/merchant-manage-ui into feature/addr20221031
parents
0fc293c8
baa19369
Changes
49
Show whitespace changes
Inline
Side-by-side
Showing
49 changed files
with
4076 additions
and
367 deletions
+4076
-367
config.js
config/config.js
+9
-2
env.config.js
config/env.config.js
+10
-4
index.jsx
src/components/FormSearch/index.jsx
+199
-0
index.less
src/components/FormSearch/index.less
+32
-0
MessageIcon.jsx
src/components/GlobalHeader/MessageIcon.jsx
+33
-0
RightContent.jsx
src/components/GlobalHeader/RightContent.jsx
+4
-1
index.less
src/components/GlobalHeader/index.less
+21
-0
index.jsx
src/components/MessageReminder/Complex/index.jsx
+346
-0
index.less
src/components/MessageReminder/Complex/index.less
+154
-0
index.jsx
src/components/MessageReminder/Empty/index.jsx
+17
-0
index.less
src/components/MessageReminder/Empty/index.less
+12
-0
index.jsx
src/components/MessageReminder/Simple/index.jsx
+269
-0
index.less
src/components/MessageReminder/Simple/index.less
+199
-0
index.jsx
src/components/MessageReminder/index.jsx
+9
-0
global.less
src/global.less
+16
-0
BasicLayout.jsx
src/layouts/BasicLayout.jsx
+35
-6
messageReminder.js
src/models/messageReminder.js
+65
-0
index.jsx
src/pages/Admin/index.jsx
+16
-3
AfterLog.jsx
src/pages/AfterSaleManage/components/AfterLog.jsx
+1
-1
CancelAuditModal.jsx
src/pages/AfterSaleManage/components/CancelAuditModal.jsx
+113
-0
CancelDetailTable.jsx
src/pages/AfterSaleManage/components/CancelDetailTable.jsx
+35
-0
CancelRejectModal.jsx
src/pages/AfterSaleManage/components/CancelRejectModal.jsx
+61
-0
LogisticsRecordModal.jsx
...pages/AfterSaleManage/components/LogisticsRecordModal.jsx
+79
-0
auditModal.jsx
src/pages/AfterSaleManage/components/auditModal.jsx
+131
-116
proofsModal.jsx
src/pages/AfterSaleManage/components/proofsModal.jsx
+1
-1
data.js
src/pages/AfterSaleManage/data.js
+531
-150
index.jsx
src/pages/AfterSaleManage/index.jsx
+432
-14
index.less
src/pages/AfterSaleManage/index.less
+61
-0
services.js
src/pages/AfterSaleManage/services.js
+67
-22
index.jsx
src/pages/AfterSaleManageOld/PassAudit/index.jsx
+0
-0
index.jsx
src/pages/AfterSaleManageOld/Pending/index.jsx
+0
-0
AfterLog.jsx
src/pages/AfterSaleManageOld/components/AfterLog.jsx
+38
-0
auditModal.jsx
src/pages/AfterSaleManageOld/components/auditModal.jsx
+243
-0
detailTable.jsx
src/pages/AfterSaleManageOld/components/detailTable.jsx
+29
-0
proofsModal.jsx
src/pages/AfterSaleManageOld/components/proofsModal.jsx
+43
-0
rejectModal.jsx
src/pages/AfterSaleManageOld/components/rejectModal.jsx
+61
-0
data.js
src/pages/AfterSaleManageOld/data.js
+179
-0
index.jsx
src/pages/AfterSaleManageOld/index.jsx
+30
-0
services.js
src/pages/AfterSaleManageOld/services.js
+95
-0
styles.less
src/pages/AfterSaleManageOld/styles.less
+0
-0
FormPackage.jsx
src/pages/ServiceGoods/components/FormPackage.jsx
+3
-3
storeModal.jsx
src/pages/chainStoreManage/components/storeModal.jsx
+6
-2
index.jsx
src/pages/orderManage/pendingDeliveryOrder/index.jsx
+115
-4
index.less
src/pages/orderManage/pendingDeliveryOrder/index.less
+78
-0
styles.less
src/pages/orderManage/pendingDeliveryOrder/styles.less
+0
-38
messageReminder.js
src/services/messageReminder.js
+41
-0
constants.js
src/utils/constants.js
+1
-0
utils.js
src/utils/utils.js
+18
-0
websocket.js
src/utils/websocket.js
+138
-0
No files found.
config/config.js
View file @
d8defddc
...
@@ -146,19 +146,26 @@ export default {
...
@@ -146,19 +146,26 @@ export default {
icon
:
'
smile
'
,
icon
:
'
smile
'
,
component
:
'
./AfterSaleManage/index
'
,
component
:
'
./AfterSaleManage/index
'
,
},
},
{
title
:
'
商户管理后台
'
,
path
:
'
/afterSaleManageOld
'
,
name
:
'
afterSaleManageOld
'
,
icon
:
'
smile
'
,
component
:
'
./AfterSaleManageOld/index
'
,
},
{
{
title
:
'
商户管理后台
'
,
title
:
'
商户管理后台
'
,
path
:
'
/auditPending
'
,
path
:
'
/auditPending
'
,
name
:
'
auditPending
'
,
name
:
'
auditPending
'
,
icon
:
'
smile
'
,
icon
:
'
smile
'
,
component
:
'
./AfterSaleManage/Pending
'
,
component
:
'
./AfterSaleManage
Old
/Pending
'
,
},
},
{
{
title
:
'
商户管理后台
'
,
title
:
'
商户管理后台
'
,
path
:
'
/passAudit
'
,
path
:
'
/passAudit
'
,
name
:
'
passAudit
'
,
name
:
'
passAudit
'
,
icon
:
'
smile
'
,
icon
:
'
smile
'
,
component
:
'
./AfterSaleManage/PassAudit
'
,
component
:
'
./AfterSaleManage
Old
/PassAudit
'
,
},
},
{
{
title
:
'
商户管理后台
'
,
title
:
'
商户管理后台
'
,
...
...
config/env.config.js
View file @
d8defddc
...
@@ -2,14 +2,16 @@ const isProduction = process.env.NODE_ENV === 'production';
...
@@ -2,14 +2,16 @@ const isProduction = process.env.NODE_ENV === 'production';
const
isPre
=
process
.
env
.
PRE_ENV
===
'
pre
'
;
const
isPre
=
process
.
env
.
PRE_ENV
===
'
pre
'
;
const
environment
=
'
sc
'
;
const
environment
=
'
sc
'
;
const
envAPi
=
{
const
envAPi
=
{
api
:
`https://security-
${
environment
}
.liangkebang.net`
,
api
:
`https://security-
${
environment
}
.liangkebang.net`
,
//'https://security-xyqb.liangkebang.net',
kdspOpApi
:
`https://sc-merchant-api-
${
environment
}
.liangkebang.net`
,
kdspOpApi
:
`https://sc-merchant-api-
${
environment
}
.liangkebang.net`
,
kdspApi
:
`https://sc-merchant-api-
${
environment
}
.liangkebang.net`
,
kdspApi
:
`https://sc-merchant-api-
${
environment
}
.liangkebang.net`
,
goodsApi
:
`https://sc-merchant-api-
${
environment
}
.liangkebang.net`
,
goodsApi
:
`https://sc-merchant-api-
${
environment
}
.liangkebang.net`
,
querysApi
:
`https://sc-merchant-api-
${
environment
}
.liangkebang.net
/admin/merchant/sc-settlement
`
,
querysApi
:
`https://sc-merchant-api-
${
environment
}
.liangkebang.net`
,
prologueDomain
:
`https://mall-
${
environment
}
.liangkebang.net`
,
prologueDomain
:
`https://mall-
${
environment
}
.liangkebang.net`
,
qiniuHost
:
`https://kdspstatic.q-gp.com/`
,
qiniuHost
:
'
https://kdspstatic.q-gp.com/
'
,
opapiHost
:
`https://yxm-gateway-
${
environment
}
.liangkebang.net`
,
opapiHost
:
`https://opapi-
${
environment
}
.liangkebang.net`
,
wsApi
:
`wss://push-
${
environment
}
.liangkebang.net`
,
msgApi
:
`https://msgapi-
${
environment
}
.liangkebang.net`
,
};
};
const
prodApi
=
{
const
prodApi
=
{
...
@@ -25,6 +27,8 @@ const prodApi = {
...
@@ -25,6 +27,8 @@ const prodApi = {
// opapiHost: 'https://opapi.q-gp.com',
// opapiHost: 'https://opapi.q-gp.com',
// querysApi: 'https://sc-settlement-api.q-gp.com',
// querysApi: 'https://sc-settlement-api.q-gp.com',
querysApi
:
'
https://sc-merchant-api.q-gp.com/admin/merchant/sc-settlement
'
,
querysApi
:
'
https://sc-merchant-api.q-gp.com/admin/merchant/sc-settlement
'
,
wsApi
:
'
wss://push.q-gp.com
'
,
msgApi
:
'
https://msgapi.q-gp.com
'
,
};
};
const
preProdApi
=
{
const
preProdApi
=
{
...
@@ -36,6 +40,8 @@ const preProdApi = {
...
@@ -36,6 +40,8 @@ const preProdApi = {
qiniuHost
:
'
https://kdspstatic.q-gp.com/
'
,
qiniuHost
:
'
https://kdspstatic.q-gp.com/
'
,
opapiHost
:
'
https://opapi-pre.q-gp.com
'
,
opapiHost
:
'
https://opapi-pre.q-gp.com
'
,
querysApi
:
'
https://sc-settlement-api.q-gp.com
'
,
querysApi
:
'
https://sc-settlement-api.q-gp.com
'
,
wsApi
:
'
wss://push.q-gp.com
'
,
msgApi
:
'
https://msgapi.q-gp.com
'
,
};
};
let
exportApi
=
envAPi
;
let
exportApi
=
envAPi
;
...
...
src/components/FormSearch/index.jsx
0 → 100644
View file @
d8defddc
import
React
,
{
useState
}
from
'
react
'
;
import
{
Form
,
Input
,
Select
,
DatePicker
,
Button
,
Space
}
from
'
antd
'
;
import
moment
from
'
moment
'
;
import
style
from
'
./index.less
'
;
const
{
Option
}
=
Select
;
const
{
RangePicker
}
=
DatePicker
;
const
SEARCH_TYPE
=
{
SELECT
:
'
select
'
,
RANGE_PICKER
:
'
range_picker
'
,
INPUT
:
'
input
'
,
DATE_PICKER
:
'
date_picker
'
,
};
const
FormSearch
=
props
=>
{
const
{
width
=
'
100%
'
,
form
,
initialValues
=
{},
onFinish
=
()
=>
{},
formConfig
,
formOptions
,
btnConfig
,
}
=
props
;
const
FormItemBox
=
({
bindKey
,
label
,
column
,
children
,
afterRender
})
=>
{
const
columnValue
=
column
;
// if (afterRender) {
// columnValue = '';
// }
return
(
<
Form
.
Item
className=
{
style
[
'
custom-form-item
'
]
}
column=
{
columnValue
}
name=
{
bindKey
}
label=
{
label
}
>
{
children
}
</
Form
.
Item
>
);
};
// 下拉框类型
const
FormItemSelect
=
config
=>
{
const
{
bindKey
,
options
:
configOptions
=
[],
originOptions
=
{},
afterRender
=
null
,
afterOptions
=
{},
column
,
}
=
config
;
const
attrs
=
{
placeholder
:
'
请选择
'
,
...
originOptions
};
// 提取公共部分
const
BaseSelectElement
=
()
=>
(
<
FormItemBox
{
...
config
}
>
<
Select
name=
{
bindKey
}
className=
{
style
[
'
form-item-tag
'
]
}
{
...
attrs
}
>
{
configOptions
.
map
(
option
=>
(
<
Option
key=
{
option
.
value
}
value=
{
option
.
value
}
>
{
option
.
name
}
</
Option
>
))
}
</
Select
>
</
FormItemBox
>
);
if
(
afterRender
)
{
return
(
<>
{
/* // <div className={style['custom-form-item-group']}> */
}
<
BaseSelectElement
/>
<
FormItemBox
column=
{
column
}
{
...
afterOptions
}
>
{
afterRender
()
}
</
FormItemBox
>
{
/* // </div> */
}
</>
);
}
return
<
BaseSelectElement
/>;
};
// 选择日期范围类型
const
FormItemRangePicker
=
config
=>
{
const
{
originOptions
=
{},
bindKey
,
limit
}
=
config
;
const
attrs
=
{
placeholder
:
[
'
开始日期
'
,
'
结束日期
'
],
...
originOptions
};
// 自动截取日期限制的范围
const
onOpenChange
=
open
=>
{
if
(
!
open
)
{
if
(
limit
)
{
const
{
rangeNum
,
rangeUnit
}
=
limit
;
const
[
date1
,
date2
]
=
form
.
getFieldValue
(
bindKey
);
const
diffNum
=
date2
.
diff
(
date1
,
rangeUnit
);
const
unitMapping
=
{
months
:
'
month
'
,
};
if
(
diffNum
>=
rangeNum
)
{
setTimeout
(()
=>
{
form
.
setFieldsValue
({
[
bindKey
]:
[
moment
(
date1
),
moment
(
date1
).
add
(
rangeNum
,
unitMapping
[
rangeUnit
])],
});
});
}
}
}
};
return
(
<
FormItemBox
{
...
config
}
>
<
RangePicker
className=
{
style
[
'
form-item-tag
'
]
}
onOpenChange=
{
onOpenChange
}
{
...
attrs
}
/>
</
FormItemBox
>
);
};
// 选择日期
const
FormItemDatePicker
=
config
=>
{
const
{
originOptions
=
{}
}
=
config
;
const
attrs
=
{
placeholder
:
'
请选择日期
'
,
...
originOptions
};
return
(
<
FormItemBox
{
...
config
}
>
<
DatePicker
className=
{
style
[
'
form-item-tag
'
]
}
{
...
attrs
}
/>
</
FormItemBox
>
);
};
// 多级联动
// 输入框类型
const
FormItemInput
=
config
=>
{
const
{
originOptions
=
{}
}
=
config
;
const
attrs
=
{
placeholder
:
'
请输入
'
,
allowClear
:
true
,
...
originOptions
};
return
(
<
FormItemBox
{
...
config
}
>
<
Input
className=
{
style
[
'
form-item-tag
'
]
}
{
...
attrs
}
/>
</
FormItemBox
>
);
};
// 表单内容元素
const
FormItemElement
=
()
=>
formConfig
.
map
(
config
=>
{
const
{
type
}
=
config
;
switch
(
type
)
{
case
SEARCH_TYPE
.
SELECT
:
return
<
FormItemSelect
key=
{
config
.
bindKey
}
{
...
config
}
/>;
case
SEARCH_TYPE
.
RANGE_PICKER
:
return
<
FormItemRangePicker
key=
{
config
.
bindKey
}
{
...
config
}
/>;
case
SEARCH_TYPE
.
INPUT
:
return
<
FormItemInput
key=
{
config
.
bindKey
}
{
...
config
}
/>;
case
SEARCH_TYPE
.
DATE_PICKER
:
return
<
FormItemDatePicker
key=
{
config
.
bindKey
}
{
...
config
}
/>;
default
:
return
<></>;
}
});
/**
* @module 按钮操作
*/
const
FormItemButton
=
()
=>
(
<
Space
size=
{
10
}
>
{
btnConfig
.
map
(
config
=>
{
const
{
label
,
onClick
=
()
=>
{},
type
=
'
primary
'
,
clickType
=
'
search
'
}
=
config
;
const
htmlType
=
clickType
===
'
reset
'
?
'
reset
'
:
'
submit
'
;
const
callback
=
()
=>
{
setTimeout
(()
=>
{
onClick
({
type
:
clickType
,
params
:
form
.
getFieldValue
()
});
});
};
return
(
<
Button
key=
{
clickType
}
type=
{
type
}
htmlType=
{
htmlType
}
onClick=
{
callback
}
>
{
label
}
</
Button
>
);
})
}
</
Space
>
);
return
(
<
div
className=
{
style
[
'
form-search
'
]
}
>
<
Form
style=
{
{
width
}
}
layout=
"inline"
form=
{
form
}
initialValues=
{
initialValues
}
onFinish=
{
onFinish
}
{
...
formOptions
}
>
<
FormItemElement
></
FormItemElement
>
<
Form
.
Item
>
<
FormItemButton
></
FormItemButton
>
</
Form
.
Item
>
</
Form
>
</
div
>
);
};
export
{
FormSearch
,
SEARCH_TYPE
};
src/components/FormSearch/index.less
0 → 100644
View file @
d8defddc
.form-search {
padding: 15px;
background: #fff;
}
.custom-form-item {
min-width: 320px;
margin-bottom: 20px !important;
&[column='1'] {
width: 100%;
}
&[column='2'] {
width: calc(50% - 16px);
}
&[column='3'] {
width: calc(33.3333% - 32px);
}
&[column='4'] {
width: calc(25% - 48px);
}
&[column='5'] {
width: calc(20% - 54px);
}
}
.form-item-tag {
width: 100%;
}
// .custom-form-item-group {
// display: flex;
// }
src/components/GlobalHeader/MessageIcon.jsx
0 → 100644
View file @
d8defddc
import
React
,
{
useState
,
useEffect
}
from
'
react
'
;
import
{
Tag
,
message
,
Badge
}
from
'
antd
'
;
import
{
connect
}
from
'
dva
'
;
import
groupBy
from
'
lodash/groupBy
'
;
import
moment
from
'
moment
'
;
import
{
BellOutlined
}
from
'
@ant-design/icons
'
;
import
NoticeIcon
from
'
../NoticeIcon
'
;
import
styles
from
'
./index.less
'
;
const
GlobalHeaderRight
=
props
=>
{
const
{
messageReminderComplexRef
,
unReadCount
=
0
}
=
props
;
const
[
count
,
setCount
]
=
useState
(
0
);
const
open
=
()
=>
{
messageReminderComplexRef
.
current
.
open
();
};
useEffect
(()
=>
{
setCount
(
unReadCount
);
},
[
unReadCount
]);
return
(
<
span
className=
{
styles
[
'
badge-box
'
]
}
onClick=
{
open
}
>
<
Badge
count=
{
count
}
className=
{
styles
[
'
badge-box__self
'
]
}
>
<
BellOutlined
className=
{
styles
[
'
badge-box__icon
'
]
}
/>
</
Badge
>
</
span
>
);
};
export
default
connect
(({
messageReminder
})
=>
({
unReadCount
:
messageReminder
.
unReadCount
,
}))(
GlobalHeaderRight
);
src/components/GlobalHeader/RightContent.jsx
View file @
d8defddc
import
{
Tooltip
}
from
'
antd
'
;
import
{
Tooltip
}
from
'
antd
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
{
NotificationOutlined
}
from
'
@ant-design/icons
'
;
import
{
connect
}
from
'
dva
'
;
import
{
connect
}
from
'
dva
'
;
import
Avatar
from
'
./AvatarDropdown
'
;
import
Avatar
from
'
./AvatarDropdown
'
;
import
HeaderSearch
from
'
../HeaderSearch
'
;
import
HeaderSearch
from
'
../HeaderSearch
'
;
import
styles
from
'
./index.less
'
;
import
styles
from
'
./index.less
'
;
import
MessageIcon
from
'
./MessageIcon
'
;
const
GlobalHeaderRight
=
props
=>
{
const
GlobalHeaderRight
=
props
=>
{
const
{
theme
,
layout
}
=
props
;
const
{
theme
,
layout
,
messageReminderComplexRef
}
=
props
;
let
className
=
styles
.
right
;
let
className
=
styles
.
right
;
if
(
theme
===
'
dark
'
&&
layout
===
'
topmenu
'
)
{
if
(
theme
===
'
dark
'
&&
layout
===
'
topmenu
'
)
{
...
@@ -15,6 +17,7 @@ const GlobalHeaderRight = props => {
...
@@ -15,6 +17,7 @@ const GlobalHeaderRight = props => {
return
(
return
(
<
div
className=
{
className
}
>
<
div
className=
{
className
}
>
<
MessageIcon
messageReminderComplexRef=
{
messageReminderComplexRef
}
/>
<
Avatar
/>
<
Avatar
/>
</
div
>
</
div
>
);
);
...
...
src/components/GlobalHeader/index.less
View file @
d8defddc
...
@@ -132,3 +132,24 @@
...
@@ -132,3 +132,24 @@
}
}
}
}
}
}
// Badge
.badge-box {
display: inline-block;
height: 100%;
padding: 0 12px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: rgba(0, 0, 0, 0.025);
}
&__self {
font-size: 16px !important;
}
&__icon {
padding: 4px;
vertical-align: middle;
}
}
src/components/MessageReminder/Complex/index.jsx
0 → 100644
View file @
d8defddc
import
React
,
{
useState
,
useEffect
,
forwardRef
,
useImperativeHandle
}
from
'
react
'
;
import
{
useHistory
}
from
'
react-router-dom
'
;
import
{
Modal
,
Tabs
,
Pagination
,
Spin
,
notification
,
Checkbox
,
Badge
}
from
'
antd
'
;
import
{
apiGetBussinessMsgList
,
apiGetBusinessMsgUnReadCount
}
from
'
@/services/messageReminder
'
;
import
{
connect
}
from
'
dva
'
;
import
Empty
from
'
../Empty
'
;
import
styles
from
'
./index.less
'
;
import
{
CHANNEL_ID
}
from
'
@/utils/constants
'
;
const
INIT_PAGINATION
=
{
pageSize
:
20
,
pageNo
:
1
,
};
const
INIT_QUERY_PARAMS
=
{
type
:
'
0
'
,
readStatus
:
''
,
};
const
MessageItem
=
props
=>
{
const
{
item
,
onMark
,
viewDetail
}
=
props
;
/**
* type: 0订单消息,1售后消息
* readStatus: 0未读,1已读
*/
const
{
readStatus
,
type
}
=
item
;
let
message
=
{};
try
{
message
=
JSON
.
parse
(
item
.
sendContent
||
'
{}
'
);
}
catch
(
e
)
{
console
.
error
(
'
消息数据格式错误
'
);
}
const
goodList
=
message
.
items
.
map
((
good
,
index
)
=>
(
<
div
className=
{
styles
.
good
}
key=
{
String
(
index
)
}
>
<
span
className=
{
styles
.
good__name
}
>
{
good
.
skuName
}
</
span
>
<
span
className=
{
styles
.
good__count
}
>
x
{
good
.
quantity
}
</
span
>
</
div
>
));
return
(
<
div
className=
{
styles
[
'
complex-list__item
'
]
}
>
<
div
className=
{
styles
[
'
complex-list__item--header
'
]
}
>
<
span
className=
{
styles
[
'
order-number
'
]
}
>
订单编号:
{
message
.
orderNo
}
</
span
>
<
span
className=
{
styles
.
time
}
>
订单时间:
{
message
.
time
}
</
span
>
<
span
className=
{
styles
[
'
read-status
'
]
}
>
{
readStatus
===
0
?
(
<
a
className=
{
styles
[
'
read-status--un-read
'
]
}
onClick=
{
()
=>
onMark
([
item
.
id
])
}
>
标记为已读
</
a
>
)
:
(
<
span
className=
{
styles
[
'
read-status--read
'
]
}
>
已读
</
span
>
)
}
</
span
>
</
div
>
<
div
className=
{
styles
[
'
complex-list__item--body
'
]
}
>
{
goodList
}
</
div
>
<
div
className=
{
styles
[
'
complex-list__item--footer
'
]
}
>
<
div
className=
{
styles
.
actions
}
>
<
a
onClick=
{
()
=>
viewDetail
(
message
)
}
className=
{
[
styles
.
notice
,
readStatus
===
0
?
styles
[
'
un-read
'
]
:
''
].
join
(
'
'
)
}
>
{
message
.
title
}
,请查看
</
a
>
</
div
>
</
div
>
</
div
>
);
};
const
Complex
=
props
=>
{
const
{
dispatch
,
refInstance
}
=
props
;
const
history
=
useHistory
();
const
[
visible
,
setVisible
]
=
useState
(
false
);
const
[
dataTotal
,
setDataTotal
]
=
useState
(
10
);
const
[
loading
,
setLoading
]
=
useState
(
false
);
// 消息列表数据
const
[
messageData
,
setMessageData
]
=
useState
([]);
// 未读消息
const
[
orderUnReadCount
,
setOrderUnReadCount
]
=
useState
(
0
);
const
[
afterUnReadCount
,
setAfterUnReadCount
]
=
useState
(
0
);
// 消息列表参数
const
[
queryParams
,
setQueryParams
]
=
useState
({
...
INIT_QUERY_PARAMS
,
...
INIT_PAGINATION
});
const
userInfo
=
JSON
.
parse
(
localStorage
.
getItem
(
'
user
'
)
||
'
{}
'
);
// 获取信息列表
const
getMsgList
=
async
()
=>
{
const
params
=
{
pageSize
:
queryParams
.
pageSize
,
pageNo
:
queryParams
.
pageNo
,
};
const
data
=
{
channelId
:
CHANNEL_ID
,
bussinessId
:
userInfo
.
supplierCode
,
...
queryParams
,
};
delete
data
.
pageSize
;
delete
data
.
pageNo
;
setLoading
(
true
);
const
res
=
await
apiGetBussinessMsgList
(
data
,
params
);
setLoading
(
false
);
if
(
res
.
code
!==
'
0000
'
)
{
notification
.
error
(
res
.
msg
);
return
;
}
const
{
content
,
totalElements
}
=
res
.
data
;
setMessageData
(
content
);
setDataTotal
(
totalElements
);
};
// 获取未读数量
const
getMsgReadCount
=
async
()
=>
{
const
data
=
{
channelId
:
CHANNEL_ID
,
bussinessId
:
userInfo
.
supplierCode
,
};
const
res
=
await
apiGetBusinessMsgUnReadCount
(
data
);
if
(
res
.
code
!==
'
0000
'
)
{
notification
.
error
(
res
.
msg
);
return
;
}
const
{
afterSalesUnRead
,
orderUnRead
}
=
res
.
data
;
setOrderUnReadCount
(
orderUnRead
);
setAfterUnReadCount
(
afterSalesUnRead
);
};
// 分页操作
const
onPageChange
=
(
page
,
size
)
=>
{
const
current
=
queryParams
.
pageSize
!==
size
?
1
:
page
;
setQueryParams
({
...
queryParams
,
pageNo
:
current
,
pageSize
:
size
,
});
};
// 筛选未读/已读
const
onReadStatusChange
=
e
=>
{
let
{
value
}
=
e
.
target
;
if
(
queryParams
.
readStatus
===
e
.
target
.
value
)
{
value
=
''
;
}
setQueryParams
({
...
queryParams
,
readStatus
:
value
,
});
};
// 过滤参数,获取当前页未读消息数据的id
const
onFilterMessageParams
=
msgList
=>
msgList
.
filter
(
message
=>
message
.
readStatus
===
0
).
map
(
item
=>
item
.
id
);
const
open
=
()
=>
{
setVisible
(
true
);
getMsgReadCount
();
};
const
close
=
()
=>
{
setMessageData
([]);
setVisible
(
false
);
};
//
/**
* 跳转到详情
* type
* 1-待发货订单
* 2-超时发货
* 3-仅退款(未发货)申请单
* 4-仅退款申请单
* 5-退货退款申请单
* 6-退货入库待审核
*/
const
viewDetail
=
message
=>
{
const
{
orderNo
,
type
}
=
message
;
if
([
1
,
2
].
includes
(
type
))
{
history
.
push
({
pathname
:
'
/orderManage/pendingDeliveryOrder
'
,
query
:
{
orderNo
},
});
}
if
([
3
,
4
,
5
,
6
].
includes
(
type
))
{
history
.
push
({
pathname
:
'
/afterSaleManage
'
,
query
:
{
orderNo
},
});
}
close
();
};
// 切换消息类型
const
onTabChange
=
index
=>
{
setQueryParams
({
...
queryParams
,
...
INIT_PAGINATION
,
type
:
index
,
});
};
// 标记已读
const
onMark
=
idsList
=>
{
if
(
!
idsList
.
length
)
{
return
;
}
const
payload
=
{
channelId
:
CHANNEL_ID
,
bussinessId
:
userInfo
.
supplierCode
,
type
:
Number
(
queryParams
.
type
),
idsList
,
};
dispatch
({
type
:
'
messageReminder/setMarkRead
'
,
payload
,
options
:
{
setLoading
,
callback
:
()
=>
{
getMsgReadCount
();
getMsgList
();
},
},
});
};
// 展开初始化
useEffect
(()
=>
{
if
(
visible
)
{
getMsgList
();
}
},
[
visible
,
queryParams
]);
useEffect
(()
=>
{
if
(
!
visible
)
{
setQueryParams
({
...
INIT_QUERY_PARAMS
,
...
INIT_PAGINATION
});
}
},
[
visible
]);
useImperativeHandle
(
refInstance
,
()
=>
({
open
,
}));
const
modalProps
=
{
bodyStyle
:
{
display
:
'
flex
'
,
flexWrap
:
'
wrap
'
,
padding
:
0
,
backgroundColor
:
'
#f7f8f9
'
,
},
wrapClassName
:
'
complex-modal
'
,
width
:
'
880px
'
,
height
:
'
650px
'
,
visible
,
title
:
<
span
className=
"title-icon"
>
消息提醒
</
span
>,
footer
:
null
,
onCancel
:
close
,
};
const
TabRender
=
tabProps
=>
{
const
{
title
,
count
=
0
}
=
tabProps
;
return
(
<
span
className=
{
styles
[
'
tab-render
'
]
}
>
{
title
}
<
Badge
overflowCount=
{
999
}
count=
{
count
}
/>
</
span
>
);
};
const
FilterRender
=
filterProps
=>
(
<
div
className=
{
styles
[
'
filter-box
'
]
}
>
<
div
className=
{
styles
[
'
filter-box__content
'
]
}
>
<
Checkbox
checked=
{
queryParams
.
readStatus
===
'
0
'
}
value=
"0"
onChange=
{
onReadStatusChange
}
>
未读
</
Checkbox
>
<
Checkbox
checked=
{
queryParams
.
readStatus
===
'
1
'
}
value=
"1"
onChange=
{
onReadStatusChange
}
>
已读
</
Checkbox
>
</
div
>
<
div
className=
{
styles
[
'
filter-box__actions
'
]
}
>
<
a
onClick=
{
()
=>
onMark
(
onFilterMessageParams
(
messageData
))
}
>
全部标记为已读
</
a
>
</
div
>
</
div
>
);
return
(
<
Modal
{
...
modalProps
}
>
<
Tabs
className=
{
styles
.
tabs
}
activeKey=
{
queryParams
.
type
}
tabBarStyle=
{
{
width
:
'
150px
'
,
}
}
tabPosition=
"left"
tabBarGutter=
{
0
}
onChange=
{
onTabChange
}
>
<
Tabs
.
TabPane
tab=
{
<
TabRender
title=
"订单消息"
count=
{
orderUnReadCount
}
/>
}
key=
"0"
/>
<
Tabs
.
TabPane
tab=
{
<
TabRender
title=
"售后消息"
count=
{
afterUnReadCount
}
/>
}
key=
"1"
/>
</
Tabs
>
<
div
className=
{
styles
[
'
tab-pane
'
]
}
>
<
Spin
spinning=
{
loading
}
>
<
FilterRender
/>
<
div
className=
{
styles
[
'
complex-list
'
]
}
>
{
visible
&&
messageData
.
length
?
(
messageData
.
map
((
item
,
index
)
=>
(
<
MessageItem
key=
{
String
(
index
)
}
item=
{
item
}
onMark=
{
onMark
}
viewDetail=
{
viewDetail
}
/>
))
)
:
(
<
Empty
text=
"暂无数据"
/>
)
}
</
div
>
</
Spin
>
{
dataTotal
>
0
?
(
<
div
className=
{
styles
.
pagination
}
>
<
Pagination
onChange=
{
onPageChange
}
total=
{
dataTotal
}
showTotal=
{
(
total
,
range
)
=>
`第${range[0]}-${range[1]}条 /总共${total}条`
}
showSizeChanger
pageSize=
{
queryParams
.
pageSize
}
current=
{
queryParams
.
pageNo
}
/>
</
div
>
)
:
(
''
)
}
</
div
>
</
Modal
>
);
};
const
MiddleComponent
=
connect
(({
messageReminder
})
=>
({
unReadCount
:
messageReminder
.
unReadCount
,
unReadData
:
messageReminder
.
unReadData
,
}))(
Complex
);
// 注意:这里不要在Component上使用ref;换个属性名字比如refInstance;不然会导致覆盖
export
default
forwardRef
((
props
,
ref
)
=>
<
MiddleComponent
{
...
props
}
refInstance=
{
ref
}
/>);
src/components/MessageReminder/Complex/index.less
0 → 100644
View file @
d8defddc
.tab-pane {
flex: 1;
}
.tabs {
:global .ant-tabs-tab {
height: 48px;
line-height: 48px;
border-bottom: 1px solid #efefef;
}
:global .ant-tabs-ink-bar {
height: 40px !important;
margin-top: 4px !important;
}
}
.tab-render {
display: flex;
align-items: center;
:global .ant-badge {
margin-left: 5px;
}
}
.filter-box {
display: flex;
margin-top: 15px;
padding-right: 25px;
&__content {
flex: 1;
}
}
:global .complex-modal {
.ant-modal-header {
text-align: center;
background-color: #2d8cf0;
}
.ant-modal-title,
.ant-modal-close {
color: #fff;
.title-icon {
padding-left: 25px;
background: #1890ff url('https://img.lkbang.net/notice.94d42513.png') no-repeat 0 center;
background-size: 20px 20px;
}
}
}
.complex-list {
min-height: 300px;
max-height: 500px;
margin-top: 20px;
padding-right: 25px;
overflow-y: auto;
& :first-child {
margin: 0;
}
&__item {
margin-top: 15px;
background: #fff;
border: 1px solid #efefef;
&--header {
display: flex;
width: 100%;
height: 40px;
padding: 0 15px;
line-height: 40px;
background: #fff;
border-bottom: 1px solid #efefef;
.order-number,
.time {
flex: 1;
padding-right: 20px;
color: #999;
word-break: break-all;
// white-space: nowrap
}
.read-status {
min-width: 80px;
text-align: right;
&--read {
color: #999;
}
}
}
&--body {
padding: 10px 15px;
.good {
display: flex;
padding: 5px 0;
line-height: 1.5;
&__name {
flex: 1;
word-break: break-all;
}
&__count {
min-width: 80px;
text-align: right;
}
}
}
&--footer {
display: flex;
border-top: 1px solid #efefef;
.actions {
display: flex;
width: 100%;
margin-left: 20px;
line-height: 40px;
.notice {
position: relative;
display: block;
flex: 1;
color: #999;
font-weight: 400;
&.un-read {
color: #ff1515;
&::before {
display: inline-block;
width: 6px;
height: 6px;
margin-top: 17px;
margin-right: 6px;
vertical-align: top;
background: #ff1515;
border-radius: 50%;
content: '';
}
}
&::after {
position: absolute;
top: 50%;
right: 10px;
display: block;
width: 0;
height: 0;
margin-top: -3px;
border: 6px solid transparent;
border-left: 8px solid #999;
content: '';
}
}
}
}
}
}
.pagination {
width: 100%;
padding: 25px;
text-align: right;
}
src/components/MessageReminder/Empty/index.jsx
0 → 100644
View file @
d8defddc
import
React
from
'
react
'
;
import
styles
from
'
./index.less
'
;
const
MessageReminderEmpty
=
props
=>
{
const
{
text
}
=
props
;
return
(
<
div
className=
{
styles
.
notFound
}
>
<
img
src=
"https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
alt=
"not found"
/>
<
div
>
{
text
}
</
div
>
</
div
>
);
};
export
default
MessageReminderEmpty
;
src/components/MessageReminder/Empty/index.less
0 → 100644
View file @
d8defddc
@import '~antd/es/style/themes/default.less';
.notFound {
padding: 73px 0 88px;
color: @text-color-secondary;
text-align: center;
background-color: #fff;
img {
display: inline-block;
height: 76px;
margin-bottom: 16px;
}
}
src/components/MessageReminder/Simple/index.jsx
0 → 100644
View file @
d8defddc
import
React
,
{
useState
,
useEffect
}
from
'
react
'
;
import
{
Badge
,
notification
,
Spin
}
from
'
antd
'
;
import
{
CloseOutlined
,
DoubleLeftOutlined
,
DoubleRightOutlined
}
from
'
@ant-design/icons
'
;
import
classNames
from
'
classnames
'
;
import
{
useHistory
}
from
'
react-router-dom
'
;
// import copy from 'copy-to-clipboard';
import
{
connect
}
from
'
dva
'
;
import
{
apiGetBussinessMsgList
}
from
'
@/services/messageReminder
'
;
import
Empty
from
'
../Empty
'
;
import
styles
from
'
./index.less
'
;
import
{
CHANNEL_ID
}
from
'
@/utils/constants
'
;
const
Horn
=
props
=>
{
const
{
count
,
toggle
,
animationClass
,
stowClass
,
onStow
}
=
props
;
return
(
<
div
className=
{
classNames
(
styles
.
horn
,
styles
[
animationClass
],
styles
[
stowClass
])
}
onClick=
{
toggle
}
>
<
span
className=
{
styles
[
'
horn--btn
'
]
}
onClick=
{
e
=>
onStow
(
e
)
}
>
{
stowClass
?
<
DoubleLeftOutlined
/>
:
<
DoubleRightOutlined
/>
}
</
span
>
<
Badge
count=
{
count
}
>
<
div
className=
{
styles
[
'
horn--num
'
]
}
>
消息提醒
</
div
>
</
Badge
>
</
div
>
);
};
const
Message
=
props
=>
{
const
{
toggle
,
animationClass
,
messageData
,
openComplex
,
onMark
,
loading
,
viewDetail
,
onStow
,
stowClass
,
}
=
props
;
const
ReminderItem
=
args
=>
{
const
{
item
}
=
args
;
let
message
=
{};
try
{
message
=
JSON
.
parse
(
item
.
sendContent
||
'
{}
'
);
}
catch
(
e
)
{
console
.
error
(
'
消息数据格式错误
'
);
}
return
(
<
div
className=
{
styles
.
item
}
>
<
div
className=
{
styles
.
info
}
>
<
span
className=
{
styles
[
'
order-number
'
]
}
>
{
message
.
orderNo
}
</
span
>
<
span
className=
{
styles
[
'
mark-read
'
]
}
onClick=
{
()
=>
onMark
([
item
.
id
])
}
>
标记为已读
</
span
>
</
div
>
<
div
className=
{
styles
.
time
}
>
<
span
>
{
message
.
time
}
</
span
>
</
div
>
<
div
className=
{
styles
.
notice
}
>
<
a
onClick=
{
()
=>
viewDetail
(
message
)
}
>
{
message
.
title
}
,请查看
</
a
>
</
div
>
</
div
>
);
};
return
(
<
div
className=
{
classNames
(
styles
[
'
message-reminder
'
],
styles
[
animationClass
])
}
>
<
div
className=
{
styles
[
'
message-reminder__header
'
]
}
>
<
div
className=
{
styles
[
'
message-reminder__header--title
'
]
}
>
消息提醒
<
span
className=
{
styles
.
close
}
onClick=
{
toggle
}
>
<
CloseOutlined
/>
</
span
>
</
div
>
</
div
>
<
div
className=
{
styles
[
'
message-reminder__body
'
]
}
>
<
Spin
spinning=
{
loading
}
>
<
div
className=
{
styles
[
'
message-reminder__body--list
'
]
}
>
{
messageData
.
length
?
(
messageData
.
filter
((
e
,
i
)
=>
i
<
10
)
.
map
(
item
=>
<
ReminderItem
key=
{
item
.
id
}
item=
{
item
}
/>)
)
:
(
<
Empty
text=
"暂无数据"
/>
)
}
</
div
>
</
Spin
>
</
div
>
<
div
className=
{
styles
[
'
message-reminder__footer
'
]
}
>
<
div
className=
{
styles
[
'
message-reminder__footer--actions
'
]
}
>
<
div
className=
{
styles
.
more
}
onClick=
{
openComplex
}
>
查看更多
</
div
>
<
div
className=
{
styles
[
'
mark-all
'
]
}
onClick=
{
()
=>
{
onMark
(
messageData
.
filter
((
e
,
i
)
=>
i
<
10
).
map
(
item
=>
item
.
id
));
}
}
>
全部标记为已读
</
div
>
</
div
>
</
div
>
</
div
>
);
};
const
Simple
=
props
=>
{
const
{
dispatch
,
unReadCount
,
complexRef
,
unReadData
}
=
props
;
const
history
=
useHistory
();
const
[
visible
,
setVisible
]
=
useState
(
false
);
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
count
,
setCount
]
=
useState
(
0
);
const
[
hornClass
,
setHornClass
]
=
useState
(
''
);
const
[
messageClass
,
setMessageClass
]
=
useState
(
''
);
const
[
stowClass
,
setStowClass
]
=
useState
(
''
);
const
[
messageData
,
setMessageData
]
=
useState
([]);
let
userInfo
=
JSON
.
parse
(
localStorage
.
getItem
(
'
user
'
)
||
'
{}
'
);
const
toggle
=
()
=>
{
setVisible
(
!
visible
);
};
// 初始化获取数据
const
getMsgList
=
async
()
=>
{
if
(
!
userInfo
.
supplierCode
)
{
userInfo
=
JSON
.
parse
(
localStorage
.
getItem
(
'
user
'
)
||
'
{}
'
);
setTimeout
(()
=>
{
getMsgList
();
},
1000
);
return
;
}
const
params
=
{
pageNo
:
1
,
pageSize
:
1000
,
};
const
data
=
{
channelId
:
CHANNEL_ID
,
bussinessId
:
userInfo
.
supplierCode
,
readStatus
:
0
,
};
setLoading
(
true
);
const
res
=
await
apiGetBussinessMsgList
(
data
,
params
);
setLoading
(
false
);
if
(
res
.
code
!==
'
0000
'
)
{
notification
.
error
(
res
.
msg
);
return
;
}
const
{
content
}
=
res
.
data
;
setVisible
(
!!
content
.
length
);
dispatch
({
type
:
'
messageReminder/setUnReadData
'
,
payload
:
content
,
options
:
{
unReadCount
:
content
.
length
,
},
});
};
const
viewDetail
=
message
=>
{
const
{
orderNo
,
type
}
=
message
;
console
.
log
(
type
);
if
([
1
,
2
].
includes
(
type
))
{
history
.
push
({
pathname
:
'
/orderManage/pendingDeliveryOrder
'
,
query
:
{
orderNo
},
});
}
if
([
3
,
4
,
5
,
6
].
includes
(
type
))
{
history
.
push
({
pathname
:
'
/afterSaleManage
'
,
query
:
{
orderNo
},
});
}
toggle
();
};
// 打开消息提醒弹框
const
openComplex
=
()
=>
{
complexRef
.
current
.
open
();
toggle
();
};
const
onStow
=
e
=>
{
e
.
preventDefault
();
e
.
stopPropagation
();
if
(
stowClass
)
{
setStowClass
(
''
);
}
else
{
setStowClass
(
'
stow
'
);
}
};
// 标记已读信息
const
onMark
=
idsList
=>
{
if
(
!
idsList
.
length
)
{
return
;
}
const
payload
=
{
channelId
:
CHANNEL_ID
,
bussinessId
:
userInfo
.
supplierCode
,
idsList
,
};
dispatch
({
type
:
'
messageReminder/setMarkRead
'
,
payload
,
options
:
{
setLoading
,
},
});
};
useEffect
(()
=>
{
setCount
(
unReadCount
);
},
[
unReadCount
]);
useEffect
(()
=>
{
setHornClass
(
visible
?
'
hide
'
:
'
show
'
);
setMessageClass
(
visible
?
'
show
'
:
'
hide
'
);
},
[
visible
]);
useEffect
(()
=>
{
getMsgList
();
},
[]);
useEffect
(()
=>
{
setMessageData
(
unReadData
);
},
[
unReadData
]);
const
hornProps
=
{
animationClass
:
hornClass
,
stowClass
,
toggle
,
count
,
onStow
,
};
const
messageProps
=
{
animationClass
:
messageClass
,
toggle
,
messageData
,
openComplex
,
onMark
,
loading
,
viewDetail
,
};
// 隐藏消息提醒
return
(
<>
<
Horn
{
...
hornProps
}
/>
<
Message
{
...
messageProps
}
/>
</>
);
};
export
default
connect
(({
messageReminder
})
=>
({
unReadCount
:
messageReminder
.
unReadCount
,
unReadData
:
messageReminder
.
unReadData
,
}))(
Simple
);
src/components/MessageReminder/Simple/index.less
0 → 100644
View file @
d8defddc
.horn {
position: fixed;
right: 30px;
bottom: 10px;
z-index: 19;
// width: 112px;
// height: 50px;
color: #fff;
background: #2d8cf0;
transform: translate(0, 0);
cursor: pointer;
opacity: 1;
transition: 0.2s ease-in;
&.hide {
transform: translateY(100%) scale(0.3);
opacity: 0;
transition: 0.2s ease-out;
}
&.stow {
transform: translateX(142px);
transition: 0.2s ease-out;
}
&--btn {
display: inline-block;
width: 20px;
height: 50px;
line-height: 50px;
text-align: center;
vertical-align: top;
background: #006ad9;
border-right: 1px solid #2479d3;
&:hover {
background: #2479d3;
transition: 0.2s ease-out;
}
}
&--num {
width: 112px;
height: 50px;
color: #fff;
font-size: 16px;
line-height: 50px;
text-align: center;
&::before {
display: inline-block;
width: 20px;
height: 20px;
margin-right: 5px;
vertical-align: -3px;
background: url('https://img.lkbang.net/notice.94d42513.png') no-repeat;
background-size: 100% 100%;
content: '';
}
}
}
.message-reminder {
position: fixed;
right: 10px;
bottom: 10px;
z-index: 19;
display: flex;
flex-direction: column;
width: 404px;
height: 412px;
background: #fff;
transform: translate(50%, 110%) scale(0.5);
transition: 0.2s ease-in;
&.hide {
transform: translate(50%, 110%) scale(0.5);
opacity: 0;
transition: 0.2s ease-out;
}
&.show {
transform: translateY(0) scale(1);
opacity: 1;
transition: 0.2s ease-in;
}
&__header {
&--title {
position: relative;
padding-left: 35px;
color: #fff;
font-weight: 700;
font-size: 16px;
line-height: 50px;
background: #1890ff url('https://img.lkbang.net/notice.94d42513.png') no-repeat 10px center;
background-size: 20px 20px;
.close {
position: absolute;
right: 20px;
font-weight: 400;
cursor: pointer;
}
}
}
&__body {
flex: 1;
padding: 15px;
overflow: auto;
background: #f8f8f9;
&--list {
& .item:first-child {
margin-top: 0;
}
.item {
margin-top: 15px;
padding: 15px 0;
background: #fff;
border: 1px solid #efefef;
&:hover {
// border-color: #ccc;
// transition: 0.2s linear;
}
}
.info {
display: flex;
padding: 0 15px;
.order-number {
flex: 1;
padding-left: 20px;
background: url('https://img.lkbang.net/order_no.f4e0a8b3.png') no-repeat scroll 0 center;
}
.mark-read {
color: #178fff;
cursor: pointer;
}
}
.time {
display: flex;
margin-top: 12px;
padding: 0 15px;
span {
flex: 1;
padding-left: 20px;
background: url('https://img.lkbang.net/datetime.1b5f9199.png') no-repeat scroll 0 center;
}
}
.notice {
margin-top: 12px;
padding: 10px 15px 0;
border-top: 1px solid #efefef;
a {
position: relative;
display: block;
color: #ff1515;
&::before {
display: inline-block;
width: 6px;
height: 6px;
margin-top: 8px;
margin-right: 6px;
vertical-align: top;
background: #ff1515;
border-radius: 50%;
content: '';
}
&::after {
position: absolute;
top: 50%;
right: 0;
display: block;
width: 0;
height: 0;
margin-top: -3px;
border: 6px solid transparent;
border-left: 8px solid #999;
content: '';
}
}
}
}
}
&__footer {
background-color: #fff;
border: 1px solid #efefef;
&--actions {
display: flex;
& div:first-child {
border: none;
}
> div {
flex: 1;
color: #2d8cf0;
line-height: 40px;
text-align: center;
border-left: 1px solid #efefef;
cursor: pointer;
// &:hover {
// background: #f9f9f9;
// cursor: pointer;
// }
}
}
}
}
src/components/MessageReminder/index.jsx
0 → 100644
View file @
d8defddc
import
Simple
from
'
./Simple
'
;
import
Complex
from
'
./Complex
'
;
const
MessageReminder
=
{};
MessageReminder
.
Simple
=
Simple
;
MessageReminder
.
Complex
=
Complex
;
export
default
MessageReminder
;
src/global.less
View file @
d8defddc
...
@@ -53,3 +53,19 @@ ol {
...
@@ -53,3 +53,19 @@ ol {
}
}
}
}
}
}
// Button
@btn-warning-color: #f50;
.ant-btn {
&-warning,
&-warning:active,
&-warning:hover,
&-warning:visited {
color: #fff !important;
background-color: @btn-warning-color !important;
border-color: @btn-warning-color !important;
&:hover {
opacity: 0.7;
}
}
}
src/layouts/BasicLayout.jsx
View file @
d8defddc
...
@@ -5,17 +5,22 @@
...
@@ -5,17 +5,22 @@
*/
*/
/* eslint-disable no-underscore-dangle, no-confusing-arrow */
/* eslint-disable no-underscore-dangle, no-confusing-arrow */
import
ProLayout
from
'
@ant-design/pro-layout
'
;
import
ProLayout
from
'
@ant-design/pro-layout
'
;
import
React
,
{
useState
,
useEffect
}
from
'
react
'
;
import
React
,
{
useState
,
useEffect
,
useRef
}
from
'
react
'
;
import
{
Link
}
from
'
umi
'
;
import
{
Link
}
from
'
umi
'
;
import
{
connect
}
from
'
dva
'
;
import
{
connect
}
from
'
dva
'
;
import
{
Icon
as
LegacyIcon
}
from
'
@ant-design/compatible
'
;
import
{
Icon
as
LegacyIcon
}
from
'
@ant-design/compatible
'
;
import
{
Result
,
Button
,
Layout
,
Menu
}
from
'
antd
'
;
import
{
Result
,
Button
,
Layout
,
Menu
}
from
'
antd
'
;
import
Authorized
from
'
@/utils/Authorized
'
;
import
Authorized
from
'
@/utils/Authorized
'
;
import
RightContent
from
'
@/components/GlobalHeader/RightContent
'
;
import
RightContent
from
'
@/components/GlobalHeader/RightContent
'
;
import
MessageReminder
from
'
@/components/MessageReminder
'
;
import
{
getAuthorityFromRouter
}
from
'
@/utils/utils
'
;
import
{
getAuthorityFromRouter
}
from
'
@/utils/utils
'
;
import
{
getSocketUrl
}
from
'
@/services/messageReminder
'
;
import
logo
from
'
../assets/logo.png
'
;
import
logo
from
'
../assets/logo.png
'
;
import
style
from
'
./BasicLayout.less
'
;
import
style
from
'
./BasicLayout.less
'
;
import
Socket
from
'
@/utils/websocket
'
;
import
{
CHANNEL_ID
}
from
'
@/utils/constants
'
;
const
{
Sider
}
=
Layout
;
const
{
Sider
}
=
Layout
;
const
{
SubMenu
}
=
Menu
;
const
{
SubMenu
}
=
Menu
;
...
@@ -48,12 +53,27 @@ const BasicLayout = props => {
...
@@ -48,12 +53,27 @@ const BasicLayout = props => {
}
=
props
;
}
=
props
;
const
[
siderCollapsed
,
setSiderCollapsed
]
=
useState
(
false
);
const
[
siderCollapsed
,
setSiderCollapsed
]
=
useState
(
false
);
const
messageReminderComplexRef
=
useRef
();
/**
// const audioRef = useRef()
* constructor
*/
useEffect
(()
=>
{
useEffect
(()
=>
{
try
{
const
token
=
window
.
localStorage
.
getItem
(
'
token
'
);
const
socket
=
new
Socket
({
url
:
getSocketUrl
({
token
,
channelId
:
CHANNEL_ID
}),
});
socket
.
connection
();
socket
.
event
.
on
(
'
message
'
,
msg
=>
{
dispatch
({
type
:
'
messageReminder/setUnReadData
'
,
payload
:
[
JSON
.
parse
(
msg
.
data
)],
});
});
}
catch
(
e
)
{
console
.
log
(
e
);
}
if
(
dispatch
)
{
if
(
dispatch
)
{
dispatch
({
dispatch
({
type
:
'
settings/getSetting
'
,
type
:
'
settings/getSetting
'
,
...
@@ -61,6 +81,9 @@ const BasicLayout = props => {
...
@@ -61,6 +81,9 @@ const BasicLayout = props => {
dispatch
({
dispatch
({
type
:
'
menu/getMenuData
'
,
type
:
'
menu/getMenuData
'
,
});
});
dispatch
({
type
:
'
messageReminder/getUnReadMsgList
'
,
});
}
}
},
[]);
},
[]);
/**
/**
...
@@ -156,7 +179,9 @@ const BasicLayout = props => {
...
@@ -156,7 +179,9 @@ const BasicLayout = props => {
);
);
}
}
}
}
menuDataRender=
{
()
=>
menuData
}
menuDataRender=
{
()
=>
menuData
}
rightContentRender=
{
rightProps
=>
<
RightContent
{
...
rightProps
}
/>
}
rightContentRender=
{
rightProps
=>
(
<
RightContent
{
...
rightProps
}
messageReminderComplexRef=
{
messageReminderComplexRef
}
/>
)
}
pageTitleRender=
{
()
=>
''
}
pageTitleRender=
{
()
=>
''
}
menuHeaderRender=
{
()
=>
(
menuHeaderRender=
{
()
=>
(
<
div
className=
{
style
[
'
custom-title-box
'
]
}
onClick=
{
toIndex
}
>
<
div
className=
{
style
[
'
custom-title-box
'
]
}
onClick=
{
toIndex
}
>
...
@@ -175,6 +200,10 @@ const BasicLayout = props => {
...
@@ -175,6 +200,10 @@ const BasicLayout = props => {
<
Authorized
authority=
{
authorized
.
authority
}
noMatch=
{
noMatch
}
>
<
Authorized
authority=
{
authorized
.
authority
}
noMatch=
{
noMatch
}
>
{
children
}
{
children
}
</
Authorized
>
</
Authorized
>
<
MessageReminder
.
Simple
complexRef=
{
messageReminderComplexRef
}
/>
<
MessageReminder
.
Complex
ref=
{
messageReminderComplexRef
}
/>
{
/* <Button ref={audioRef} onClick={() => { socket.play() }}>声音</Button> */
}
</
ProLayout
>
</
ProLayout
>
);
);
};
};
...
...
src/models/messageReminder.js
0 → 100644
View file @
d8defddc
import
{
apiUpdageBusinessMsgStatus
}
from
'
@/services/messageReminder
'
;
const
MessageReminderModel
=
{
namespace
:
'
messageReminder
'
,
state
:
{
unReadCount
:
0
,
// 未读总数
unReadData
:
[],
// 未读数据
},
effects
:
{
*
setUnReadData
({
payload
,
options
},
{
put
,
call
})
{
yield
setTimeout
(()
=>
{
put
({
type
:
'
setUnReadData
'
,
payload
,
options
,
});
});
},
*
setMarkRead
({
payload
,
options
=
{}
},
{
put
,
call
})
{
const
{
setLoading
}
=
options
;
if
(
setLoading
)
{
setLoading
(
true
);
}
const
res
=
yield
call
(
apiUpdageBusinessMsgStatus
,
payload
);
// console.log(res)
if
(
setLoading
)
{
setLoading
(
false
);
}
if
(
res
.
code
!==
'
0000
'
)
{
return
;
}
if
(
Object
.
hasOwnProperty
.
call
(
options
,
'
callback
'
)
&&
typeof
options
.
callback
===
'
function
'
)
{
options
.
callback
(
res
);
}
yield
put
({
type
:
'
updateUnReadData
'
,
payload
,
options
,
});
},
},
reducers
:
{
setUnReadData
(
state
,
{
payload
,
options
=
{}
})
{
const
currentData
=
[...
payload
,
...
state
.
unReadData
];
let
unReadCount
=
state
.
unReadCount
+
1
;
if
(
options
.
unReadCount
||
options
.
unReadCount
===
0
)
{
// eslint-disable-next-line prefer-destructuring
unReadCount
=
options
.
unReadCount
;
}
return
{
...
state
,
unReadData
:
currentData
,
unReadCount
};
},
updateUnReadData
(
state
,
{
payload
,
options
=
{}
})
{
// 删除已读的数据
const
currentData
=
state
.
unReadData
.
filter
(
message
=>
!
payload
.
idsList
.
includes
(
message
.
id
));
// 重置当前未读数量
const
unReadCount
=
currentData
.
length
;
// state.unReadCount - (options.readCount || payload.idsList.length || 1)
return
{
...
state
,
unReadCount
,
unReadData
:
currentData
};
},
},
};
export
default
MessageReminderModel
;
src/pages/Admin/index.jsx
View file @
d8defddc
...
@@ -36,7 +36,7 @@ const Admin = props => {
...
@@ -36,7 +36,7 @@ const Admin = props => {
</
div
>
</
div
>
<
div
style=
{
{
marginTop
:
'
20px
'
}
}
>
<
div
style=
{
{
marginTop
:
'
20px
'
}
}
>
<
Row
className=
{
style
.
orderContent
}
>
<
Row
className=
{
style
.
orderContent
}
>
{
showPendingDeliveryOrderList
||
show
CancelBill
List
?
(
{
showPendingDeliveryOrderList
||
show
AfterSale
List
?
(
<
Col
span=
{
2
}
className=
"titleOne"
>
<
Col
span=
{
2
}
className=
"titleOne"
>
<
div
>
<
div
>
<
FileTextOutlined
/>
<
FileTextOutlined
/>
...
@@ -74,11 +74,14 @@ const Admin = props => {
...
@@ -74,11 +74,14 @@ const Admin = props => {
)
:
(
)
:
(
''
''
)
}
)
}
{
show
CancelBill
List
?
(
{
show
AfterSale
List
?
(
<
Col
span=
{
4
}
>
<
Col
span=
{
4
}
>
<
Link
<
Link
to=
{
{
to=
{
{
pathname
:
'
cancelBillManage
'
,
pathname
:
'
afterSaleManage
'
,
query
:
{
tab
:
'
1
'
,
},
}
}
}
}
>
>
<
strong
>
{
pendingNum
?.
cancelOrderPendingAuditNum
||
0
}
</
strong
>
<
strong
>
{
pendingNum
?.
cancelOrderPendingAuditNum
||
0
}
</
strong
>
...
@@ -107,6 +110,16 @@ const Admin = props => {
...
@@ -107,6 +110,16 @@ const Admin = props => {
<
p
>
退款待审核
{
'
<
'
}
24小时
</
p
>
<
p
>
退款待审核
{
'
<
'
}
24小时
</
p
>
</
Link
>
</
Link
>
</
Col
>
</
Col
>
<
Col
span=
{
4
}
>
<
Link
to=
{
{
pathname
:
'
afterSaleManage
'
,
}
}
>
<
strong
>
{
pendingNum
?.
interceptRefundPendingAuditNum
||
0
}
</
strong
>
<
p
>
物流拦截待审核
{
'
<
'
}
48小时
</
p
>
</
Link
>
</
Col
>
</>
</>
)
:
(
)
:
(
''
''
...
...
src/pages/AfterSaleManage/components/AfterLog.jsx
View file @
d8defddc
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
/* eslint-disable guard-for-in */
/* eslint-disable guard-for-in */
import
React
,
{
useState
,
useEffect
}
from
'
react
'
;
import
React
,
{
useState
,
useEffect
}
from
'
react
'
;
import
{
Modal
,
Timeline
,
Button
}
from
'
antd
'
;
import
{
Modal
,
Timeline
,
Button
}
from
'
antd
'
;
import
styles
from
'
../
styles
.less
'
;
import
styles
from
'
../
index
.less
'
;
const
LogisticsCom
=
props
=>
{
const
LogisticsCom
=
props
=>
{
const
{
visible
,
onCancel
,
data
}
=
props
;
const
{
visible
,
onCancel
,
data
}
=
props
;
...
...
src/pages/AfterSaleManage/components/CancelAuditModal.jsx
0 → 100644
View file @
d8defddc
import
React
from
'
react
'
;
import
{
Form
}
from
'
@ant-design/compatible
'
;
import
'
@ant-design/compatible/assets/index.css
'
;
import
{
Modal
,
Input
,
Cascader
,
notification
}
from
'
antd
'
;
import
{
cancelShopAudit
}
from
'
../services
'
;
const
FormItem
=
Form
.
Item
;
const
{
TextArea
}
=
Input
;
const
AuditModal
=
props
=>
{
const
{
visible
,
onCancel
,
form
:
{
getFieldDecorator
,
getFieldValue
,
validateFields
,
resetFields
},
formData
=
{},
}
=
props
;
const
handleCancel
=
isSuccess
=>
{
resetFields
();
onCancel
(
isSuccess
);
};
const
treeData
=
[
{
label
:
'
同意
'
,
value
:
true
,
},
{
label
:
'
不同意
'
,
value
:
false
,
children
:
[
{
label
:
'
订单已发货
'
,
value
:
10
,
},
{
label
:
'
已与客户沟通继续发货
'
,
value
:
20
,
},
{
label
:
'
其他
'
,
value
:
30
,
},
],
},
];
const
handleOk
=
()
=>
{
validateFields
(
async
(
error
,
fieldsValue
)
=>
{
if
(
!
error
)
{
const
{
approve
}
=
fieldsValue
;
const
data
=
await
cancelShopAudit
({
...
fieldsValue
,
refuseReasonCode
:
approve
?.[
1
],
approve
:
approve
?.[
0
],
serviceNo
:
formData
?.
serviceNo
,
});
if
(
data
.
businessCode
===
'
0000
'
)
{
notification
.
success
({
message
:
approve
?.[
0
]
?
'
审核成功
'
:
'
审核拒绝提交成功
'
});
handleCancel
(
true
);
}
}
});
};
const
layout
=
{
labelCol
:
{
span
:
6
},
wrapperCol
:
{
span
:
16
},
};
const
approve
=
getFieldValue
(
'
approve
'
);
const
isRefuse
=
()
=>
approve
&&
approve
[
0
]
!==
1
;
return
(
<
Modal
title=
"售后操作确认"
visible=
{
visible
}
onOk=
{
()
=>
handleOk
()
}
onCancel=
{
()
=>
handleCancel
()
}
>
<
Form
{
...
layout
}
name=
"formData"
>
<
FormItem
label=
"审核结果"
>
{
getFieldDecorator
(
'
approve
'
)(
<
Cascader
allowClear
showSearch
style=
{
{
width
:
'
315px
'
}
}
dropdownStyle=
{
{
maxHeight
:
400
,
overflow
:
'
auto
'
}
}
options=
{
treeData
}
placeholder=
"请选择"
/>,
)
}
</
FormItem
>
{
isRefuse
()
&&
approve
[
1
]
===
30
&&
(
<
FormItem
label=
"拒绝原因"
>
{
getFieldDecorator
(
'
refuseReasonRemark
'
,
{
initialValue
:
formData
.
refuseDesc
,
rules
:
[
{
required
:
true
,
message
:
'
请填写拒绝原因!
'
,
},
],
})(
<
TextArea
placeholder=
"请填写拒绝原因"
allowClear
autoSize=
{
{
minRows
:
3
,
maxRows
:
6
}
}
/>,
)
}
</
FormItem
>
)
}
</
Form
>
</
Modal
>
);
};
export
default
Form
.
create
()(
AuditModal
);
src/pages/AfterSaleManage/components/CancelDetailTable.jsx
0 → 100644
View file @
d8defddc
import
React
from
'
react
'
;
import
{
Modal
,
Table
}
from
'
antd
'
;
export
default
props
=>
{
const
{
visible
,
onCancel
,
dataSource
}
=
props
;
const
handleCancel
=
()
=>
{
onCancel
();
};
const
columns
=
[
{
title
:
'
商品名称
'
,
width
:
400
,
dataIndex
:
'
skuName
'
,
},
{
title
:
'
商品属性
'
,
dataIndex
:
'
skuAttr
'
,
},
{
title
:
'
商品件数
'
,
dataIndex
:
'
count
'
,
},
];
return
(
<
Modal
title=
"订单详情"
visible=
{
visible
}
onCancel=
{
handleCancel
}
footer=
{
null
}
width=
{
800
}
>
<
Table
dataSource=
{
dataSource
}
columns=
{
columns
}
key=
{
r
=>
r
.
orderId
}
pagination=
{
false
}
bordered
/>
</
Modal
>
);
};
src/pages/AfterSaleManage/components/CancelRejectModal.jsx
0 → 100644
View file @
d8defddc
import
React
from
'
react
'
;
import
{
Form
}
from
'
@ant-design/compatible
'
;
import
'
@ant-design/compatible/assets/index.css
'
;
import
{
Modal
,
Input
,
notification
}
from
'
antd
'
;
import
{
shopCheck
}
from
'
../service
'
;
const
FormItem
=
Form
.
Item
;
const
{
TextArea
}
=
Input
;
const
RejectModal
=
props
=>
{
const
{
visible
,
onCancel
,
form
:
{
getFieldDecorator
,
validateFields
,
resetFields
},
serviceNo
=
null
,
}
=
props
;
const
handleCancel
=
isSuccess
=>
{
resetFields
();
onCancel
(
isSuccess
);
};
const
handleOk
=
()
=>
{
validateFields
(
async
(
error
,
fieldsValue
)
=>
{
if
(
!
error
)
{
const
data
=
await
shopCheck
({
...
fieldsValue
,
serviceNo
,
auditResult
:
2
,
});
if
(
data
.
code
===
'
0000
'
)
{
notification
.
success
({
message
:
'
操作成功
'
});
handleCancel
(
true
);
}
}
});
};
const
layout
=
{
labelCol
:
{
span
:
6
},
wrapperCol
:
{
span
:
16
},
};
return
(
<
Modal
title=
"驳回"
visible=
{
visible
}
onOk=
{
()
=>
handleOk
()
}
onCancel=
{
()
=>
handleCancel
()
}
>
<
Form
{
...
layout
}
name=
"formData"
>
<
FormItem
label=
"原因"
>
{
getFieldDecorator
(
'
refuseReasonRemark
'
,
{
rules
:
[
{
required
:
true
,
message
:
'
请填写原因!
'
,
},
],
})(
<
TextArea
placeholder=
"请填写原因"
allowClear
autoSize=
{
{
minRows
:
3
,
maxRows
:
6
}
}
/>,
)
}
</
FormItem
>
</
Form
>
</
Modal
>
);
};
export
default
Form
.
create
()(
RejectModal
);
src/pages/AfterSaleManage/components/LogisticsRecordModal.jsx
0 → 100644
View file @
d8defddc
import
React
,
{
useState
,
forwardRef
,
useImperativeHandle
,
useEffect
}
from
'
react
'
;
import
{
Modal
,
Timeline
,
notification
,
Spin
}
from
'
antd
'
;
import
{
getLogisticsRecord
}
from
'
../services
'
;
const
LogisticsRecordModal
=
(
props
,
ref
)
=>
{
// const { } = props;
const
[
visible
,
setVisible
]
=
useState
(
false
);
const
[
result
,
setResult
]
=
useState
({});
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
getRecordList
=
async
orderNo
=>
{
const
tempObj
=
{
detailList
:
[],
key
:
Date
.
now
(),
};
setLoading
(
true
);
const
res
=
await
getLogisticsRecord
({
orderNo
});
setLoading
(
false
);
if
(
!
res
)
{
notification
.
info
({
message
:
'
暂无物流信息
'
});
return
;
}
const
{
logisticsName
,
logisticsBillNo
,
logisticsList
=
[]
}
=
res
.
data
;
tempObj
.
expressCompanyName
=
logisticsName
;
tempObj
.
deliveryNo
=
logisticsBillNo
;
logisticsList
.
forEach
(
v
=>
{
tempObj
.
detailList
=
[...
tempObj
.
detailList
,
...
v
.
detailList
];
});
setResult
(
tempObj
);
};
const
open
=
orderNo
=>
{
setVisible
(
true
);
getRecordList
(
orderNo
);
};
const
onCancel
=
()
=>
{
setVisible
(
false
);
setResult
([]);
};
useImperativeHandle
(
ref
,
()
=>
({
open
,
}));
const
modalProps
=
{
title
:
'
查看物流
'
,
visible
,
onCancel
,
destroyOnClose
:
true
,
bodyStyle
:
{
maxHeight
:
'
600px
'
,
minHeight
:
'
200px
'
,
overflow
:
'
auto
'
},
footer
:
null
,
};
const
emptyStyle
=
{
textAlign
:
'
center
'
,
padding
:
'
30px 0
'
,
color
:
'
#999
'
,
};
return
(
<
Modal
{
...
modalProps
}
>
<
Spin
spinning=
{
loading
}
>
{
result
.
detailList
?.
length
?
(
<
Timeline
>
{
result
?.
detailList
?.
map
((
item
,
index
)
=>
(
<
Timeline
.
Item
color=
{
index
>
0
?
'
gray
'
:
'
blue
'
}
key=
{
index
.
toString
()
}
>
<
p
>
{
item
.
desc
}
</
p
>
<
p
>
{
item
.
logisticsTime
}
</
p
>
</
Timeline
.
Item
>
))
}
</
Timeline
>
)
:
(
<
div
style=
{
emptyStyle
}
>
暂无物流信息
</
div
>
)
}
</
Spin
>
</
Modal
>
);
};
export
default
forwardRef
(
LogisticsRecordModal
);
src/pages/AfterSaleManage/components/auditModal.jsx
View file @
d8defddc
import
React
from
'
react
'
;
import
React
,
{
useRef
}
from
'
react
'
;
import
{
Form
}
from
'
@ant-design/compatible
'
;
import
{
Form
}
from
'
@ant-design/compatible
'
;
import
'
@ant-design/compatible/assets/index.css
'
;
import
'
@ant-design/compatible/assets/index.css
'
;
import
{
Modal
,
Input
,
Cascader
,
notification
,
InputNumber
}
from
'
antd
'
;
import
{
Modal
,
Input
,
Cascader
,
notification
,
InputNumber
}
from
'
antd
'
;
import
{
shopAudit
}
from
'
../services
'
;
import
{
shopAudit
}
from
'
../services
'
;
import
styles
from
'
../styles.less
'
;
import
styles
from
'
../index.less
'
;
import
LogisticsRecordModal
from
'
./LogisticsRecordModal
'
;
const
FormItem
=
Form
.
Item
;
const
FormItem
=
Form
.
Item
;
const
{
TextArea
}
=
Input
;
const
{
TextArea
}
=
Input
;
...
@@ -15,7 +17,7 @@ const AuditModal = props => {
...
@@ -15,7 +17,7 @@ const AuditModal = props => {
form
:
{
getFieldDecorator
,
getFieldValue
,
validateFields
,
resetFields
},
form
:
{
getFieldDecorator
,
getFieldValue
,
validateFields
,
resetFields
},
formData
=
{},
formData
=
{},
}
=
props
;
}
=
props
;
const
logisticsRecordModalRef
=
useRef
();
const
handleCancel
=
isSuccess
=>
{
const
handleCancel
=
isSuccess
=>
{
resetFields
();
resetFields
();
onCancel
(
isSuccess
);
onCancel
(
isSuccess
);
...
@@ -102,6 +104,10 @@ const AuditModal = props => {
...
@@ -102,6 +104,10 @@ const AuditModal = props => {
});
});
};
};
const
openLogisticsRecord
=
()
=>
{
logisticsRecordModalRef
.
current
.
open
(
formData
.
orderNo
);
};
const
layout
=
{
const
layout
=
{
labelCol
:
{
span
:
6
},
labelCol
:
{
span
:
6
},
wrapperCol
:
{
span
:
16
},
wrapperCol
:
{
span
:
16
},
...
@@ -110,6 +116,7 @@ const AuditModal = props => {
...
@@ -110,6 +116,7 @@ const AuditModal = props => {
const
isAgree
=
()
=>
auditResult
?.[
0
]
===
1
;
const
isAgree
=
()
=>
auditResult
?.[
0
]
===
1
;
const
isRefuse
=
()
=>
auditResult
&&
auditResult
[
0
]
!==
1
;
const
isRefuse
=
()
=>
auditResult
&&
auditResult
[
0
]
!==
1
;
return
(
return
(
<>
<
Modal
<
Modal
title=
"售后操作确认"
title=
"售后操作确认"
visible=
{
visible
}
visible=
{
visible
}
...
@@ -120,9 +127,10 @@ const AuditModal = props => {
...
@@ -120,9 +127,10 @@ const AuditModal = props => {
<
div
className=
{
styles
.
redTip
}
>
<
div
className=
{
styles
.
redTip
}
>
温馨提示:当前售后类型为用户未收到产品,申请
温馨提示:当前售后类型为用户未收到产品,申请
<
span
className=
{
styles
.
redTipBold
}
>
仅退款
</
span
>
<
span
className=
{
styles
.
redTipBold
}
>
仅退款
</
span
>
,请务必检查此单物流状态后审核。
,请务必检查此单物流状态后审核。
<
a
onClick=
{
openLogisticsRecord
}
>
查看物流
</
a
>
</
div
>
</
div
>
)
}
)
}
<
Form
{
...
layout
}
name=
"formData"
>
<
Form
{
...
layout
}
name=
"formData"
>
<
FormItem
label=
"审核结果"
>
<
FormItem
label=
"审核结果"
>
{
getFieldDecorator
(
'
auditResult
'
,
{
{
getFieldDecorator
(
'
auditResult
'
,
{
...
@@ -236,7 +244,14 @@ const AuditModal = props => {
...
@@ -236,7 +244,14 @@ const AuditModal = props => {
</
FormItem
>
</
FormItem
>
)
}
)
}
</
Form
>
</
Form
>
{
+
formData
.
serviceType
===
2
&&
(
<
div
align=
"right"
style=
{
{
paddingRight
:
'
42px
'
,
marginTop
:
'
-25px
'
}
}
>
<
a
onClick=
{
openLogisticsRecord
}
>
查看物流
</
a
>
</
div
>
)
}
</
Modal
>
</
Modal
>
<
LogisticsRecordModal
ref=
{
logisticsRecordModalRef
}
/>
</>
);
);
};
};
...
...
src/pages/AfterSaleManage/components/proofsModal.jsx
View file @
d8defddc
import
React
,
{
useState
}
from
'
react
'
;
import
React
,
{
useState
}
from
'
react
'
;
import
{
Modal
}
from
'
antd
'
;
import
{
Modal
}
from
'
antd
'
;
import
style
from
'
../
styles
.less
'
;
import
style
from
'
../
index
.less
'
;
export
default
props
=>
{
export
default
props
=>
{
const
{
visible
,
onCancel
,
data
}
=
props
;
const
{
visible
,
onCancel
,
data
}
=
props
;
...
...
src/pages/AfterSaleManage/data.js
View file @
d8defddc
import
{
Tag
,
Badge
}
from
'
antd
'
;
import
{
Tag
,
Badge
,
Statistic
,
Button
,
Popconfirm
}
from
'
antd
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
moment
from
'
moment
'
;
import
{
SEARCH_TYPE
}
from
'
@/components/FormSearch
'
;
const
{
Countdown
}
=
Statistic
;
export
const
TAB_MAPPING_DATA
=
{
''
:
{},
1
:
{
label
:
'
仅退款(未发货)待审核
'
,
type
:
0
,
dealStatus
:
14
,
},
2
:
{
label
:
'
仅退款待审核
'
,
type
:
1
,
dealStatus
:
14
,
},
3
:
{
label
:
'
退货退款待审核
'
,
type
:
2
,
dealStatus
:
14
,
},
4
:
{
label
:
'
退货入库待审核
'
,
dealStatus
:
40
,
},
5
:
{
label
:
'
已完成
'
,
dealStatus
:
70
,
},
};
// 售后类型
const
AFTER_TYPE
=
[
{
value
:
0
,
name
:
'
仅退款(未发货)
'
,
},
{
value
:
1
,
name
:
'
仅退款
'
,
},
{
value
:
2
,
name
:
'
退货退款
'
,
},
];
// 售后状态
const
AFTER_STATUS
=
[
{
value
:
14
,
name
:
'
商户审核中
'
,
},
{
value
:
16
,
name
:
'
商户审核拒绝
'
,
},
{
value
:
30
,
name
:
'
待填写退货物流信息
'
,
},
{
value
:
40
,
name
:
'
待退货入库
'
,
},
{
value
:
50
,
name
:
'
退货拒收
'
,
},
{
value
:
70
,
name
:
'
退款成功
'
,
},
{
value
:
99
,
name
:
'
用户撤销
'
,
},
];
export
const
getFormConfig
=
(
props
=
{})
=>
{
const
{
setTableParams
,
setCurrentTab
,
tableParams
,
actionRef
}
=
props
;
return
{
formConfig
:
[
{
type
:
SEARCH_TYPE
.
INPUT
,
label
:
'
订单ID
'
,
bindKey
:
'
orderNo
'
,
column
:
5
,
},
{
type
:
SEARCH_TYPE
.
INPUT
,
label
:
'
售后单ID
'
,
bindKey
:
'
serviceNo
'
,
column
:
5
,
},
{
type
:
SEARCH_TYPE
.
SELECT
,
label
:
'
售后状态
'
,
column
:
5
,
bindKey
:
'
dealStatus
'
,
options
:
AFTER_STATUS
,
originOptions
:
{
allowClear
:
true
,
},
},
{
type
:
SEARCH_TYPE
.
SELECT
,
label
:
'
售后类型
'
,
column
:
5
,
bindKey
:
'
type
'
,
options
:
AFTER_TYPE
,
originOptions
:
{
allowClear
:
true
,
},
},
{
type
:
SEARCH_TYPE
.
INPUT
,
label
:
'
收货人姓名
'
,
column
:
5
,
bindKey
:
'
receiverName
'
,
},
{
type
:
SEARCH_TYPE
.
INPUT
,
label
:
'
收货人手机号
'
,
column
:
5
,
bindKey
:
'
receiverPhone
'
,
},
{
type
:
SEARCH_TYPE
.
RANGE_PICKER
,
label
:
'
售后日期
'
,
column
:
5
,
bindKey
:
'
afterTime
'
,
limit
:
{
rangeNum
:
3
,
rangeUnit
:
'
months
'
,
},
originOptions
:
{
placeholder
:
[
'
开始日期
'
,
'
结束日期
'
],
allowClear
:
false
,
},
},
],
btnConfig
:
[
{
label
:
'
筛选
'
,
clickType
:
'
submit
'
,
onClick
:
({
params
})
=>
{
// 参数相同,直接执行刷新
if
(
JSON
.
stringify
(
params
)
===
JSON
.
stringify
(
tableParams
))
{
actionRef
.
current
.
reload
();
return
;
}
const
{
type
,
dealStatus
}
=
params
;
let
hasMatchingKey
=
''
;
Object
.
keys
(
TAB_MAPPING_DATA
).
forEach
(
key
=>
{
const
item
=
TAB_MAPPING_DATA
[
key
];
if
(
type
===
item
.
type
&&
dealStatus
===
item
.
dealStatus
)
{
hasMatchingKey
=
key
;
}
});
setCurrentTab
(
hasMatchingKey
);
setTableParams
(
params
);
},
},
{
label
:
'
重置
'
,
type
:
''
,
clickType
:
'
reset
'
,
onClick
:
({
params
})
=>
{
setTableParams
({});
actionRef
.
current
.
reload
();
setCurrentTab
(
''
);
},
},
],
};
};
export
const
appealType
=
{
export
const
appealType
=
{
1
:
'
已申诉
'
,
1
:
'
已申诉
'
,
0
:
'
未申诉
'
,
0
:
'
未申诉
'
,
};
};
export
const
columnSticData
=
[
export
const
getColumns
=
props
=>
{
const
{
openAudit
,
// viewAppeal,
viewProofs
,
openLogistics
,
viewDetail
,
viewLog
,
handleCom
,
refund
,
reject
,
canEditable
,
openCancelAudit
,
viewCancelDetail
,
tableParams
,
}
=
props
;
const
{
dealStatus
}
=
tableParams
;
let
auditCountDown
=
[
{
title
:
'
审核倒计时
'
,
dataIndex
:
'
serviceTime
'
,
key
:
'
serviceTime
'
,
hideInSearch
:
true
,
width
:
150
,
render
:
(
val
,
record
)
=>
{
if
(
record
.
status
!==
14
)
{
return
<
div
align
=
"
center
"
>-<
/div>
;
}
const
serviceTime
=
moment
(
record
.
approvalEndTime
).
valueOf
();
return
(
<
Countdown
format
=
"
HH时mm分ss秒
"
value
=
{
serviceTime
}
valueStyle
=
{{
color
:
'
red
'
,
fontSize
:
'
14px
'
}}
/
>
);
},
},
];
// 售后状态:已完成/退款成功不展示审核倒计时
if
(
dealStatus
===
70
)
{
auditCountDown
=
[];
}
return
[
...
auditCountDown
,
{
title
:
'
售后状态
'
,
dataIndex
:
'
serviceStatus
'
,
hideInSearch
:
true
,
width
:
120
,
},
{
{
title
:
'
售后类型
'
,
title
:
'
售后类型
'
,
dataIndex
:
'
serviceType
'
,
dataIndex
:
'
serviceType
'
,
hideInSearch
:
true
,
hideInSearch
:
true
,
width
:
12
0
,
width
:
14
0
,
render
:
serviceType
=>
{
render
:
serviceType
=>
{
if
(
+
serviceType
===
0
)
{
return
<
span
style
=
{{
color
:
'
#ff1616
'
}}
>
仅退款
(
未发货
)
<
/span>
;
}
if
(
+
serviceType
===
1
)
{
if
(
+
serviceType
===
1
)
{
return
<
span
style
=
{{
color
:
'
#ff1616
'
}}
>
仅退款
<
/span>
;
return
(
<
span
style
=
{{
color
:
'
#ff1616
'
}}
>
{
/* {AFTER_TYPE.find(item=> item.value === serviceType)} */
}
仅退款
<
/span
>
);
}
}
return
<
span
>
退货退款
<
/span>
;
return
<
span
>
退货退款
<
/span>
;
},
},
},
},
{
title
:
'
订单ID
'
,
dataIndex
:
'
orderNo
'
,
hideInTable
:
true
,
width
:
200
,
},
{
{
title
:
'
售后单ID
'
,
title
:
'
售后单ID
'
,
dataIndex
:
'
serviceNo
'
,
dataIndex
:
'
serviceNo
'
,
...
@@ -42,56 +278,19 @@ export const columnSticData = [
...
@@ -42,56 +278,19 @@ export const columnSticData = [
width
:
300
,
width
:
300
,
},
},
{
{
title
:
'
售后状态
'
,
title
:
'
收货人信息
'
,
dataIndex
:
'
dealStatus
'
,
hideInTable
:
true
,
valueEnum
:
{
0
:
'
待审核
'
,
10
:
'
三方审核中
'
,
11
:
'
三方审核通过
'
,
12
:
'
三方审核拒绝
'
,
13
:
'
客服审核通过
'
,
14
:
'
商户审核中
'
,
15
:
'
商户审核通过
'
,
16
:
'
商户审核拒绝
'
,
20
:
'
审核拒绝
'
,
21
:
'
申诉中
'
,
30
:
'
待填写退货物流信息
'
,
40
:
'
待退货入库
'
,
50
:
'
退货拒收
'
,
60
:
'
待退款
'
,
61
:
'
退货处理中
'
,
70
:
'
售后成功
'
,
99
:
'
用户取消
'
,
},
width
:
100
,
},
{
title
:
'
售后类型
'
,
dataIndex
:
'
type
'
,
hideInTable
:
true
,
width
:
120
,
valueEnum
:
{
1
:
'
仅退款
'
,
2
:
'
退货退款
'
,
},
},
{
title
:
'
收货人姓名
'
,
dataIndex
:
'
receiverName
'
,
dataIndex
:
'
receiverName
'
,
width
:
200
,
width
:
200
,
render
:
(
_
,
record
)
=>
{
const
{
receiverPhone
,
receiveAddress
}
=
record
;
return
(
<>
<
p
>
{
_
}
<
/p
>
<
p
>
{
receiverPhone
}
<
/p
>
<
p
>
{
receiveAddress
}
<
/p
>
<
/
>
);
},
},
{
title
:
'
收货人手机号
'
,
dataIndex
:
'
receiverPhone
'
,
width
:
200
,
},
{
title
:
'
收货人地址
'
,
dataIndex
:
'
receiveAddress
'
,
width
:
200
,
hideInSearch
:
true
,
},
},
{
{
title
:
'
订单开始时间
'
,
title
:
'
订单开始时间
'
,
...
@@ -127,16 +326,16 @@ export const columnSticData = [
...
@@ -127,16 +326,16 @@ export const columnSticData = [
hideInSearch
:
true
,
hideInSearch
:
true
,
width
:
200
,
width
:
200
,
},
},
{
//
{
title
:
'
是否催办
'
,
//
title: '是否催办',
dataIndex
:
'
reminderFlag
'
,
//
dataIndex: 'reminderFlag',
hideInSearch
:
true
,
//
hideInSearch: true,
width
:
120
,
//
width: 120,
valueEnum
:
{
//
valueEnum: {
true
:
'
是
'
,
//
true: '是',
false
:
'
否
'
,
//
false: '否',
},
//
},
},
//
},
{
{
title
:
'
是否同意售后
'
,
title
:
'
是否同意售后
'
,
dataIndex
:
'
isAgree
'
,
dataIndex
:
'
isAgree
'
,
...
@@ -149,31 +348,213 @@ export const columnSticData = [
...
@@ -149,31 +348,213 @@ export const columnSticData = [
hideInSearch
:
true
,
hideInSearch
:
true
,
width
:
200
,
width
:
200
,
},
},
];
export
const
columnPassAudit
=
[
...
columnSticData
,
{
{
title
:
'
商家退货地址
'
,
title
:
'
商家退货信息
'
,
dataIndex
:
'
merchantAddress
'
,
dataIndex
:
'
merchantAddress
'
,
hideInSearch
:
true
,
hideInSearch
:
true
,
width
:
200
,
width
:
200
,
render
:
(
_
,
record
)
=>
{
const
{
expressCompanyName
,
deliveryNo
}
=
record
;
return
(
<>
<
p
>
{
_
}
<
/p
>
<
p
>
{
expressCompanyName
}
<
/p
>
<
p
>
{
deliveryNo
}
<
/p
>
<
/
>
);
},
},
{
title
:
'
退回物流
'
,
dataIndex
:
'
expressCompanyName
'
,
hideInSearch
:
true
,
width
:
150
,
},
},
{
{
title
:
'
退回物流单号
'
,
title
:
'
售后凭证
'
,
dataIndex
:
'
deliveryNo
'
,
dataIndex
:
'
proofs
'
,
hideInSearch
:
true
,
hideInSearch
:
true
,
width
:
200
,
width
:
100
,
render
:
(
val
,
r
)
=>
<
a
onClick
=
{()
=>
viewProofs
(
r
.
proofs
)}
>
查看凭证
<
/a>
,
},
},
// {
// title: '售后申诉',
// dataIndex: 'appealFlag',
// valueEnum: appealType,
// hideInSearch: true,
// width: 120,
// render: (appealFlag, r) => {
// if (appealFlag) {
// return <a onClick={() => viewAppeal(r)}>已申诉</a>;
// }
// return <div>未申诉</div>;
// },
// },
{
{
title
:
'
售后状态
'
,
title
:
'
操作
'
,
dataIndex
:
'
serviceStatus
'
,
hideInSearch
:
true
,
hideInSearch
:
true
,
width
:
120
,
dataIndex
:
'
action
'
,
width
:
250
,
fixed
:
'
right
'
,
render
:
(
val
,
record
)
=>
{
const
{
status
,
supplierType
,
serviceType
,
intercept
,
showRefuse
,
showRefund
,
showLogistics
,
showRefunded
,
isReceived
,
}
=
record
;
// 是否是服务类商品
const
isServiceGoods
=
[
'
pop
'
].
includes
(
supplierType
);
// 按钮通用属性
const
btnProps
=
{
className
:
'
mr10 mt10
'
,
type
:
'
primary
'
,
};
const
refundBtnProps
=
{
title
:
'
确定允许退款?
'
,
okText
:
'
确认
'
,
cancelText
:
'
取消
'
,
key
:
'
pop
'
,
};
/** @name applyTpye=1 */
// 允许退款/已退款按钮
const
refundBtn
=
(
<
Popconfirm
{...
refundBtnProps
}
onConfirm
=
{()
=>
refund
(
record
)}
disabled
=
{
!
showRefund
}
>
<
Button
key
=
"
link1
"
{...
btnProps
}
disabled
=
{
!
showRefund
}
>
{
showRefunded
?
'
已退款
'
:
'
允许退款
'
}
<
/Button
>
<
/Popconfirm
>
);
// 审核按钮
let
auditBtn
=
(
<
Button
key
=
"
link2
"
onClick
=
{()
=>
openAudit
(
record
)}
{...
btnProps
}
>
审核
<
/Button
>
);
// 驳回按钮
let
refuseBtn
=
(
<
Button
key
=
"
link3
"
onClick
=
{()
=>
reject
(
record
)}
{...
btnProps
}
disabled
=
{
!
showRefuse
}
>
驳回
<
/Button
>
);
// 物流拦截按钮
let
logisticsInterceptionBtn
=
(
<
Button
key
=
"
link7
"
onClick
=
{()
=>
openLogistics
(
record
)}
{...
btnProps
}
disabled
=
{
serviceType
!==
1
||
(
serviceType
===
1
&&
intercept
)}
>
物流拦截
<
/Button
>
);
// 订单详情
const
detailBtn
=
(
<
Button
key
=
"
link4
"
onClick
=
{()
=>
viewDetail
(
record
)}
{...
btnProps
}
>
订单详情
<
/Button
>
);
// 查看物流
const
viewLogisticsBtn
=
(
<
Button
key
=
"
link5
"
onClick
=
{()
=>
handleCom
(
record
)}
{...
btnProps
}
disabled
=
{
!
showLogistics
}
>
退货物流
<
/Button
>
);
// 查看记录
const
viewLogBtn
=
(
<
Button
key
=
"
link6
"
onClick
=
{()
=>
viewLog
(
record
)}
{...
btnProps
}
>
查看记录
<
/Button
>
);
let
cancelAuditBtn
=
(
<
Button
key
=
"
cancellink1
"
onClick
=
{()
=>
openCancelAudit
(
record
)}
{...
btnProps
}
>
审核
<
/Button
>
);
const
cancelDetailBtn
=
(
<
Button
key
=
"
cancellink2
"
onClick
=
{()
=>
viewCancelDetail
(
record
)}
{...
btnProps
}
>
订单详情
<
/Button
>
);
// 仅退款状态 && 用户已收到货 不展示物流拦截按钮
if
(
serviceType
===
1
&&
isReceived
===
1
)
{
logisticsInterceptionBtn
=
''
;
}
// 无权限 不展示审核、驳回、物流拦截
if
(
!
canEditable
)
{
auditBtn
=
''
;
refuseBtn
=
''
;
logisticsInterceptionBtn
=
''
;
cancelAuditBtn
=
''
;
}
// 服务商品
if
(
isServiceGoods
)
{
// 待商户审核14
if
([
14
].
includes
(
status
))
{
return
[
auditBtn
,
detailBtn
,
viewLogBtn
];
}
// 拒绝16, 处理成功70
if
([
16
,
70
].
includes
(
status
))
{
return
[
detailBtn
,
viewLogBtn
];
}
}
// 实物商品-仅退款未发货 serviceType = 0
if
(
serviceType
===
0
)
{
// 待商户审核14 待审核0
if
([
14
,
0
].
includes
(
status
))
{
return
[
cancelAuditBtn
,
cancelDetailBtn
,
viewLogBtn
];
}
// 拒绝16, 处理成功70
if
([
16
,
70
].
includes
(
status
))
{
return
[
cancelDetailBtn
,
viewLogBtn
];
}
}
// 实物商品-仅退款 serviceType = 1
if
(
serviceType
===
1
)
{
// 待商户审核14
if
([
14
].
includes
(
status
))
{
return
[
auditBtn
,
detailBtn
,
viewLogBtn
,
logisticsInterceptionBtn
];
}
// 拒绝16/处理成功70
if
([
16
,
70
].
includes
(
status
))
{
return
[
detailBtn
,
viewLogBtn
];
}
}
// 实物商品-退货退款 serviceType = 2
if
(
serviceType
===
2
)
{
// 待商户审核14
if
([
14
].
includes
(
status
))
{
return
[
auditBtn
,
detailBtn
,
viewLogBtn
];
}
// 待退货入库40
if
([
40
].
includes
(
status
))
{
return
[
refundBtn
,
refuseBtn
,
viewLogisticsBtn
,
detailBtn
,
viewLogBtn
];
}
// 拒绝16/待填写退货物流信息30/处理成功70
if
([
16
,
30
,
70
].
includes
(
status
))
{
return
[
viewLogisticsBtn
,
detailBtn
,
viewLogBtn
];
}
}
return
[
detailBtn
,
viewLogBtn
];
},
},
];
},
];
};
src/pages/AfterSaleManage/index.jsx
View file @
d8defddc
import
{
Tabs
}
from
'
antd
'
;
import
React
,
{
useRef
,
useState
,
useEffect
}
from
'
react
'
;
import
React
from
'
react
'
;
import
{
Tabs
,
notification
,
Form
,
Modal
}
from
'
antd
'
;
import
{
PageHeaderWrapper
}
from
'
@ant-design/pro-layout
'
;
import
{
PageHeaderWrapper
}
from
'
@ant-design/pro-layout
'
;
import
ProTable
from
'
@ant-design/pro-table
'
;
import
{
AFTER_SALE_ORDER
}
from
'
@/../config/permission.config
'
;
import
{
AFTER_SALE_ORDER
}
from
'
@/../config/permission.config
'
;
import
{
connect
}
from
'
dva
'
;
import
{
connect
}
from
'
dva
'
;
import
Pending
from
'
./Pending
'
;
import
{
FormSearch
}
from
'
@/components/FormSearch
'
;
import
PassAudit
from
'
./PassAudit
'
;
import
moment
from
'
moment
'
;
import
{
useLocation
}
from
'
react-router-dom
'
;
import
AuditModal
from
'
./components/auditModal
'
;
import
DetailTable
from
'
./components/detailTable
'
;
import
ProofsModal
from
'
./components/proofsModal
'
;
// import AppealDetail from '@/pages/afterSale/components/detail';
import
AfterLog
from
'
./components/AfterLog
'
;
import
RejectModal
from
'
./components/rejectModal
'
;
import
LogisticsCom
from
'
../orderManage/pendingDeliveryOrder/components/LogisticsCom
'
;
import
CancelAuditModal
from
'
./components/CancelAuditModal
'
;
import
CancelDetailTable
from
'
./components/CancelDetailTable
'
;
import
{
getColumns
,
getFormConfig
,
TAB_MAPPING_DATA
}
from
'
./data.js
'
;
import
{
getDetail
}
from
'
@/pages/afterSale/appeal/services
'
;
import
{
getClientInfo
}
from
'
@/utils/utils
'
;
import
{
searchList
,
logisticsIntercept
,
orderDetail
,
getOpLog
,
auditInfoApi
,
trackInfo
,
shopCheck
,
getAfterPendingNum
,
cancelOrderDetail
,
}
from
'
./services
'
;
import
styles
from
'
./index.less
'
;
const
{
TabPane
}
=
Tabs
;
const
{
TabPane
}
=
Tabs
;
const
{
confirm
}
=
Modal
;
function
AfterSale
(
props
)
{
const
AfterSale
=
props
=>
{
const
{
permissions
}
=
props
;
const
{
permissions
}
=
props
;
const
canEditable
=
permissions
[
AFTER_SALE_ORDER
.
EDITABLE
];
const
canEditable
=
permissions
[
AFTER_SALE_ORDER
.
EDITABLE
];
const
location
=
useLocation
();
const
actionRef
=
useRef
();
const
formRef
=
useRef
();
const
[
tableParams
,
setTableParams
]
=
useState
({});
const
[
currentTab
,
setCurrentTab
]
=
useState
(
''
);
// const [appealDetailModal, setAppealDetailModal] = useState(false);
// const [selectedRow, setSelectedRow] = useState({});
const
[
tabInfoData
,
setTabInfoData
]
=
useState
({
cancelOrderPendingAuditNum
:
0
,
refundPendingAuditNum
:
0
,
returnPendingAuditNum
:
0
,
returnWaitAuditNum
:
0
,
afterSalseDoneNum
:
0
,
});
const
scrollYRef
=
useRef
();
const
[
tableScrollY
,
setTableScrollY
]
=
useState
(
0
);
// 申诉
const
[
proofsData
,
setProofsData
]
=
useState
([]);
const
[
proofsVisible
,
setProofsVisible
]
=
useState
(
false
);
// 详情
const
[
detailVisible
,
setDetailVisible
]
=
useState
(
false
);
const
[
detailInfo
,
setDetailInfo
]
=
useState
([]);
// 售后操作记录
const
[
afterVisible
,
setAfterVisible
]
=
useState
(
false
);
const
[
afterList
,
setAfterList
]
=
useState
([]);
// 审核
const
[
visible
,
setVisible
]
=
useState
(
false
);
const
[
auditInfo
,
setAuditInfo
]
=
useState
({});
// 查看物流
const
[
LogisticsComList
,
setLogisticsComList
]
=
useState
({});
const
[
logisticsComModalVisible
,
setLogisticsComModalVisible
]
=
useState
(
false
);
// 驳回
const
[
serviceNoInfo
,
setServiceNoInfo
]
=
useState
({});
const
[
rejectVisible
,
setRejectVisible
]
=
useState
(
false
);
// 取消订单审核
const
[
cancelVisible
,
setCancelVisible
]
=
useState
(
false
);
const
[
cancelAuditInfo
,
setCancelAuditInfo
]
=
useState
({});
// 取消订单审核详情
const
[
cancelDetailVisible
,
setCancelDetailVisible
]
=
useState
(
false
);
const
[
cancelDetailInfo
,
setCancelDetailInfo
]
=
useState
({});
// 关闭modal
const
closeModal
=
isReload
=>
{
if
(
isReload
===
true
)
{
// eslint-disable-next-line no-unused-expressions
actionRef
.
current
?.
reload
?.();
}
setVisible
(
false
);
setDetailVisible
(
false
);
setProofsVisible
(
false
);
// setAppealDetailModal(false);
setAfterVisible
(
false
);
setLogisticsComModalVisible
(
false
);
setCancelVisible
(
false
);
setCancelDetailVisible
(
false
);
setRejectVisible
(
false
);
};
// 查看申诉详情
// const viewAppeal = async r => {
// const detailData = await getDetail({ appealNo: r.appealNo });
// setAppealDetailModal(true);
// setSelectedRow(detailData);
// };
// 查看凭证
const
viewProofs
=
proofs
=>
{
if
(
!
proofs
)
{
notification
.
warning
({
message
:
'
该订单没有凭证
'
});
return
;
}
const
list
=
proofs
.
replace
(
/
(\u
ff1b|
\u
ff0c|
\u
003b
)
/g
,
'
,
'
).
split
(
'
,
'
);
setProofsData
(
list
);
setProofsVisible
(
true
);
};
// 审核
const
openAudit
=
async
({
serviceNo
,
serviceType
,
orderNo
})
=>
{
const
data
=
await
auditInfoApi
({
serviceNo
});
setAuditInfo
({
...
data
?.
data
,
serviceNo
,
serviceType
,
orderNo
});
setVisible
(
true
);
};
// 查看物流
const
handleCom
=
async
({
expressCompanyCode
,
deliveryNo
})
=>
{
const
tempObj
=
{
detailList
:
[],
key
:
Date
.
now
(),
};
const
data
=
await
trackInfo
({
expressCompanyCode
,
logisticsNo
:
deliveryNo
});
if
(
!
data
)
{
notification
.
info
({
message
:
'
暂无物流信息
'
});
return
;
}
tempObj
.
expressCompanyName
=
data
.
logisticsName
;
tempObj
.
deliveryNo
=
data
.
logisticsBillNo
;
if
(
data
.
logisticsList
?.
length
)
{
data
.
logisticsList
.
forEach
(
v
=>
{
tempObj
.
detailList
=
[...
tempObj
.
detailList
,
...
v
.
detailList
];
});
}
setLogisticsComModalVisible
(
true
);
setLogisticsComList
(
tempObj
);
};
// 物流拦截
const
openLogistics
=
r
=>
{
confirm
({
title
:
'
温馨提示
'
,
okText
:
'
确认拦截
'
,
cancelText
:
'
取消拦截
'
,
content
:
(
<
div
>
请48小时内自行联系物流公司进行物流拦截,系统监测拦截成功后
<
span
className=
{
styles
.
redTipBold
}
>
自动同意
</
span
>
退款
</
div
>
),
async
onOk
()
{
const
data
=
await
logisticsIntercept
({
serviceNo
:
r
.
serviceNo
});
if
(
data
.
businessCode
===
'
0000
'
)
{
notification
.
success
({
message
:
'
拦截成功
'
});
actionRef
.
current
.
reload
();
}
else
{
notification
.
error
({
message
:
data
.
msg
||
'
拦截失败
'
});
}
},
onCancel
()
{
console
.
log
(
'
Cancel
'
);
},
});
};
// 查看
const
viewDetail
=
async
({
serviceNo
})
=>
{
const
res
=
await
orderDetail
({
serviceNo
});
const
data
=
res
.
data
||
[];
setDetailInfo
(
data
);
setDetailVisible
(
true
);
};
// 查看售后操作日志
const
viewLog
=
async
r
=>
{
const
data
=
await
getOpLog
(
r
.
serviceNo
);
if
(
data
?.
data
?.
length
)
{
setAfterList
(
data
.
data
);
setAfterVisible
(
true
);
}
};
// 已退款/允许退款
const
refund
=
async
({
serviceNo
})
=>
{
const
data
=
await
shopCheck
({
serviceNo
,
auditResult
:
1
,
});
if
(
data
.
businessCode
===
'
0000
'
)
{
notification
.
success
({
message
:
'
操作成功
'
});
closeModal
(
true
);
}
};
// 驳回
const
reject
=
async
({
serviceNo
})
=>
{
setServiceNoInfo
(
serviceNo
);
setRejectVisible
(
true
);
};
// 取消订单审核
const
openCancelAudit
=
async
({
serviceNo
})
=>
{
// const data = await auditInfoApi({ serviceNo });
setCancelAuditInfo
({
serviceNo
});
setCancelVisible
(
true
);
};
// 取消订单审核详情
const
viewCancelDetail
=
async
({
serviceNo
})
=>
{
const
data
=
await
cancelOrderDetail
({
serviceNo
});
setCancelDetailInfo
(
data
||
[]);
setCancelDetailVisible
(
true
);
};
const
[
form
]
=
Form
.
useForm
();
const
formConfig
=
getFormConfig
({
actionRef
,
tableParams
,
setTableParams
,
setCurrentTab
,
});
const
afterTime
=
[
moment
().
subtract
(
3
,
'
month
'
),
moment
()];
const
FormSearchProps
=
{
form
,
initialValues
:
{
afterTime
,
},
formOptions
:
{},
...
formConfig
,
};
// tab选项框回调
const
tabChange
=
tabIndex
=>
{
setCurrentTab
(
tabIndex
);
const
{
type
,
dealStatus
}
=
TAB_MAPPING_DATA
[
tabIndex
];
form
.
resetFields
();
form
.
setFieldsValue
({
...
tableParams
,
dealStatus
,
type
,
});
setTableParams
({
...
tableParams
,
dealStatus
,
type
});
};
// 监听路由query参数变化重新发起请求
useEffect
(()
=>
{
if
(
location
?.
query
)
{
const
{
orderNo
:
orderNumber
,
tab
}
=
location
?.
query
;
if
(
orderNumber
)
{
tabChange
(
''
);
}
const
params
=
{
orderNo
:
orderNumber
,
dealStatus
:
null
,
serviceNo
:
null
,
type
:
null
,
receiverPhone
:
null
,
receiverName
:
null
,
};
form
.
setFieldsValue
(
params
);
setTableParams
(
params
);
if
(
tab
)
{
tabChange
(
tab
);
}
if
(
actionRef
.
current
)
{
actionRef
.
current
.
reload
();
}
}
},
[
location
]);
// 动态计算表格滚动区域高度
const
clientInfo
=
getClientInfo
();
const
calculationLayoutHeight
=
e
=>
{
const
otherHeight
=
typeof
e
===
'
number
'
?
e
:
330
;
const
h
=
clientInfo
.
height
-
scrollYRef
.
current
.
offsetTop
-
otherHeight
;
setTableScrollY
(
h
<
500
?
'
500px
'
:
h
);
};
useEffect
(()
=>
{
calculationLayoutHeight
(
330
);
},
[]);
window
.
removeEventListener
(
'
resize
'
,
calculationLayoutHeight
);
window
.
addEventListener
(
'
resize
'
,
calculationLayoutHeight
);
const
columns
=
getColumns
({
openAudit
,
// viewAppeal,
viewProofs
,
openLogistics
,
viewDetail
,
viewLog
,
handleCom
,
refund
,
reject
,
canEditable
,
openCancelAudit
,
viewCancelDetail
,
tableParams
,
});
// 表格属性
const
tableProps
=
{
columns
,
params
:
tableParams
,
bordered
:
true
,
scroll
:
{
x
:
'
100%
'
,
y
:
tableScrollY
},
rowKey
:
r
=>
r
.
serviceNo
,
request
:
async
params
=>
{
const
[
start
,
end
]
=
params
.
afterTime
||
afterTime
;
const
{
current
:
page
,
pageSize
:
size
}
=
params
;
const
startDate
=
start
?
moment
(
start
).
format
(
'
YYYY-MM-DD
'
)
:
''
;
const
endDate
=
end
?
moment
(
end
).
format
(
'
YYYY-MM-DD
'
)
:
''
;
const
requestParams
=
{
page
,
size
,
...
params
,
startDate
,
endDate
,
};
delete
requestParams
.
afterTime
;
delete
requestParams
.
current
;
delete
requestParams
.
pageSize
;
const
countRes
=
await
getAfterPendingNum
({
startDate
,
endDate
,
});
const
res
=
await
searchList
(
requestParams
);
const
{
records
=
[],
total
}
=
res
.
data
;
if
(
countRes
.
code
===
'
0000
'
)
{
setTabInfoData
(
countRes
.
data
);
}
return
{
data
:
records
,
total
,
};
},
toolBarRender
:
null
,
};
const
TabCountElement
=
({
count
,
text
})
=>
(
<
span
>
{
text
}
<
span
className=
"count"
>
{
count
}
</
span
>
</
span
>
);
return
(
return
(
<
PageHeaderWrapper
>
<
PageHeaderWrapper
>
<
Tabs
defaultActiveKey=
"1"
>
<
FormSearch
{
...
FormSearchProps
}
/>
<
TabPane
tab=
"未审核"
key=
"1"
>
<
div
className=
{
styles
[
'
tab-box
'
]
}
>
<
Pending
canEditable=
{
canEditable
}
/>
<
Tabs
</
TabPane
>
activeKey=
{
currentTab
}
<
TabPane
tab=
"已审核"
key=
"2"
>
onChange=
{
tabChange
}
<
PassAudit
canEditable=
{
canEditable
}
/>
size=
"large"
</
TabPane
>
tabBarStyle=
{
{
padding
:
'
0 30px 0 15px
'
,
border
:
'
none
'
}
}
>
<
TabPane
key=
""
tab=
"全部"
></
TabPane
>
<
TabPane
key=
"1"
tab=
{
<
TabCountElement
text=
"仅退款(未发货)待审核"
count=
{
tabInfoData
.
cancelOrderPendingAuditNum
}
/>
}
/>
<
TabPane
key=
"2"
tab=
{
<
TabCountElement
text=
"仅退款待审核"
count=
{
tabInfoData
.
refundPendingAuditNum
}
/>
}
/>
<
TabPane
key=
"3"
tab=
{
<
TabCountElement
text=
"退货退款待审核"
count=
{
tabInfoData
.
returnPendingAuditNum
}
/>
}
/>
<
TabPane
key=
"4"
tab=
{
<
TabCountElement
text=
"退货入库待审核"
count=
{
tabInfoData
.
returnWaitAuditNum
}
/>
}
/>
<
TabPane
key=
"5"
tab=
{
<
TabCountElement
text=
"已完成"
count=
{
tabInfoData
.
afterSalseDoneNum
}
/>
}
/>
</
Tabs
>
</
Tabs
>
</
div
>
<
div
ref=
{
scrollYRef
}
></
div
>
<
ProTable
{
...
tableProps
}
actionRef=
{
actionRef
}
formRef=
{
formRef
}
search=
{
false
}
// toolBarRender={false}
/>
<
AuditModal
visible=
{
visible
}
onCancel=
{
closeModal
}
formData=
{
auditInfo
}
/>
<
DetailTable
visible=
{
detailVisible
}
onCancel=
{
closeModal
}
dataSource=
{
detailInfo
}
/>
<
ProofsModal
visible=
{
proofsVisible
}
onCancel=
{
closeModal
}
data=
{
proofsData
}
/>
<
LogisticsCom
onSubmit=
{
closeModal
}
onCancel=
{
closeModal
}
modalVisible=
{
logisticsComModalVisible
}
value=
{
LogisticsComList
}
key=
{
LogisticsComList
.
key
}
/>
{
/* <AppealDetail
data={selectedRow}
modalVisible={appealDetailModal}
onCancel={closeModal}
></AppealDetail> */
}
<
AfterLog
visible=
{
afterVisible
}
onCancel=
{
closeModal
}
data=
{
afterList
}
/>
<
RejectModal
visible=
{
rejectVisible
}
onCancel=
{
closeModal
}
serviceNo=
{
serviceNoInfo
}
/>
<
CancelAuditModal
visible=
{
cancelVisible
}
onCancel=
{
closeModal
}
formData=
{
cancelAuditInfo
}
/>
<
CancelDetailTable
visible=
{
cancelDetailVisible
}
onCancel=
{
closeModal
}
dataSource=
{
cancelDetailInfo
}
/>
</
PageHeaderWrapper
>
</
PageHeaderWrapper
>
);
);
}
}
;
export
default
connect
(({
menu
})
=>
({
export
default
connect
(({
menu
})
=>
({
permissions
:
menu
.
permissions
,
permissions
:
menu
.
permissions
,
...
...
src/pages/AfterSaleManage/index.less
0 → 100644
View file @
d8defddc
.proofs {
padding: 5px;
border: 1px solid #ddd;
border-radius: 10px;
}
.proofsWrap {
display: flex;
justify-content: space-between;
min-height: 300px;
max-height: 600px;
overflow: auto;
}
.detailWrap {
min-height: 300px;
max-height: 600px;
overflow: auto;
}
.logBtn {
display: inherit;
margin: 20px auto;
}
.redTip {
color: #ff4d4f;
font-size: 14px;
}
.redTipBold {
color: #ff1616;
font-weight: bold;
}
.tab-box {
position: relative;
z-index: 2;
width: calc(100% - 100px);
// margin-top: 20px;
// background-color: #fff;
margin-top: 20px;
margin-bottom: -80px;
:global .ant-tabs-nav::before {
display: none;
}
:global .ant-tabs-tab {
.count {
padding-left: 5px;
color: #888;
transition: 0.3s ease;
}
&:hover .count {
color: #1890ff;
transition: 0.3s ease;
}
}
:global .ant-tabs-tab-active {
.count {
color: #1890ff;
}
}
}
src/pages/AfterSaleManage/services.js
View file @
d8defddc
...
@@ -6,35 +6,55 @@ import _ from 'lodash';
...
@@ -6,35 +6,55 @@ import _ from 'lodash';
const
{
kdspApi
}
=
config
;
const
{
kdspApi
}
=
config
;
// 分页查询所有数据
// 分页查询所有数据
export
async
function
searchList
(
params
,
queryStatus
)
{
export
const
searchList
=
params
=>
const
param
=
{
request
.
post
(
'
/api/kdsp/op/afs/shop/list
'
,
{
...
params
,
pageNo
:
params
.
current
,
pageSize
:
params
.
pageSize
||
20
,
queryStatus
,
};
const
data
=
await
request
.
post
(
'
/api/kdsp/op/afs/shop/list
'
,
{
prefix
:
kdspApi
,
prefix
:
kdspApi
,
data
:
stringify
(
_
.
omitBy
(
param
,
v
=>
!
v
)),
data
:
stringify
(
_
.
omitBy
(
param
s
,
v
=>
!
v
&&
v
!==
0
)),
headers
:
{
headers
:
{
'
Content-Type
'
:
'
application/x-www-form-urlencoded
'
,
'
Content-Type
'
:
'
application/x-www-form-urlencoded
'
,
},
},
});
});
if
(
data
.
data
)
{
return
{
// export async function searchList(params, queryStatus) {
total
:
data
.
data
.
total
,
// const param = {
data
:
data
.
data
.
records
,
// ...params,
};
// pageNo: params.current,
}
// pageSize: params.pageSize || 20,
return
{
// queryStatus,
total
:
0
,
// };
data
:
[],
// const data = await request.post('/api/kdsp/op/afs/shop/list', {
};
// prefix: kdspApi,
}
// data: stringify(_.omitBy(param, v => !v)),
// headers: {
// 'Content-Type': 'application/x-www-form-urlencoded',
// },
// });
// if (data.data) {
// return {
// total: data.data.total,
// data: data.data.records,
// };
// }
// return {
// total: 0,
// data: [],
// };
// }
// 售后单详情
// 售后单详情
export
async
function
orderDetail
(
params
)
{
export
function
orderDetail
(
params
)
{
const
data
=
await
request
.
get
(
'
/api/kdsp/op/afs/sku
'
,
{
return
request
.
get
(
'
/api/kdsp/op/afs/sku
'
,
{
prefix
:
kdspApi
,
params
,
headers
:
{
'
Content-Type
'
:
'
application/x-www-form-urlencoded
'
,
},
});
}
// 取消售后单详情
export
async
function
cancelOrderDetail
(
params
)
{
const
data
=
await
request
.
get
(
'
/cancel-order/sku
'
,
{
prefix
:
kdspApi
,
prefix
:
kdspApi
,
params
,
params
,
headers
:
{
headers
:
{
...
@@ -43,6 +63,7 @@ export async function orderDetail(params) {
...
@@ -43,6 +63,7 @@ export async function orderDetail(params) {
});
});
return
data
.
data
||
[];
return
data
.
data
||
[];
}
}
// 售后审核
// 售后审核
export
async
function
shopAudit
(
params
)
{
export
async
function
shopAudit
(
params
)
{
return
request
.
post
(
'
/api/kdsp/op/afs/shop/audit
'
,
{
return
request
.
post
(
'
/api/kdsp/op/afs/shop/audit
'
,
{
...
@@ -50,6 +71,15 @@ export async function shopAudit(params) {
...
@@ -50,6 +71,15 @@ export async function shopAudit(params) {
prefix
:
kdspApi
,
prefix
:
kdspApi
,
});
});
}
}
// 取消订单审核
export
async
function
cancelShopAudit
(
params
)
{
return
request
.
post
(
'
/cancel-order/audit
'
,
{
data
:
params
,
prefix
:
kdspApi
,
});
}
// 查询审核信息
// 查询审核信息
export
async
function
auditInfoApi
(
params
)
{
export
async
function
auditInfoApi
(
params
)
{
return
request
.
get
(
'
/api/kdsp/op/afs/back-info
'
,
{
return
request
.
get
(
'
/api/kdsp/op/afs/back-info
'
,
{
...
@@ -93,3 +123,18 @@ export async function getOpLog(params) {
...
@@ -93,3 +123,18 @@ export async function getOpLog(params) {
prefix
:
kdspApi
,
prefix
:
kdspApi
,
});
});
}
}
// 查询物流记录信息
export
async
function
getLogisticsRecord
(
params
)
{
return
request
.
get
(
`/api/kdsp/op/logistics/kd100/track-list?orderNo=
${
params
.
orderNo
}
`
,
{
prefix
:
kdspApi
,
});
}
// 查询售后待办数量
export
function
getAfterPendingNum
(
data
)
{
return
request
.
post
(
'
/api/kdsp/op/afs/getPendingNum
'
,
{
prefix
:
kdspApi
,
data
,
});
}
src/pages/AfterSaleManage/PassAudit/index.jsx
→
src/pages/AfterSaleManage
Old
/PassAudit/index.jsx
View file @
d8defddc
File moved
src/pages/AfterSaleManage/Pending/index.jsx
→
src/pages/AfterSaleManage
Old
/Pending/index.jsx
View file @
d8defddc
File moved
src/pages/AfterSaleManageOld/components/AfterLog.jsx
0 → 100644
View file @
d8defddc
/* eslint-disable no-restricted-syntax */
/* eslint-disable guard-for-in */
import
React
,
{
useState
,
useEffect
}
from
'
react
'
;
import
{
Modal
,
Timeline
,
Button
}
from
'
antd
'
;
import
styles
from
'
../styles.less
'
;
const
LogisticsCom
=
props
=>
{
const
{
visible
,
onCancel
,
data
}
=
props
;
return
(
<
Modal
destroyOnClose
title=
"查看记录"
visible=
{
visible
}
onCancel=
{
()
=>
onCancel
()
}
onOk=
{
()
=>
onCancel
()
}
footer=
{
false
}
>
{
data
?.
length
?
(
<
Timeline
>
{
data
?.
map
((
item
,
index
)
=>
(
// eslint-disable-next-line react/no-array-index-key
<
Timeline
.
Item
color=
{
index
>
0
?
'
gray
'
:
'
blue
'
}
key=
{
index
}
>
<
p
>
{
item
.
optDesc
}
</
p
>
<
p
>
{
item
.
optTime
}
</
p
>
</
Timeline
.
Item
>
))
}
</
Timeline
>
)
:
(
'
暂无查看记录
'
)
}
<
Button
type=
"primary"
onClick=
{
props
.
onCancel
}
className=
{
styles
.
logBtn
}
>
关闭
</
Button
>
</
Modal
>
);
};
export
default
LogisticsCom
;
src/pages/AfterSaleManageOld/components/auditModal.jsx
0 → 100644
View file @
d8defddc
import
React
from
'
react
'
;
import
{
Form
}
from
'
@ant-design/compatible
'
;
import
'
@ant-design/compatible/assets/index.css
'
;
import
{
Modal
,
Input
,
Cascader
,
notification
,
InputNumber
}
from
'
antd
'
;
import
{
shopAudit
}
from
'
../services
'
;
import
styles
from
'
../styles.less
'
;
const
FormItem
=
Form
.
Item
;
const
{
TextArea
}
=
Input
;
const
{
confirm
}
=
Modal
;
const
AuditModal
=
props
=>
{
const
{
visible
,
onCancel
,
form
:
{
getFieldDecorator
,
getFieldValue
,
validateFields
,
resetFields
},
formData
=
{},
}
=
props
;
const
handleCancel
=
isSuccess
=>
{
resetFields
();
onCancel
(
isSuccess
);
};
const
treeData
=
[
{
label
:
'
同意
'
,
value
:
1
,
},
{
label
:
'
不同意
'
,
value
:
2
,
children
:
[
{
label
:
'
商品与出售商品不符
'
,
value
:
1
,
},
{
label
:
'
影响二次销售
'
,
value
:
2
,
},
{
label
:
'
未提供商品照片,需要上传照片
'
,
value
:
4
,
},
{
label
:
'
快递正在配送中
'
,
value
:
5
,
},
{
label
:
'
订单已经取消
'
,
value
:
6
,
},
{
label
:
'
商品已超过售后期
'
,
value
:
7
,
},
{
label
:
'
其他
'
,
value
:
3
,
},
],
},
];
const
submitCheckResult
=
async
fieldsValue
=>
{
const
{
auditResult
}
=
fieldsValue
;
const
data
=
await
shopAudit
({
...
fieldsValue
,
refuseCode
:
auditResult
?.[
1
],
auditResult
:
auditResult
?.[
0
],
serviceNo
:
formData
?.
serviceNo
,
});
if
(
data
.
businessCode
===
'
0000
'
)
{
notification
.
success
({
message
:
'
审核成功
'
});
handleCancel
(
true
);
}
};
const
handleOk
=
()
=>
{
validateFields
((
error
,
fieldsValue
)
=>
{
if
(
!
error
)
{
const
{
auditResult
}
=
fieldsValue
;
// 如果为仅退款 并且 审核同意 弹出二次确认提示
if
(
+
formData
.
serviceType
===
1
&&
auditResult
?.[
0
]
===
1
)
{
confirm
({
title
:
'
温馨提示
'
,
content
:
(
<
div
>
当前类型为【
<
span
className=
{
styles
.
redTipBold
}
>
仅退款
</
span
>
】,请核查该订单物流状态,如有在途物流,务必自行拦截后点击确认;
</
div
>
),
okText
:
'
确认退款
'
,
onOk
()
{
submitCheckResult
(
fieldsValue
);
},
onCancel
()
{
handleCancel
(
true
);
},
});
}
else
{
submitCheckResult
(
fieldsValue
);
}
}
});
};
const
layout
=
{
labelCol
:
{
span
:
6
},
wrapperCol
:
{
span
:
16
},
};
const
auditResult
=
getFieldValue
(
'
auditResult
'
);
const
isAgree
=
()
=>
auditResult
?.[
0
]
===
1
;
const
isRefuse
=
()
=>
auditResult
&&
auditResult
[
0
]
!==
1
;
return
(
<
Modal
title=
"售后操作确认"
visible=
{
visible
}
onOk=
{
()
=>
handleOk
()
}
onCancel=
{
()
=>
handleCancel
()
}
>
{
+
formData
.
serviceType
===
1
&&
(
<
div
className=
{
styles
.
redTip
}
>
温馨提示:当前售后类型为用户未收到产品,申请
<
span
className=
{
styles
.
redTipBold
}
>
仅退款
</
span
>
,请务必检查此单物流状态后审核。
</
div
>
)
}
<
Form
{
...
layout
}
name=
"formData"
>
<
FormItem
label=
"审核结果"
>
{
getFieldDecorator
(
'
auditResult
'
,
{
rules
:
[
{
required
:
true
,
message
:
'
请选择审核结果!
'
,
},
],
})(
<
Cascader
allowClear
showSearch
style=
{
{
width
:
'
315px
'
}
}
dropdownStyle=
{
{
maxHeight
:
400
,
overflow
:
'
auto
'
}
}
options=
{
treeData
}
placeholder=
"请选择"
/>,
)
}
</
FormItem
>
{
isAgree
()
&&
+
formData
.
serviceType
===
2
&&
(
<
div
>
<
FormItem
label=
"退货地址"
>
{
getFieldDecorator
(
'
receiveAddress
'
,
{
initialValue
:
formData
.
address
,
rules
:
[
{
required
:
true
,
message
:
'
请填写退货地址!
'
,
},
{
message
:
'
格式有误,请填写正确的退货地址!
'
,
pattern
:
/
[
a-zA-Z0-9_
\u
4e00-
\u
9fa5
]
/g
,
},
],
})(<
Input
placeholder=
"最多输入50个字符"
maxLength=
"50"
allowClear
/>)
}
</
FormItem
>
<
FormItem
label=
"收件人"
>
{
getFieldDecorator
(
'
receiverName
'
,
{
initialValue
:
formData
.
name
,
rules
:
[
{
required
:
true
,
message
:
'
请填写收件人!
'
,
},
{
message
:
'
格式有误,请输入中英文、数字!
'
,
pattern
:
/^
[
A-Za-z0-9
\u
4e00-
\u
9fa5
]
+$/
,
},
],
})(<
Input
placeholder=
"最多输入25个字符"
maxLength=
"25"
allowClear
/>)
}
</
FormItem
>
<
FormItem
label=
"手机号码"
>
{
getFieldDecorator
(
'
receiverPhone
'
,
{
initialValue
:
formData
.
phone
,
rules
:
[
//
{
// pattern: new RegExp(/^1[3456789]\d
{
9
}
$
/
,
'
g
'
),
// message: '手机号码格式不正确!',
//
},
{
pattern
:
new
RegExp
(
/^
[
0-9
]
*$/
),
message
:
'
手机号码格式不正确!
'
,
},
{
required
:
true
,
message
:
'
请填写手机号码!
'
,
},
],
})(<
Input
placeholder=
"请填写手机号码"
maxLength=
"11"
allowClear
/>)
}
</
FormItem
>
</
div
>
)
}
{
isRefuse
()
&&
auditResult
[
1
]
===
3
&&
(
<
FormItem
label=
"拒绝原因"
>
{
getFieldDecorator
(
'
refuseDesc
'
,
{
initialValue
:
formData
.
refuseDesc
,
rules
:
[
{
required
:
true
,
message
:
'
请填写拒绝原因!
'
,
},
],
})(
<
TextArea
placeholder=
"请填写拒绝原因"
allowClear
autoSize=
{
{
minRows
:
3
,
maxRows
:
6
}
}
/>,
)
}
</
FormItem
>
)
}
{
isRefuse
()
&&
auditResult
[
1
]
!==
3
&&
(
<
FormItem
label=
"补充说明"
>
{
getFieldDecorator
(
'
refuseDesc
'
,
{
initialValue
:
formData
.
refuseDesc
,
rules
:
[
{
message
:
'
请填写补充说明!
'
,
},
],
})(
<
TextArea
placeholder=
"请填写补充说明"
allowClear
autoSize=
{
{
minRows
:
3
,
maxRows
:
6
}
}
maxLength=
{
120
}
showCount
/>,
)
}
</
FormItem
>
)
}
</
Form
>
</
Modal
>
);
};
export
default
Form
.
create
()(
AuditModal
);
src/pages/AfterSaleManageOld/components/detailTable.jsx
0 → 100644
View file @
d8defddc
import
React
from
'
react
'
;
import
{
Modal
,
Table
}
from
'
antd
'
;
export
default
props
=>
{
const
{
visible
,
onCancel
,
dataSource
}
=
props
;
const
handleCancel
=
()
=>
{
onCancel
();
};
const
columns
=
[
{
title
:
'
商品名称
'
,
width
:
400
,
dataIndex
:
'
skuName
'
,
},
{
title
:
'
商品属性
'
,
dataIndex
:
'
skuAttr
'
,
},
{
title
:
'
商品件数
'
,
dataIndex
:
'
count
'
,
},
];
return
(
<
Modal
title=
"订单详情"
visible=
{
visible
}
onCancel=
{
handleCancel
}
footer=
{
null
}
width=
{
800
}
>
<
Table
dataSource=
{
dataSource
}
columns=
{
columns
}
key=
"skuName"
pagination=
{
false
}
bordered
/>
</
Modal
>
);
};
src/pages/AfterSaleManageOld/components/proofsModal.jsx
0 → 100644
View file @
d8defddc
import
React
,
{
useState
}
from
'
react
'
;
import
{
Modal
}
from
'
antd
'
;
import
style
from
'
../styles.less
'
;
export
default
props
=>
{
const
{
visible
,
onCancel
,
data
}
=
props
;
const
[
detailModal
,
setDetailModal
]
=
useState
(
false
);
const
[
detailSrc
,
setDetailSrc
]
=
useState
(
null
);
const
handleCancel
=
()
=>
{
onCancel
();
};
const
clickImg
=
src
=>
{
setDetailSrc
(
src
);
setDetailModal
(
true
);
};
return
(
<
Modal
title=
"售后凭证"
visible=
{
visible
}
onCancel=
{
handleCancel
}
footer=
{
null
}
width=
{
600
}
>
<
div
className=
{
style
.
proofsWrap
}
>
{
data
.
map
(
item
=>
(
<
img
src=
{
item
}
key=
{
item
}
alt=
{
item
}
className=
{
style
.
proofs
}
width=
{
250
}
onClick=
{
()
=>
clickImg
(
item
)
}
/>
))
}
</
div
>
<
Modal
title=
"凭证详情"
visible=
{
detailModal
}
onCancel=
{
()
=>
setDetailModal
(
false
)
}
footer=
{
null
}
width=
{
700
}
>
<
p
className=
{
style
.
detailWrap
}
>
<
img
src=
{
detailSrc
}
alt=
{
detailSrc
}
/>
</
p
>
</
Modal
>
</
Modal
>
);
};
src/pages/AfterSaleManageOld/components/rejectModal.jsx
0 → 100644
View file @
d8defddc
import
React
from
'
react
'
;
import
{
Form
}
from
'
@ant-design/compatible
'
;
import
'
@ant-design/compatible/assets/index.css
'
;
import
{
Modal
,
Input
,
notification
}
from
'
antd
'
;
import
{
shopCheck
}
from
'
../services
'
;
const
FormItem
=
Form
.
Item
;
const
{
TextArea
}
=
Input
;
const
RejectModal
=
props
=>
{
const
{
visible
,
onCancel
,
form
:
{
getFieldDecorator
,
validateFields
,
resetFields
},
serviceNo
=
null
,
}
=
props
;
const
handleCancel
=
isSuccess
=>
{
resetFields
();
onCancel
(
isSuccess
);
};
const
handleOk
=
()
=>
{
validateFields
(
async
(
error
,
fieldsValue
)
=>
{
if
(
!
error
)
{
const
data
=
await
shopCheck
({
...
fieldsValue
,
serviceNo
,
auditResult
:
2
,
});
if
(
data
.
code
===
'
0000
'
)
{
notification
.
success
({
message
:
'
操作成功
'
});
handleCancel
(
true
);
}
}
});
};
const
layout
=
{
labelCol
:
{
span
:
6
},
wrapperCol
:
{
span
:
16
},
};
return
(
<
Modal
title=
"驳回"
visible=
{
visible
}
onOk=
{
()
=>
handleOk
()
}
onCancel=
{
()
=>
handleCancel
()
}
>
<
Form
{
...
layout
}
name=
"formData"
>
<
FormItem
label=
"原因"
>
{
getFieldDecorator
(
'
refuseDesc
'
,
{
rules
:
[
{
required
:
true
,
message
:
'
请填写原因!
'
,
},
],
})(
<
TextArea
placeholder=
"请填写原因"
allowClear
autoSize=
{
{
minRows
:
3
,
maxRows
:
6
}
}
/>,
)
}
</
FormItem
>
</
Form
>
</
Modal
>
);
};
export
default
Form
.
create
()(
RejectModal
);
src/pages/AfterSaleManageOld/data.js
0 → 100644
View file @
d8defddc
import
{
Tag
,
Badge
}
from
'
antd
'
;
import
React
from
'
react
'
;
export
const
appealType
=
{
1
:
'
已申诉
'
,
0
:
'
未申诉
'
,
};
export
const
columnSticData
=
[
{
title
:
'
售后类型
'
,
dataIndex
:
'
serviceType
'
,
hideInSearch
:
true
,
width
:
120
,
render
:
serviceType
=>
{
if
(
+
serviceType
===
1
)
{
return
<
span
style
=
{{
color
:
'
#ff1616
'
}}
>
仅退款
<
/span>
;
}
return
<
span
>
退货退款
<
/span>
;
},
},
{
title
:
'
订单ID
'
,
dataIndex
:
'
orderNo
'
,
hideInTable
:
true
,
width
:
200
,
},
{
title
:
'
售后单ID
'
,
dataIndex
:
'
serviceNo
'
,
width
:
300
,
render
:
(
serviceNo
,
r
)
=>
(
<
div
>
{
r
.
timeout
?
<
Tag
color
=
"
red
"
>
{
serviceNo
}
<
/Tag> : serviceNo
}
{
<
Badge
count
=
{
r
.
reminderFlag
?
'
催
'
:
''
}
size
=
"
default
"
/>
}
<
/div
>
),
},
{
title
:
'
订单ID
'
,
dataIndex
:
'
orderNo
'
,
hideInSearch
:
true
,
width
:
300
,
},
{
title
:
'
售后状态
'
,
dataIndex
:
'
dealStatus
'
,
hideInTable
:
true
,
valueEnum
:
{
0
:
'
待审核
'
,
10
:
'
三方审核中
'
,
11
:
'
三方审核通过
'
,
12
:
'
三方审核拒绝
'
,
13
:
'
客服审核通过
'
,
14
:
'
商户审核中
'
,
15
:
'
商户审核通过
'
,
16
:
'
商户审核拒绝
'
,
20
:
'
审核拒绝
'
,
21
:
'
申诉中
'
,
30
:
'
待填写退货物流信息
'
,
40
:
'
待退货入库
'
,
50
:
'
退货拒收
'
,
60
:
'
待退款
'
,
61
:
'
退货处理中
'
,
70
:
'
售后成功
'
,
99
:
'
用户取消
'
,
},
width
:
100
,
},
{
title
:
'
售后类型
'
,
dataIndex
:
'
type
'
,
hideInTable
:
true
,
width
:
120
,
valueEnum
:
{
1
:
'
仅退款
'
,
2
:
'
退货退款
'
,
},
},
{
title
:
'
收货人姓名
'
,
dataIndex
:
'
receiverName
'
,
width
:
200
,
},
{
title
:
'
收货人手机号
'
,
dataIndex
:
'
receiverPhone
'
,
width
:
200
,
},
{
title
:
'
收货人地址
'
,
dataIndex
:
'
receiveAddress
'
,
width
:
200
,
hideInSearch
:
true
,
},
{
title
:
'
订单开始时间
'
,
width
:
120
,
dataIndex
:
'
startDate
'
,
key
:
'
startDate
'
,
valueType
:
'
date
'
,
hideInTable
:
true
,
},
{
title
:
'
订单结束时间
'
,
width
:
120
,
dataIndex
:
'
endDate
'
,
key
:
'
endDate
'
,
valueType
:
'
date
'
,
hideInTable
:
true
,
},
{
title
:
'
售后原因
'
,
dataIndex
:
'
serviceReason
'
,
hideInSearch
:
true
,
width
:
200
,
},
{
title
:
'
售后发生时间
'
,
dataIndex
:
'
serviceTime
'
,
hideInSearch
:
true
,
width
:
200
,
},
{
title
:
'
超时时间
'
,
dataIndex
:
'
overtime
'
,
hideInSearch
:
true
,
width
:
200
,
},
{
title
:
'
是否催办
'
,
dataIndex
:
'
reminderFlag
'
,
hideInSearch
:
true
,
width
:
120
,
valueEnum
:
{
true
:
'
是
'
,
false
:
'
否
'
,
},
},
{
title
:
'
是否同意售后
'
,
dataIndex
:
'
isAgree
'
,
hideInSearch
:
true
,
width
:
120
,
},
{
title
:
'
拒绝原因
'
,
dataIndex
:
'
refuseReason
'
,
hideInSearch
:
true
,
width
:
200
,
},
];
export
const
columnPassAudit
=
[
...
columnSticData
,
{
title
:
'
商家退货地址
'
,
dataIndex
:
'
merchantAddress
'
,
hideInSearch
:
true
,
width
:
200
,
},
{
title
:
'
退回物流
'
,
dataIndex
:
'
expressCompanyName
'
,
hideInSearch
:
true
,
width
:
150
,
},
{
title
:
'
退回物流单号
'
,
dataIndex
:
'
deliveryNo
'
,
hideInSearch
:
true
,
width
:
200
,
},
{
title
:
'
售后状态
'
,
dataIndex
:
'
serviceStatus
'
,
hideInSearch
:
true
,
width
:
120
,
},
];
src/pages/AfterSaleManageOld/index.jsx
0 → 100644
View file @
d8defddc
import
{
Tabs
}
from
'
antd
'
;
import
React
from
'
react
'
;
import
{
PageHeaderWrapper
}
from
'
@ant-design/pro-layout
'
;
import
{
AFTER_SALE_ORDER
}
from
'
@/../config/permission.config
'
;
import
{
connect
}
from
'
dva
'
;
import
Pending
from
'
./Pending
'
;
import
PassAudit
from
'
./PassAudit
'
;
const
{
TabPane
}
=
Tabs
;
function
AfterSale
(
props
)
{
const
{
permissions
}
=
props
;
const
canEditable
=
permissions
[
AFTER_SALE_ORDER
.
EDITABLE
];
return
(
<
PageHeaderWrapper
>
<
Tabs
defaultActiveKey=
"1"
>
<
TabPane
tab=
"未审核"
key=
"1"
>
<
Pending
canEditable=
{
canEditable
}
/>
</
TabPane
>
<
TabPane
tab=
"已审核"
key=
"2"
>
<
PassAudit
canEditable=
{
canEditable
}
/>
</
TabPane
>
</
Tabs
>
</
PageHeaderWrapper
>
);
}
export
default
connect
(({
menu
})
=>
({
permissions
:
menu
.
permissions
,
}))(
AfterSale
);
src/pages/AfterSaleManageOld/services.js
0 → 100644
View file @
d8defddc
import
request
from
'
@/utils/request
'
;
import
config
from
'
../../../config/env.config
'
;
import
{
stringify
}
from
'
qs
'
;
import
_
from
'
lodash
'
;
const
{
kdspApi
}
=
config
;
// 分页查询所有数据
export
async
function
searchList
(
params
,
queryStatus
)
{
const
param
=
{
...
params
,
pageNo
:
params
.
current
,
pageSize
:
params
.
pageSize
||
20
,
queryStatus
,
};
const
data
=
await
request
.
post
(
'
/api/kdsp/op/afs/shop/list
'
,
{
prefix
:
kdspApi
,
data
:
stringify
(
_
.
omitBy
(
param
,
v
=>
!
v
)),
headers
:
{
'
Content-Type
'
:
'
application/x-www-form-urlencoded
'
,
},
});
if
(
data
.
data
)
{
return
{
total
:
data
.
data
.
total
,
data
:
data
.
data
.
records
,
};
}
return
{
total
:
0
,
data
:
[],
};
}
// 售后单详情
export
async
function
orderDetail
(
params
)
{
const
data
=
await
request
.
get
(
'
/api/kdsp/op/afs/sku
'
,
{
prefix
:
kdspApi
,
params
,
headers
:
{
'
Content-Type
'
:
'
application/x-www-form-urlencoded
'
,
},
});
return
data
.
data
||
[];
}
// 售后审核
export
async
function
shopAudit
(
params
)
{
return
request
.
post
(
'
/api/kdsp/op/afs/shop/audit
'
,
{
data
:
params
,
prefix
:
kdspApi
,
});
}
// 查询审核信息
export
async
function
auditInfoApi
(
params
)
{
return
request
.
get
(
'
/api/kdsp/op/afs/back-info
'
,
{
params
,
prefix
:
kdspApi
,
});
}
// 审核核检
export
async
function
shopCheck
(
params
)
{
return
request
.
post
(
'
/api/kdsp/op/afs/shop/check
'
,
{
params
,
headers
:
{
'
Content-Type
'
:
'
application/x-www-form-urlencoded
'
,
},
prefix
:
kdspApi
,
});
}
// 查询物流信息
export
async
function
trackInfo
(
params
)
{
const
data
=
await
request
.
get
(
'
/api/kdsp/op/logistics/kd100/track-list
'
,
{
params
,
prefix
:
kdspApi
,
});
if
(
data
.
businessCode
===
'
0000
'
)
{
return
data
.
data
;
}
return
{};
}
// 物流拦截
export
async
function
logisticsIntercept
(
params
)
{
return
request
.
get
(
'
/api/kdsp/op/afs/shop/logisticsIntercept
'
,
{
params
,
prefix
:
kdspApi
,
});
}
// 售后操作日志
export
async
function
getOpLog
(
params
)
{
return
request
.
get
(
`/afterSaleAdmin/getOpLog/
${
params
}
`
,
{
prefix
:
kdspApi
,
});
}
src/pages/AfterSaleManage/styles.less
→
src/pages/AfterSaleManage
Old
/styles.less
View file @
d8defddc
File moved
src/pages/ServiceGoods/components/FormPackage.jsx
View file @
d8defddc
...
@@ -59,7 +59,7 @@ const FormCai = forwardRef((props, ref) => {
...
@@ -59,7 +59,7 @@ const FormCai = forwardRef((props, ref) => {
name=
{
[
name
,
'
dishName
'
]
}
name=
{
[
name
,
'
dishName
'
]
}
rules=
{
[{
required
:
true
,
message
:
'
请输入菜名.
'
}]
}
rules=
{
[{
required
:
true
,
message
:
'
请输入菜名.
'
}]
}
>
>
<
Input
maxLength=
{
1
0
}
placeholder=
"最多10
个字符"
/>
<
Input
maxLength=
{
1
3
}
placeholder=
"最多13
个字符"
/>
</
Form
.
Item
>
</
Form
.
Item
>
</
Col
>
</
Col
>
<
Col
span=
{
2
}
>
<
Col
span=
{
2
}
>
...
@@ -312,8 +312,8 @@ const FormPackage = forwardRef((props, ref) => {
...
@@ -312,8 +312,8 @@ const FormPackage = forwardRef((props, ref) => {
onCancel=
{
()
=>
setVisibleGroup
(
false
)
}
onCancel=
{
()
=>
setVisibleGroup
(
false
)
}
>
>
<
Input
<
Input
placeholder=
"请输入分组名称,最多1
0
个字"
placeholder=
"请输入分组名称,最多1
3
个字"
maxLength=
{
1
0
}
maxLength=
{
1
3
}
onChange=
{
e
=>
setGroupName
(
e
.
target
.
value
)
}
onChange=
{
e
=>
setGroupName
(
e
.
target
.
value
)
}
/>
/>
</
Modal
>
</
Modal
>
...
...
src/pages/chainStoreManage/components/storeModal.jsx
View file @
d8defddc
...
@@ -223,7 +223,11 @@ const StoreModal = props => {
...
@@ -223,7 +223,11 @@ const StoreModal = props => {
onCancel=
{
()
=>
handleCancel
()
}
onCancel=
{
()
=>
handleCancel
()
}
>
>
<
Form
{
...
layout
}
name=
"formData"
>
<
Form
{
...
layout
}
name=
"formData"
>
<
FormItem
label=
"门店名称"
name=
"name"
>
<
FormItem
label=
"门店名称"
name=
"name"
extra=
"门店名称由商家名称+门店地址组成,示例:优衣库(三里屯店)"
>
{
getFieldDecorator
(
'
name
'
,
{
{
getFieldDecorator
(
'
name
'
,
{
rules
:
[{
required
:
true
,
message
:
'
请输入门店名称!
'
}],
rules
:
[{
required
:
true
,
message
:
'
请输入门店名称!
'
}],
initialValue
:
formData
.
name
,
initialValue
:
formData
.
name
,
...
@@ -286,7 +290,7 @@ const StoreModal = props => {
...
@@ -286,7 +290,7 @@ const StoreModal = props => {
{
getFieldDecorator
(
'
address
'
,
{
{
getFieldDecorator
(
'
address
'
,
{
rules
:
[{
required
:
true
,
message
:
'
请输入详细地址!
'
}],
rules
:
[{
required
:
true
,
message
:
'
请输入详细地址!
'
}],
initialValue
:
formData
.
address
,
initialValue
:
formData
.
address
,
})(<
Input
placeholder=
"请输入详细地址"
allowClear
maxLength=
{
50
}
/>)
}
})(<
Input
placeholder=
"请输入详细地址
,不包含省市区地址
"
allowClear
maxLength=
{
50
}
/>)
}
</
FormItem
>
</
FormItem
>
<
FormItem
label=
"经纬度"
>
<
FormItem
label=
"经纬度"
>
{
getFieldDecorator
(
'
lnglat
'
,
{
{
getFieldDecorator
(
'
lnglat
'
,
{
...
...
src/pages/orderManage/pendingDeliveryOrder/index.jsx
View file @
d8defddc
import
{
Button
,
notification
,
Modal
}
from
'
antd
'
;
import
{
Button
,
notification
,
Modal
,
Popover
}
from
'
antd
'
;
import
React
,
{
useState
,
useEffect
,
useRef
}
from
'
react
'
;
import
React
,
{
useState
,
useEffect
,
useRef
}
from
'
react
'
;
import
{
PageHeaderWrapper
}
from
'
@ant-design/pro-layout
'
;
import
{
PageHeaderWrapper
}
from
'
@ant-design/pro-layout
'
;
import
{
useLocation
}
from
'
react-router-dom
'
;
import
ProTable
from
'
@ant-design/pro-table
'
;
import
ProTable
from
'
@ant-design/pro-table
'
;
import
{
FormInstance
}
from
'
antd/lib/form
'
;
import
{
FormInstance
}
from
'
antd/lib/form
'
;
import
moment
from
'
moment
'
;
import
moment
from
'
moment
'
;
import
{
connect
}
from
'
dva
'
;
import
{
connect
}
from
'
dva
'
;
import
{
PENDING_DELIVERY_ORDER
,
DELIVERY_ORDER
}
from
'
@/../config/permission.config
'
;
import
{
PENDING_DELIVERY_ORDER
,
DELIVERY_ORDER
}
from
'
@/../config/permission.config
'
;
import
style
from
'
./index.less
'
;
import
LogisticsForm
from
'
./components/LogisticsForm
'
;
import
LogisticsForm
from
'
./components/LogisticsForm
'
;
import
style
from
'
./styles.less
'
;
import
PopoverDom
from
'
./components/PreviewImage
'
;
import
PopoverDom
from
'
./components/PreviewImage
'
;
import
LogisticsCom
from
'
./components/LogisticsCom
'
;
import
LogisticsCom
from
'
./components/LogisticsCom
'
;
import
DelayDeliverGoods
from
'
./components/DelayDeliverGoods
'
;
import
DelayDeliverGoods
from
'
./components/DelayDeliverGoods
'
;
...
@@ -23,8 +24,10 @@ import {
...
@@ -23,8 +24,10 @@ import {
const
{
confirm
}
=
Modal
;
const
{
confirm
}
=
Modal
;
const
TableList
=
props
=>
{
const
TableList
=
props
=>
{
const
location
=
useLocation
();
const
dateFormat
=
'
YYYY-MM-DD
'
;
const
dateFormat
=
'
YYYY-MM-DD
'
;
const
{
permissions
}
=
props
;
const
{
permissions
}
=
props
;
// 因待发货 和 已发货都使用当前组件,功能且相同,所以只要授权其中一个均可获得发货物流权限
// 因待发货 和 已发货都使用当前组件,功能且相同,所以只要授权其中一个均可获得发货物流权限
const
canEditable
=
const
canEditable
=
permissions
[
PENDING_DELIVERY_ORDER
.
EDITABLE
]
||
permissions
[
DELIVERY_ORDER
.
EDITABLE
];
permissions
[
PENDING_DELIVERY_ORDER
.
EDITABLE
]
||
permissions
[
DELIVERY_ORDER
.
EDITABLE
];
...
@@ -44,6 +47,7 @@ const TableList = props => {
...
@@ -44,6 +47,7 @@ const TableList = props => {
const
[
endTime
,
setEndTime
]
=
useState
(
moment
());
const
[
endTime
,
setEndTime
]
=
useState
(
moment
());
const
[
orderStatus
,
setorderStatus
]
=
useState
(
1
);
const
[
orderStatus
,
setorderStatus
]
=
useState
(
1
);
const
[
orderNo
,
setOrderNo
]
=
useState
(
''
);
const
[
endTimeStr
,
setEndTimeStr
]
=
useState
(
moment
().
format
(
dateFormat
));
const
[
endTimeStr
,
setEndTimeStr
]
=
useState
(
moment
().
format
(
dateFormat
));
const
[
visibleDelay
,
setVisibleDelay
]
=
useState
(
false
);
const
[
visibleDelay
,
setVisibleDelay
]
=
useState
(
false
);
...
@@ -133,6 +137,15 @@ const TableList = props => {
...
@@ -133,6 +137,15 @@ const TableList = props => {
return
''
;
return
''
;
};
};
const
PopoverNotice
=
noticeProps
=>
{
const
{
time
,
content
}
=
noticeProps
||
{};
return
(
<
div
className=
{
style
[
'
popover-notice
'
]
}
>
{
time
}
:
{
content
}
</
div
>
);
};
const
columns
=
[
const
columns
=
[
{
{
title
:
'
订单ID
'
,
title
:
'
订单ID
'
,
...
@@ -141,6 +154,81 @@ const TableList = props => {
...
@@ -141,6 +154,81 @@ const TableList = props => {
order
:
6
,
order
:
6
,
width
:
250
,
width
:
250
,
fixed
:
'
left
'
,
fixed
:
'
left
'
,
formItemProps
:
{
value
:
orderNo
,
onChange
(
e
)
{
console
.
log
(
e
.
target
.
value
);
setOrderNo
(
e
.
target
.
value
);
},
},
render
:
(
_
,
record
)
=>
{
const
{
urgeDeliverGoodsList
}
=
record
;
const
content
=
()
=>
urgeDeliverGoodsList
?.
map
(
item
=>
(
<
PopoverNotice
time=
{
item
?.
time
}
content=
{
item
?.
content
}
/>
));
return
(
<
div
className=
{
style
[
'
urge-box
'
]
}
>
{
_
}
{
urgeDeliverGoodsList
?
(
<
Popover
placement=
"top"
title=
"催发货"
content=
{
<>
{
content
()
}
</>
}
trigger=
"hover"
>
<
span
className=
{
style
.
urge
}
>
催单
</
span
>
</
Popover
>
)
:
(
''
)
}
</
div
>
);
},
},
{
title
:
'
订单提醒
'
,
dataIndex
:
'
orderNotice
'
,
key
:
'
orderNotice
'
,
width
:
150
,
render
:
(
_
,
record
)
=>
{
const
{
updateAddressList
,
platformRemark
}
=
record
;
// let addressData;
// if (updateAddressList) {
// addressData = updateAddressList.pop();
// }
const
content
=
()
=>
(
<
div
className=
{
style
[
'
popover-notice-box
'
]
}
>
{
updateAddressList
?.
map
(
item
=>
(
<
PopoverNotice
time=
{
item
?.
time
}
content=
{
item
?.
content
}
/>
))
}
</
div
>
);
return
(
<
div
className=
{
style
[
'
notice-btn
'
]
}
>
{
updateAddressList
?
(
<
Popover
placement=
"top"
title=
"收货地址变更"
content=
{
content
()
}
trigger=
"hover"
>
<
Button
block
type=
"primary"
>
收货地址变更
</
Button
>
</
Popover
>
)
:
(
''
)
}
{
platformRemark
?
(
<
Popover
placement=
"top"
title=
"平台备注"
content=
{
<
PopoverNotice
time=
{
platformRemark
?.
time
}
content=
{
platformRemark
?.
content
}
/>
}
trigger=
"hover"
>
<
Button
block
type=
"warning"
>
平台备注
</
Button
>
</
Popover
>
)
:
(
''
)
}
</
div
>
);
},
},
},
{
{
title
:
'
商品名称
'
,
title
:
'
商品名称
'
,
...
@@ -156,7 +244,7 @@ const TableList = props => {
...
@@ -156,7 +244,7 @@ const TableList = props => {
hideInTable
:
true
,
hideInTable
:
true
,
formItemProps
:
{
formItemProps
:
{
// eslint-disable-next-line no-nested-ternary
// eslint-disable-next-line no-nested-ternary
value
:
orderStatus
?
(
orderStatus
===
1
?
'
是
'
:
'
否
'
)
:
''
,
value
:
orderStatus
?
(
orderStatus
===
1
?
'
是
'
:
'
否
'
)
:
null
,
onChange
(
status
)
{
onChange
(
status
)
{
setorderStatus
(
Number
(
status
));
setorderStatus
(
Number
(
status
));
},
},
...
@@ -364,6 +452,23 @@ const TableList = props => {
...
@@ -364,6 +452,23 @@ const TableList = props => {
}
}
},
[]);
},
[]);
// 监听路由query参数变化重新发起请求
useEffect
(()
=>
{
if
(
location
?.
query
)
{
const
{
orderNo
:
orderNumber
}
=
location
?.
query
;
if
(
orderNumber
)
{
setorderStatus
(
''
);
}
setOrderNo
(
orderNumber
);
if
(
ref
.
current
)
{
ref
.
current
.
resetFields
();
ref
.
current
.
submit
();
}
}
else
{
setOrderNo
(
''
);
}
},
[
location
]);
const
queryToSendFn
=
params
=>
{
const
queryToSendFn
=
params
=>
{
const
transformedParam
=
{
const
transformedParam
=
{
...
params
,
...
params
,
...
@@ -371,6 +476,7 @@ const TableList = props => {
...
@@ -371,6 +476,7 @@ const TableList = props => {
pageNo
:
params
.
current
,
pageNo
:
params
.
current
,
pageSize
:
params
.
pageSize
||
20
,
pageSize
:
params
.
pageSize
||
20
,
timeOutType
:
orderStatus
||
''
,
timeOutType
:
orderStatus
||
''
,
orderNo
,
};
};
return
queryToSend
(
transformedParam
);
return
queryToSend
(
transformedParam
);
};
};
...
@@ -393,6 +499,7 @@ const TableList = props => {
...
@@ -393,6 +499,7 @@ const TableList = props => {
// eslint-disable-next-line no-unused-expressions
// eslint-disable-next-line no-unused-expressions
form
?.
resetFields
();
form
?.
resetFields
();
setorderStatus
(
''
);
setorderStatus
(
''
);
setOrderNo
(
''
);
// eslint-disable-next-line no-unused-expressions
// eslint-disable-next-line no-unused-expressions
form
?.
submit
();
form
?.
submit
();
}
}
}
}
...
@@ -406,7 +513,11 @@ const TableList = props => {
...
@@ -406,7 +513,11 @@ const TableList = props => {
type=
"primary"
type=
"primary"
onClick=
{
()
=>
{
onClick=
{
()
=>
{
if
(
ref
.
current
)
{
if
(
ref
.
current
)
{
const
obj
=
{
...
ref
.
current
.
getFieldsValue
(),
logisticsStatus
:
props
.
type
||
1
};
const
obj
=
{
...
ref
.
current
.
getFieldsValue
(),
orderNo
,
logisticsStatus
:
props
.
type
||
1
,
};
obj
.
startTime
=
startTimeStr
;
obj
.
startTime
=
startTimeStr
;
obj
.
endTime
=
endTimeStr
;
obj
.
endTime
=
endTimeStr
;
downOrder
(
obj
);
downOrder
(
obj
);
...
...
src/pages/orderManage/pendingDeliveryOrder/index.less
View file @
d8defddc
@import '~antd/es/style/themes/default.less';
@import '~antd/es/style/themes/default.less';
.protable {
:global {
.tableContent {
display: flex;
align-items: center;
height: 60px;
padding: 16px;
}
.border {
border-bottom: 1px solid #e8e8e8;
}
tbody .colStyle {
padding: 0;
}
.ant-popover-title {
max-width: 300px;
overflow: hidden;
}
}
}
.tabpane {
:global {
.ant-tabs-content {
min-height: 200px;
max-height: 700px;
overflow: auto;
}
}
}
.btnWarning {
background-color: rgb(247, 143, 74);
border-color: rgb(247, 143, 74);
}
.btnWarning:hover,
.btnWarning:focus {
background-color: rgb(253, 168, 111);
border-color: rgb(253, 168, 111);
}
.tableList {
.tableList {
.tableListOperator {
.tableListOperator {
margin-bottom: 16px;
margin-bottom: 16px;
...
@@ -73,3 +112,42 @@
...
@@ -73,3 +112,42 @@
margin-right: 10px;
margin-right: 10px;
}
}
}
}
.notice-btn {
max-width: 120px;
margin: 0 auto;
text-align: center;
button {
margin: 5px 0;
}
}
// 催发货图标
.urge-box {
white-space: nowrap;
}
.urge {
display: inline-block;
width: 36px;
height: 17px;
margin-left: 5px;
color: #ff1515;
font-size: 12px;
line-height: 16px;
text-align: center;
border: 1px solid #ff1515;
border-radius: 18px;
cursor: pointer;
}
.popover-notice-box {
max-height: 300px;
margin-right: -15px;
padding-right: 15px;
overflow: auto;
}
.popover-notice {
max-width: 400px;
line-height: 32px;
word-break: break-all;
}
src/pages/orderManage/pendingDeliveryOrder/styles.less
deleted
100644 → 0
View file @
0fc293c8
.protable {
:global {
.tableContent {
display: flex;
align-items: center;
height: 60px;
padding: 16px;
}
.border {
border-bottom: 1px solid #e8e8e8;
}
tbody .colStyle {
padding: 0;
}
.ant-popover-title {
max-width: 300px;
overflow: hidden;
}
}
}
.tabpane {
:global {
.ant-tabs-content {
min-height: 200px;
max-height: 700px;
overflow: auto;
}
}
}
.btnWarning {
background-color: rgb(247, 143, 74);
border-color: rgb(247, 143, 74);
}
.btnWarning:hover,
.btnWarning:focus {
background-color: rgb(253, 168, 111);
border-color: rgb(253, 168, 111);
}
src/services/messageReminder.js
0 → 100644
View file @
d8defddc
import
request
from
'
@/utils/request
'
;
import
config
from
'
../../config/env.config
'
;
const
{
msgApi
,
wsApi
}
=
config
;
export
const
getSocketUrl
=
({
token
,
channelId
})
=>
`
${
wsApi
}
/ws?token=
${
token
}
&channelId=
${
channelId
}
`
;
/**
* @name 商户消息列表
* @see http://yapi.quantgroups.com/project/193/interface/api/41792
*/
export
function
apiGetBussinessMsgList
(
data
,
params
)
{
return
request
.
post
(
'
/v1/send/message/getBussinessMsgList
'
,
{
data
,
params
,
prefix
:
msgApi
,
});
}
/**
* @name 获取未读消息数量
* @see http://yapi.quantgroups.com/project/193/interface/api/42792
*/
export
function
apiGetBusinessMsgUnReadCount
(
data
)
{
return
request
.
post
(
'
/v1/send/message/getBusinessMsgUnReadCount
'
,
{
data
,
prefix
:
msgApi
,
});
}
/**
* @name 设置消息为已读
* @see http://yapi.quantgroups.com/project/193/interface/api/41808
*/
export
function
apiUpdageBusinessMsgStatus
(
data
)
{
return
request
.
post
(
'
/v1/send/message/updateBussinessMsgReadStatus
'
,
{
data
,
prefix
:
msgApi
,
});
}
src/utils/constants.js
View file @
d8defddc
export
const
APP_CODE
=
'
APP013
'
;
export
const
APP_CODE
=
'
APP013
'
;
export
const
TENANT_ID
=
560761
;
export
const
TENANT_ID
=
560761
;
export
const
CHANNEL_ID
=
100001
;
// 小程序权限标识符, 用于过滤掉返回的小程序权限数据
// 小程序权限标识符, 用于过滤掉返回的小程序权限数据
export
const
APPLETS_PERMISSION_IDENTIFIER
=
'
applets
'
;
export
const
APPLETS_PERMISSION_IDENTIFIER
=
'
applets
'
;
src/utils/utils.js
View file @
d8defddc
...
@@ -92,3 +92,21 @@ export function deepClone(obj) {
...
@@ -92,3 +92,21 @@ export function deepClone(obj) {
}
}
return
result
;
return
result
;
}
}
export
const
getClientInfo
=
()
=>
{
if
(
window
.
innerHeight
!==
undefined
)
{
return
{
width
:
window
.
innerWidth
,
height
:
window
.
innerHeight
,
};
}
if
(
document
.
compatMode
===
'
CSS1Compat
'
)
{
return
{
width
:
document
.
documentElement
.
clientWidth
,
height
:
document
.
documentElement
.
clientHeight
,
};
}
return
{
width
:
document
.
body
.
clientWidth
,
height
:
document
.
body
.
clientHeight
,
};
};
src/utils/websocket.js
0 → 100644
View file @
d8defddc
import
EventEmitter
from
'
events
'
;
import
{
Modal
}
from
'
antd
'
;
class
Socket
extends
EventEmitter
{
event
=
new
EventEmitter
();
constructor
(
options
)
{
super
();
this
.
options
=
options
;
this
.
reconnectCount
=
0
;
this
.
socket
=
null
;
this
.
taskRemindInterval
=
null
;
this
.
connected
=
false
;
this
.
waitingSendData
=
[];
this
.
reconnectCount
=
0
;
this
.
heartBeatTimer
=
null
;
return
this
;
}
connection
=
()
=>
{
const
{
url
,
timeout
=
0
}
=
this
.
options
;
// 检测当前浏览器是什么浏览器来决定用什么socket
if
(
'
WebSocket
'
in
window
)
{
console
.
log
(
'
WebSocket
'
);
this
.
socket
=
new
WebSocket
(
url
);
}
else
if
(
'
MozWebSocket
'
in
window
)
{
console
.
log
(
'
MozWebSocket
'
);
// eslint-disable-next-line no-undef
this
.
socket
=
new
MozWebSocket
(
url
);
}
else
{
console
.
log
(
'
SockJS
'
);
// eslint-disable-next-line no-undef
this
.
socket
=
new
SockJS
(
url
);
}
// 链接回调
this
.
socket
.
onopen
=
this
.
onopen
;
this
.
socket
.
onmessage
=
this
.
onmessage
;
this
.
socket
.
onclose
=
this
.
onclose
;
this
.
socket
.
onerror
=
this
.
onerror
;
this
.
socket
.
sendMessage
=
this
.
sendMessage
;
// 检测返回的状态码 如果socket.readyState不等于1则连接失败,关闭连接
if
(
timeout
)
{
const
time
=
setTimeout
(()
=>
{
if
(
this
.
socket
&&
this
.
socket
.
readyState
!==
1
)
{
console
.
log
(
'
主动触发关闭
'
);
this
.
socket
.
close
();
}
clearTimeout
(
time
);
},
timeout
);
}
};
// 连接成功触发
onopen
=
()
=>
{
const
{
heartBeatTime
}
=
this
.
options
;
console
.
log
(
'
ws: 连接成功
'
,
new
Date
().
getTime
());
this
.
connected
=
true
;
this
.
heartBeat
(
heartBeatTime
);
this
.
checkWaitingData
();
this
.
event
.
emit
(
'
open
'
);
};
// 后端向前端推得数据
onmessage
=
msg
=>
{
console
.
log
(
'
ws:接收数据:
'
,
msg
);
this
.
event
.
emit
(
'
message
'
,
msg
);
// 打印出后端推得数据
};
// 关闭连接触发
onclose
=
e
=>
{
console
.
log
(
'
ws: 关闭连接
'
,
new
Date
().
getTime
());
this
.
connected
=
false
;
// 关闭将标识符改为true
if
(
this
.
heartBeatTimer
)
{
clearTimeout
(
this
.
heartBeatTimer
);
}
this
.
event
.
emit
(
'
close
'
,
e
);
// 最多重连10次
if
(
this
.
reconnectCount
>
10
)
{
this
.
reconnectCount
=
0
;
return
;
}
const
reconnect
=
()
=>
{
if
(
this
.
taskRemindInterval
)
{
clearTimeout
(
this
.
taskRemindInterval
);
}
this
.
taskRemindInterval
=
setTimeout
(()
=>
{
if
(
!
this
.
connected
)
{
this
.
reconnectCount
++
;
this
.
connection
();
reconnect
();
}
},
5000
);
};
reconnect
();
};
onerror
=
e
=>
{
console
.
log
(
'
ws: error
'
,
e
);
this
.
socket
=
null
;
this
.
event
.
emit
(
'
error
'
,
e
);
};
sendMessage
=
value
=>
{
console
.
log
(
'
ws: send
'
,
value
);
// 向后端发送数据
if
(
this
.
socket
)
{
if
(
!
this
.
connected
)
{
this
.
waitingSendData
.
unshift
(
value
);
return
;
}
const
sendValue
=
typeof
value
===
'
string
'
?
value
:
JSON
.
stringify
(
value
);
this
.
socket
.
send
(
sendValue
);
}
};
checkWaitingData
()
{
console
.
log
(
'
ws: checkWaitingData
'
,
this
.
waitingSendData
);
if
(
this
.
waitingSendData
.
length
)
{
this
.
sendMessage
(
this
.
waitingSendData
.
splice
(
0
,
1
));
this
.
checkWaitingData
();
}
}
// 保持连接-默认每3分钟重置一下服务器关闭时间
heartBeat
(
time
)
{
console
.
log
(
'
ws: call heartBeat
'
,
new
Date
().
getTime
());
this
.
heartBeatTimer
=
setTimeout
(()
=>
{
console
.
log
(
'
ws: sent heart beat
'
,
new
Date
().
getTime
());
this
.
sendMessage
(
'
HeartBeat
'
);
this
.
heartBeat
(
time
);
},
time
||
45000
);
}
}
export
default
Socket
;
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