Commit 9b7e504d authored by FE-安焕焕's avatar FE-安焕焕 👣

Merge branch 'afterSale' into 'master'

After sale

See merge request !8
parents 44d8adbc 49362fba
......@@ -122,6 +122,29 @@ export default {
name: 'settleManage',
component: './settleManage',
},
{
path: '/afterSaleManage',
name: 'afterSaleManage',
icon: 'smile',
component: './AfterSaleManage/index',
},
{
path: '/auditPending',
name: 'auditPending',
icon: 'smile',
component: './AfterSaleManage/Pending',
},
{
path: '/passAudit',
name: 'passAudit',
icon: 'smile',
component: './AfterSaleManage/PassAudit',
},
{
path: '/appeal',
name: 'appeal',
component: './afterSale/appeal',
},
{
component: './404',
},
......
......@@ -2,13 +2,13 @@ const isProduction = process.env.NODE_ENV === 'production';
let envAPi = {
// api: '//backstms-vcc3.liangkebang.net',
api: '//backstms-vcc2.liangkebang.net',
api: '//backstms-test2.liangkebang.net',
// kdspApi: '//yapi.quantgroups.com/mock/351',
// kdspApi: 'http://192.168.28.172:8042',
// kdspApi: 'https://kdsp-operation-vcc3.liangkebang.net',
kdspApi: 'https://kdsp-operation-vcc2.liangkebang.net',
kdspApi: 'https://kdsp-operation-test2.liangkebang.net',
qiniuHost: 'https://appsync.lkbang.net',
opapiHost: 'https://opapi-vcc2.liangkebang.net',
opapiHost: 'https://opapi-test2.liangkebang.net',
};
let prodApi = {
......
import React, { useState, useRef } from 'react';
import { Button, notification, Popconfirm } from 'antd';
import ProTable from '@ant-design/pro-table';
import { searchList, shopCheck, orderDetail, trackInfo } from '../services';
import { getDetail } from '../../afterSale/appeal/services';
import { columnPassAudit } from '../data';
import RejectModal from '../components/rejectModal';
import DetailTable from '../components/detailTable';
import ProofsModal from '../components/proofsModal';
import LogisticsCom from '../../orderManage/pendingDeliveryOrder/components/LogisticsCom';
import AppealDetail from '../../afterSale/components/detail';
export default () => {
const table = useRef();
const [visible, setVisible] = useState(false);
const [detailVisible, setDetailVisible] = useState(false);
const [detailInfo, setDetailInfo] = useState([]);
const [proofsVisible, setProofsVisible] = useState(false);
const [proofsData, setProofsData] = useState([]);
const [serviceNoInfo, setServiceNoInfo] = useState({});
const [LogisticsComList, setLogisticsComList] = useState({});
const [LogisticsComModalVisible, handleComModalVisible] = useState(false);
const [appealDetailModal, setAppealDetailModal] = useState(false);
const [selectedRow, setSelectedRow] = useState({});
const viewDetail = async ({ orderNo }) => {
const data = await orderDetail({ orderNo });
setDetailInfo(data || []);
setDetailVisible(true);
};
const closeModal = isReload => {
if (isReload === true) {
// eslint-disable-next-line no-unused-expressions
table.current?.reload?.();
}
setVisible(false);
setDetailVisible(false);
setProofsVisible(false);
handleComModalVisible(false);
setAppealDetailModal(false);
};
const reject = async ({ serviceNo }) => {
setServiceNoInfo(serviceNo);
setVisible(true);
};
const viewProofs = proofs => {
if (!proofs) {
notification.warning({ message: '该订单没有凭证' });
return;
}
setProofsData(proofs.split(','));
setProofsVisible(true);
};
const refund = async ({ serviceNo }) => {
const data = await shopCheck({
serviceNo,
auditResult: 1,
});
if (data.businessCode === '0000') {
notification.success({ message: '操作成功' });
closeModal(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];
});
}
handleComModalVisible(true);
setLogisticsComList(tempObj);
};
const viewAppeal = async r => {
const detailData = await getDetail({ appealNo: r.appealNo });
setAppealDetailModal(true);
setSelectedRow(detailData);
};
const columns = [
...columnPassAudit,
{
title: '售后凭证',
dataIndex: 'proofs',
hideInSearch: true,
width: 120,
order: 4,
render: proofs => <a onClick={() => viewProofs(proofs)}>查看凭证</a>,
},
{
title: '售后申诉',
dataIndex: 'appealFlag',
hideInSearch: true,
width: 120,
render: (appealFlag, r) => {
if (appealFlag) {
return <a onClick={() => viewAppeal(r)}>已申诉</a>;
}
return <div> 未申诉</div>;
},
},
{
title: '操作',
hideInSearch: true,
width: 450,
dataIndex: 'action',
fixed: 'right',
render: (_, r) => [
<Popconfirm
title="确定允许退款?"
onConfirm={() => refund(r)}
okText="确认"
cancelText="取消"
key="pop"
disabled={!r.showRefund}
>
<Button key="link1" className="mr10" type="primary" disabled={!r.showRefund}>
{r.showRefunded ? '已退款' : '允许退款'}
</Button>
</Popconfirm>,
<Button
key="link2"
onClick={() => reject(r)}
type="primary"
className="mr10"
disabled={!r.showRefuse}
>
驳回
</Button>,
<Button key="link3" onClick={() => viewDetail(r)} type="primary" className="mr10">
订单详情
</Button>,
<Button
key="link4"
onClick={() => handleCom(r)}
type="primary"
className="mr10"
disabled={!r.showLogistics}
>
查看物流
</Button>,
],
},
];
return (
<div>
<ProTable
columns={columns}
request={params => searchList(params, 2)}
rowKey="serviceNo"
pagination={{
pagesSize: 20,
}}
bordered
actionRef={table}
scroll={{ x: '100%', y: 400 }}
toolBarRender={false}
search={{
collapsed: false,
collapseRender: () => null,
}}
/>
<RejectModal visible={visible} onCancel={closeModal} serviceNo={serviceNoInfo} />
<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>
</div>
);
};
import React, { useState, useRef } from 'react';
import { notification, Button } from 'antd';
import ProTable from '@ant-design/pro-table';
import { searchList, auditInfoApi, orderDetail } from '../services';
import { columnSticData, appealType } from '../data';
import AuditModal from '../components/auditModal';
import DetailTable from '../components/detailTable';
import ProofsModal from '../components/proofsModal';
import { getDetail } from '../../afterSale/appeal/services';
import AppealDetail from '../../afterSale/components/detail';
export default () => {
const table = useRef();
const [visible, setVisible] = useState(false);
const [detailVisible, setDetailVisible] = useState(false);
const [detailInfo, setDetailInfo] = useState([]);
const [proofsVisible, setProofsVisible] = useState(false);
const [proofsData, setProofsData] = useState([]);
const [auditInfo, setAuditInfo] = useState({});
const [appealDetailModal, setAppealDetailModal] = useState(false);
const [selectedRow, setSelectedRow] = useState({});
const viewDetail = async ({ orderNo }) => {
const data = await orderDetail({ orderNo });
setDetailInfo(data || []);
setDetailVisible(true);
};
const closeModal = isReload => {
if (isReload === true) {
// eslint-disable-next-line no-unused-expressions
table.current?.reload?.();
}
setVisible(false);
setDetailVisible(false);
setProofsVisible(false);
setAppealDetailModal(false);
};
const openAudit = async ({ serviceNo }) => {
const data = await auditInfoApi({ serviceNo });
setAuditInfo({ ...data?.data, serviceNo });
setVisible(true);
};
const viewProofs = proofs => {
if (!proofs) {
notification.warning({ message: '该订单没有凭证' });
return;
}
setProofsData(proofs.split(','));
setProofsVisible(true);
};
const viewAppeal = async r => {
const detailData = await getDetail({ appealNo: r.appealNo });
setAppealDetailModal(true);
setSelectedRow(detailData);
};
const columns = [
...columnSticData,
{
title: '售后凭证',
dataIndex: 'proofs',
hideInSearch: true,
width: 100,
render: proofs => <a onClick={() => viewProofs(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: '售后状态',
dataIndex: 'serviceStatus',
hideInSearch: true,
width: 100,
},
{
title: '操作',
hideInSearch: true,
dataIndex: 'action',
width: 250,
fixed: 'right',
render: (_, r) => [
<Button key="link1" onClick={() => openAudit(r)} className="mr10" type="primary">
审核
</Button>,
<Button key="link" onClick={() => viewDetail(r)} type="primary">
订单详情
</Button>,
],
},
];
return (
<div>
<ProTable
columns={columns}
request={params => searchList(params, 1)}
rowKey="serviceNo"
pagination={{
pagesSize: 20,
}}
bordered
actionRef={table}
scroll={{ x: '100%', y: 400 }}
search={{
collapsed: false,
collapseRender: () => null,
}}
toolBarRender={false}
/>
<AuditModal visible={visible} onCancel={closeModal} formData={auditInfo} />
<DetailTable visible={detailVisible} onCancel={closeModal} dataSource={detailInfo} />
<ProofsModal visible={proofsVisible} onCancel={closeModal} data={proofsData} />
<AppealDetail
data={selectedRow}
modalVisible={appealDetailModal}
onCancel={closeModal}
></AppealDetail>
</div>
);
};
import React from 'react';
import { Modal, Form, Input, Cascader, notification } from 'antd';
import { shopAudit } 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: 1,
},
{
label: '不同意',
value: 2,
children: [
{
label: '商品与出售商品不符',
value: 1,
},
{
label: '影响二次销售',
value: 2,
},
{
label: '其他',
value: 3,
},
],
},
];
const handleOk = () => {
validateFields(async (error, fieldsValue) => {
if (!error) {
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 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()}
>
<Form {...layout} name="formData">
<FormItem label="审核结果">
{getFieldDecorator('auditResult')(
<Cascader
allowClear
showSearch
style={{ width: '315px' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
options={treeData}
placeholder="请选择"
/>,
)}
</FormItem>
{isAgree() && (
<div>
<FormItem label="退货地址">
{getFieldDecorator('receiveAddress', {
initialValue: formData.address,
rules: [
{
required: true,
message: '请填写退货地址!',
},
],
})(<Input placeholder="请填写退货地址" allowClear />)}
</FormItem>
<FormItem label="收件人">
{getFieldDecorator('receiverName', {
initialValue: formData.name,
rules: [
{
required: true,
message: '请填写收件人!',
},
],
})(<Input placeholder="请填写收件人" allowClear />)}
</FormItem>
<FormItem label="手机号码">
{getFieldDecorator('receiverPhone', {
initialValue: formData.phone,
rules: [
{
required: true,
message: '请填写手机号码!',
},
],
})(<Input placeholder="请填写手机号码" 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 }}
/>,
)}
</FormItem>
)}
</Form>
</Modal>
);
};
export default Form.create()(AuditModal);
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>
);
};
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>
);
};
import React from 'react';
import { Modal, Form, 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);
import { Tag, Badge } from 'antd';
import React from 'react';
export const appealType = {
1: '已申诉',
0: '未申诉',
};
export const columnSticData = [
{
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: '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: 'serviceType',
hideInSearch: true,
width: 120,
valueEnum: {
1: '退款不退货',
2: '退货退款',
},
},
{
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,
},
];
import { Tabs } from 'antd';
import React from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import Pending from './Pending';
import PassAudit from './PassAudit';
const { TabPane } = Tabs;
export default function AfterSale() {
return (
<PageHeaderWrapper>
<Tabs defaultActiveKey="1">
<TabPane tab="未审核" key="1">
<Pending />
</TabPane>
<TabPane tab="已审核" key="2">
<PassAudit />
</TabPane>
</Tabs>
</PageHeaderWrapper>
);
}
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/order/skus', {
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: stringify(params),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
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 {};
}
.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;
}
import React, { useState, useRef } from 'react';
import ProTable from '@ant-design/pro-table';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { notification } from 'antd';
import Detail from '../components/detail';
import ImgModal from '../components/imgModal';
import styles from './styles.less';
import { columns } from './staticdata';
import { query, getDetail } from './services';
import AuditModal from '../components/audit';
const Appeal = () => {
const [detailModal, changeDetailModal] = useState(false);
const [auditModal, changeAuditModal] = useState(false);
const [imgModal, changeImgModal] = useState(false);
const [selectedRow, getRow] = useState({});
const [imgData, getImgData] = useState([]);
const [imgTitle, changeImgTitle] = useState('');
const table = useRef();
const openDetail = async row => {
const detailData = await getDetail({ appealNo: row.appealNo });
changeDetailModal(true);
getRow(detailData);
};
const openAudit = async row => {
const detailData = await getDetail({ appealNo: row.appealNo });
getRow(detailData);
changeAuditModal(true);
};
const reload = () => {
if (table && table.current) {
table.current.reload();
}
};
const openImgModal = imgList => {
if (!imgList.length) {
notification.error({
message: '啊哦~没有数据!',
});
return;
}
getImgData(imgList);
changeImgModal(true);
changeImgTitle('查看图片');
};
const closeModal = (params, isReload) => {
if (params === 'Detail') {
changeDetailModal(false);
return;
}
if (params === 'Audit') {
changeAuditModal(false);
if (isReload) {
reload();
}
return;
}
changeImgModal(false);
};
const res = {
openAudit,
openDetail,
openImgModal,
};
return (
<PageHeaderWrapper>
<ProTable
search={{
collapsed: false,
collapseRender: () => null,
}}
columns={columns(res)}
request={params => query({ ...params })}
rowKey={r => r.orderNo}
expandIconColumnIndex={10}
actionRef={table}
bordered
className={styles.table}
toolBarRender={false}
scroll={{ x: '100%', y: 400 }}
/>
<Detail
data={selectedRow}
modalVisible={detailModal}
onCancel={() => closeModal('Detail')}
></Detail>
<AuditModal
data={selectedRow}
modalVisible={auditModal}
onCancel={isReload => closeModal('Audit', isReload)}
></AuditModal>
<ImgModal
imgData={imgData}
modalVisible={imgModal}
title={imgTitle}
onCancel={() => closeModal('imgModal')}
></ImgModal>
</PageHeaderWrapper>
);
};
export default Appeal;
import { stringify } from 'querystring';
import _ from 'lodash';
import request from '@/utils/request';
import config from '../../../../config/env.config';
export async function query(param) {
const params = {
...param,
pageNo: param.current,
};
const data = await request.post('/api/kdsp/op/appeal/list', {
// prefix: 'http://yapi.quantgroups.com/mock/351',
prefix: config.kdspApi,
data: stringify(_.omitBy(params, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
if (data.businessCode === '0000') {
return { data: data.data.records || [], total: data.data.total };
}
return [];
}
export async function getDetail(params) {
const { data } = await request.post('/api/kdsp/op/appeal/info', {
data: stringify(_.omitBy(params, v => !v)),
prefix: config.kdspApi,
// prefix: 'http://yapi.quantgroups.com/mock/351',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return data;
}
export async function audit(params) {
const data = await request.post('/api/kdsp/op/appeal/audit', {
data: stringify(params),
prefix: config.kdspApi,
// prefix: 'http://yapi.quantgroups.com/mock/351',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return data;
}
import { Button, Tag } from 'antd';
import React from 'react';
export const refuseReasonEnum = {
0: { text: '待操作' },
1: { text: '已同意' },
2: { text: '已拒绝' },
};
export function columns(res) {
const { openAudit, openDetail, openImgModal } = res;
return [
{
title: '售后申诉单ID',
width: 300,
dataIndex: 'appealNo',
key: 'appealNo',
// eslint-disable-next-line no-confusing-arrow
render: (appealNo, row) =>
row.timeout ? (
<Tag color="red" key={appealNo}>
{appealNo}
</Tag>
) : (
appealNo
),
},
{
title: '售后单ID',
width: 300,
dataIndex: 'serviceNo',
key: 'serviceNo',
},
{
title: '订单ID',
width: 200,
dataIndex: 'orderNo',
key: 'orderNo',
},
{
title: '申诉单开始时间',
width: 120,
dataIndex: 'startDate',
key: 'startDate',
valueType: 'date',
hideInTable: true,
},
{
title: '申诉单结束时间',
width: 120,
dataIndex: 'endDate',
key: 'endDate',
valueType: 'date',
hideInTable: true,
},
{
title: '商家名称',
width: 120,
dataIndex: 'merchantName',
key: 'merchantName',
hideInSearch: true,
},
{
title: '商家手机号',
width: 120,
dataIndex: 'merchantPhone',
key: 'merchantPhone',
hideInSearch: true,
},
{
title: '商家退款地址',
width: 120,
dataIndex: 'merchantAddress',
key: 'merchantAddress',
hideInSearch: true,
},
{
title: '是否同意售后',
width: 120,
dataIndex: 'isAgree',
key: 'isAgree',
hideInSearch: true,
},
{
title: '商家拒绝原因',
width: 120,
dataIndex: 'serviceReason',
key: 'serviceReason',
hideInSearch: true,
},
{
title: '售后凭证',
width: 120,
dataIndex: 'proofs',
key: 'proofs',
hideInSearch: true,
render: proofs => {
const list = proofs && proofs !== '-' ? proofs.split(',') : [];
return <a onClick={() => openImgModal(list)}>查看凭证</a>;
},
},
{
title: '申诉单创建时间',
width: 120,
dataIndex: 'appealTime',
key: 'appealTime',
hideInSearch: true,
},
{
title: '申诉单超时时间',
width: 120,
dataIndex: 'overtime',
key: 'overtime',
hideInSearch: true,
},
{
title: '争议问题描述',
width: 120,
dataIndex: 'disputeDesc',
key: 'disputeDesc',
hideInSearch: true,
},
{
title: '补充资料',
width: 120,
dataIndex: 'supplement',
key: 'supplement',
hideInSearch: true,
render: supplement => {
const list = supplement && supplement !== '-' ? supplement.split(',') : [];
return <a onClick={() => openImgModal(list)}>查看补充资料</a>;
},
},
{
title: '协商结果',
width: 120,
dataIndex: 'appealResult',
key: 'appealResult',
hideInSearch: true,
valueEnum: refuseReasonEnum,
},
{
title: '售后申诉拒绝原因',
width: 120,
dataIndex: 'refuseReason',
key: 'refuseReason',
hideInSearch: true,
},
{
title: '操作',
width: 220,
dataIndex: 'option',
valueType: 'option',
key: 'option',
render: (_, row) => [
<Button disabled={!row.showAudit} type="primary" onClick={() => openAudit(row)}>
审核
</Button>,
<Button disabled={!row.showDetail} type="primary" onClick={() => openDetail(row)}>
查看详情
</Button>,
],
},
];
}
.table {
:global {
.ant-table-wrapper {
margin: 20px;
}
}
}
import React from 'react';
import { Modal, Row, Col, Input, Form, Select, notification } from 'antd';
import { audit } from '../appeal/services';
const { TextArea } = Input;
const FormItem = Form.Item;
const { Option } = Select;
const AuditModal = props => {
const {
modalVisible,
onCancel,
data,
form: { getFieldDecorator, getFieldValue, validateFields, resetFields },
} = props;
const handleOk = () => {
// 掉接口
// 成功后掉取消接口
validateFields(async (error, fieldsValue) => {
if (!error) {
const auditData = await audit({
...fieldsValue,
appealNo: data.appealNo,
});
if (auditData && auditData.code === '0000') {
notification.success({ message: '审核成功' });
resetFields();
onCancel(true);
}
}
});
};
const layout = {
labelCol: { span: 3 },
wrapperCol: { span: 15 },
};
const isAgree = () => getFieldValue('appealResult') === '1';
const isRefuse = () => getFieldValue('appealResult') && getFieldValue('appealResult') !== '1';
return (
<Modal
destroyOnClose
title="订单审核"
width="800px"
visible={modalVisible}
onCancel={() => onCancel()}
onOk={() => handleOk()}
bodyStyle={{ maxHeight: '800px', minHeight: '200px', overflow: 'auto' }}
>
{data ? (
<div>
<Row gutter={[10, 20]}>
<Col span={12}>
<p>申诉单ID: {data.appealNo}</p>
</Col>
<Col span={12}>
<p>订单ID: {data.orderNo}</p>
</Col>
</Row>
<Row gutter={[10, 20]}>
<Col span={12}>
<p>售后单ID: {data.serviceNo}</p>
</Col>
<Col span={12}>
<p>商家名称: {data.merchantName}</p>
</Col>
</Row>
<Row gutter={[10, 20]}>
<Col span={12}>
<p>商家手机号: {data.merchantPhone}</p>
</Col>
<Col span={12}>
<p>商家退货地址: {data.merchantAddress}</p>
</Col>
</Row>
<hr style={{ border: 'none', borderTop: '1px solid #eee', margin: '30px 0' }} />
<Row type="flex" gutter={[10, 20]}>
<Col span={13}>
<p>是否同意售后: {data.isAgree}</p>
</Col>
</Row>
<Row type="flex" gutter={[10, 20]} style={{ marginBottom: '10px' }}>
<Col span={4}>商家拒绝原因:</Col>
<Col span={20}>
<p style={{ width: '70%', marginLeft: '-25px' }}> {data.serviceReason}</p>
</Col>
</Row>
<Row type="flex" justify="start" align="middle" gutter={[10, 20]}>
<Col span={12}>争议问题描述: {data.disputeDesc}</Col>
{/* <Col span={16}>
<TextArea value={data.disputeDesc} />
</Col> */}
</Row>
<hr style={{ border: 'none', borderTop: '1px solid #eee', margin: '30px 0' }} />
<Row type="flex" align="middle" gutter={[10, 20]} style={{ minHeight: '100px' }}>
<Col>售后凭证: </Col>
{data.proofs &&
data.proofs.split(',').map(item => (
<Col key={item} span={4}>
<img key={item} width="100%" src={item} alt="" />
</Col>
))}
</Row>
<Row
type="flex"
justify="start"
align="middle"
gutter={[10, 20]}
style={{ minHeight: '100px' }}
>
<Col span={3}>补充资料:</Col>
{data.supplement &&
data.supplement.split(',').map(item => (
<Col key={item} span={8}>
<img width="100%" src={item} key={item} alt="" />
</Col>
))}
</Row>
<hr style={{ border: 'none', borderTop: '1px solid #eee', margin: '30px 0' }} />
<Row gutter={[10, 20]}>
<Form {...layout} name="formData">
<FormItem label="审核结果">
{getFieldDecorator('appealResult')(
<Select placeholder="选择审核结果">
<Option value="1">同意</Option>
<Option value="2">不同意</Option>
</Select>,
)}
</FormItem>
{isAgree() && (
<div>
<FormItem label="退货地址">
{getFieldDecorator('receiveAddress', {
rules: [
{
required: true,
message: '请填写退货地址!',
},
],
})(<Input placeholder="请填写退货地址" allowClear />)}
</FormItem>
<FormItem label="收件人">
{getFieldDecorator('receiverName', {
rules: [
{
required: true,
message: '请填写收件人!',
},
],
})(<Input placeholder="请填写收件人" allowClear />)}
</FormItem>
<FormItem label="手机号码">
{getFieldDecorator('receiverPhone', {
rules: [
{
required: true,
message: '请填写手机号码!',
},
],
})(<Input placeholder="请填写手机号码" allowClear />)}
</FormItem>
</div>
)}
{isRefuse() && (
<FormItem label="拒绝原因">
{getFieldDecorator('refuseReason', {
rules: [
{
required: true,
message: '请填写拒绝原因!',
},
],
})(
<TextArea
placeholder="请填写拒绝原因"
allowClear
autoSize={{ minRows: 3, maxRows: 6 }}
/>,
)}
</FormItem>
)}
</Form>
</Row>
</div>
) : (
'暂无详情信息'
)}
</Modal>
);
};
export default Form.create()(AuditModal);
import React from 'react';
import { Modal, Row, Col } from 'antd';
const AppealDetail = props => {
const { modalVisible, onCancel, data } = props;
return (
<Modal
destroyOnClose
title="查看详情"
width="800px"
visible={modalVisible}
onCancel={() => onCancel()}
onOk={() => onCancel()}
bodyStyle={{ maxHeight: '600px', minHeight: '200px', overflow: 'auto' }}
footer={[]}
>
{data ? (
<div>
<Row gutter={[10, 20]}>
<Col span={12}>
<p>申诉单ID:{data.appealNo}</p>
</Col>
<Col span={12}>
<p>订单ID:{data.orderNo}</p>
</Col>
</Row>
<Row gutter={[10, 20]}>
<Col span={12}>
<p>售后单ID:{data.serviceNo}</p>
</Col>
<Col span={12}>
<p>商家名称:{data.merchantName}</p>
</Col>
</Row>
<Row gutter={[10, 20]}>
<Col span={12}>
<p>商家手机号:{data.merchantPhone}</p>
</Col>
<Col span={12}>
<p>商家退货地址:{data.merchantAddress}</p>
</Col>
</Row>
<hr style={{ border: 'none', borderTop: '1px solid #eee', margin: '30px 0' }} />
<Row type="flex" gutter={[10, 20]}>
<Col span={13}>
<p>是否同意售后: {data.isAgree}</p>
</Col>
</Row>
<Row type="flex" gutter={[10, 20]} style={{ marginBottom: '10px' }}>
<Col span={4}>商家拒绝原因:</Col>
<Col span={20}>
<p style={{ width: '70%', marginLeft: '-25px' }}> {data.serviceReason}</p>
</Col>
</Row>
<Row type="flex" justify="start" align="middle" gutter={[10, 20]}>
<Col span={12}>争议问题描述: {data.disputeDesc}</Col>
{/* <Col span={16}>
<TextArea value={data.disputeDesc} />
</Col> */}
</Row>
<hr style={{ border: 'none', borderTop: '1px solid #eee', margin: '30px 0' }} />
<Row type="flex" align="middle" gutter={[10, 20]} style={{ minHeight: '100px' }}>
<Col>售后凭证: </Col>
{data.proofs &&
data.proofs.split(',').map(item => (
<Col key={item} span={4}>
<img key={item} width="100%" src={item} alt="" />
</Col>
))}
</Row>
<Row
type="flex"
justify="start"
align="middle"
gutter={[10, 20]}
style={{ minHeight: '100px' }}
>
<Col span={3}>补充资料:</Col>
{data.supplement &&
data.supplement.split(',').map(item => (
<Col key={item} span={8}>
<img width="100%" src={item} key={item} alt="" />
</Col>
))}
</Row>
<hr style={{ border: 'none', borderTop: '1px solid #eee', margin: '30px 0' }} />
<Row type="flex" justify="start" align="middle" gutter={[10, 20]}>
<Col span={3}>协商结果:</Col>
<Col span={8}>{+data.appealResult === 1 ? '同意' : '不同意'}</Col>
</Row>
{+data.appealResult === 2 && (
<Row type="flex" justify="start" align="middle" gutter={[10, 20]}>
<Col span={5}>售后申诉拒绝原因:</Col>
<Col span={10} style={{ marginLeft: '-20px' }}>
{data.refuseReason}
</Col>
</Row>
)}
{+data.appealResult === 1 && [
<Row type="flex" justify="start" align="middle" gutter={[10, 30]}>
<Col span={4}>商家退货地址:</Col>
<Col span={16}>{data.merchantAddress}</Col>
</Row>,
<Row type="flex" justify="start" align="middle" gutter={[10, 20]}>
<Col span={3}>商家姓名:</Col>
<Col span={8}>{data.merchantAddress}</Col>
<Col span={3}>商家手机号:</Col>
<Col span={8}>{data.merchantPhone}</Col>
</Row>,
]}
</div>
) : (
'暂无详情信息'
)}
</Modal>
);
};
export default AppealDetail;
import { Modal, Button, Row, Col } from 'antd';
import React, { Component } from 'react';
// eslint-disable-next-line react/prefer-stateless-function
class imgModal extends Component {
state = {
imgModal: false,
selectImg: '',
};
render() {
const { modalVisible, title, imgData = [] } = this.props;
return (
<Modal
title={title}
visible={modalVisible}
footer={null}
onCancel={this.props.onCancel}
width="550px"
bodyStyle={{ minHeight: '150px', textAlign: 'center' }}
>
<Row type="flex" justify="center" align="middle" gutter={20}>
{imgData.map(item => (
<Col key={item}>
<img
onClick={() => {
this.setState({ imgModal: true, selectImg: item });
}}
key={item}
width={150}
alt=""
src={item}
></img>
</Col>
))}
</Row>
<Button type="primary" onClick={this.props.onCancel} style={{ margin: 100 }}>
取消
</Button>
<Modal
title="附件详情"
visible={this.state.imgModal}
footer={null}
onCancel={() => this.setState({ imgModal: false })}
width="800px"
style={{ textAlign: 'center' }}
>
<img width="700px" src={this.state.selectImg} alt=""></img>
</Modal>
</Modal>
);
}
}
export default imgModal;
......@@ -86,7 +86,6 @@ class PicturesWall extends React.Component {
render() {
const { previewVisible, previewImage, fileList } = this.state;
console.log('fileList---render', fileList);
const uploadButton = (
<div>
<Icon type="plus" />
......
......@@ -13,7 +13,11 @@ const LogisticsCom = props => {
return (
<Modal
destroyOnClose
title={`${result?.expressCompanyName}-${result?.deliveryNo}`}
title={`${
result?.expressCompanyName
? `${result?.expressCompanyName}-${result?.deliveryNo}`
: '物流信息'
}`}
visible={modalVisible}
onCancel={() => onCancel()}
onOk={() => onCancel()}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment