Commit 82c7b247 authored by 张子雨's avatar 张子雨

Feat/aging2.0

parent 69a1c77f
......@@ -79,6 +79,7 @@ export default {
{
path: '/',
component: '../layouts/SecurityLayout',
title: '商户管理后台',
routes: [
{
path: '/',
......@@ -86,73 +87,87 @@ export default {
authority: ['Admin', 'user'],
routes: [
{
title: '首页',
path: '/',
component: './Admin',
},
{
title: '商户管理后台',
path: '/orderManage/pendingDeliveryOrder',
name: 'pendingDeliveryOrder',
component: './orderManage/pendingDeliveryOrder',
},
{
title: '商户管理后台',
path: '/orderManage/deliveryOrder',
name: 'deliveryOrder',
component: './orderManage/deliveryOrder',
},
{
title: '商户管理后台',
path: '/orderManage/batchDelivery',
name: 'batchDeliveryOrder',
component: './orderManage/batchDelivery',
},
{
title: '商户管理后台',
path: '/afterSaleAddress',
name: 'afterSaleAddress',
component: './afterSaleAddress',
},
{
title: '商户管理后台',
path: '/reconciliation',
name: 'reconciliation',
component: './reconciliation',
},
{
title: '商户管理后台',
path: '/reconciliation/detail',
name: 'reconciliationDetail',
component: './reconciliation/detail/index',
},
{
title: '商户管理后台',
path: '/settleManage',
name: 'settleManage',
component: './settleManage',
},
{
title: '商户管理后台',
path: '/afterSaleManage',
name: 'afterSaleManage',
icon: 'smile',
component: './AfterSaleManage/index',
},
{
title: '商户管理后台',
path: '/auditPending',
name: 'auditPending',
icon: 'smile',
component: './AfterSaleManage/Pending',
},
{
title: '商户管理后台',
path: '/passAudit',
name: 'passAudit',
icon: 'smile',
component: './AfterSaleManage/PassAudit',
},
{
title: '商户管理后台',
path: '/appeal',
name: 'appeal',
component: './afterSale/appeal',
},
{
title: '商户管理后台',
path: '/cancelBillManage',
name: 'cancelBillManage',
component: './cancelBillManage',
},
{
title: '商户管理后台',
path: '/goodsManage',
name: 'goodsManage',
icon: 'smile',
......@@ -165,18 +180,21 @@ export default {
component: './distributionArea',
},
{
title: '商户管理后台',
path: '/reconciliationQuery',
name: 'reconciliationQuery',
icon: 'smile',
component: './ReconciliationQuery',
},
{
title: '商户管理后台',
path: '/settlementSheet',
name: 'settlementSheet',
icon: 'smile',
component: './SettlementSheet',
},
{
title: '商户管理后台',
path: '/paymentMange',
name: 'paymentMange',
icon: 'smile',
......
......@@ -5,7 +5,6 @@
"requires": true,
"packages": {
"": {
"name": "merchant-manage-ui",
"version": "1.0.0",
"dependencies": {
"@ant-design/colors": "^3.1.0",
......@@ -78,7 +78,7 @@ const Admin = () => {
}}
>
<strong>{pendingNum?.afterOrderPendingAuditNum || 0}</strong>
<p>退款待审核</p>
<p>退款待审核{'<'}24小时</p>
</Link>
</Col>
</Row>
......
......@@ -110,7 +110,7 @@ export default () => {
{
title: '操作',
hideInSearch: true,
width: 450,
width: 300,
dataIndex: 'action',
fixed: 'right',
render: (_, r) => [
......@@ -122,7 +122,7 @@ export default () => {
key="pop"
disabled={!r.showRefund}
>
<Button key="link1" className="mr10" type="primary" disabled={!r.showRefund}>
<Button key="link1" className="mr10 mt10" type="primary" disabled={!r.showRefund}>
{r.showRefunded ? '已退款' : '允许退款'}
</Button>
</Popconfirm>,
......@@ -130,19 +130,19 @@ export default () => {
key="link2"
onClick={() => reject(r)}
type="primary"
className="mr10"
className="mr10 mt10"
disabled={!r.showRefuse}
>
驳回
</Button>,
<Button key="link3" onClick={() => viewDetail(r)} type="primary" className="mr10">
<Button key="link3" onClick={() => viewDetail(r)} type="primary" className="mr10 mt10">
订单详情
</Button>,
<Button
key="link4"
onClick={() => handleCom(r)}
type="primary"
className="mr10"
className="mr10 mt10"
disabled={!r.showLogistics}
>
查看物流
......
import React, { useState, useRef } from 'react';
import { notification, Button } from 'antd';
import { notification, Button, Popconfirm } from 'antd';
import ProTable from '@ant-design/pro-table';
import moment from 'moment';
import _ from 'lodash';
import { searchList, auditInfoApi, orderDetail } from '../services';
import { columnSticData, appealType } from '../data';
import AuditModal from '../components/auditModal';
......@@ -19,7 +21,8 @@ export default () => {
const [auditInfo, setAuditInfo] = useState({});
const [appealDetailModal, setAppealDetailModal] = useState(false);
const [selectedRow, setSelectedRow] = useState({});
const [timeString, setTimeString] = useState({});
const [time, setTime] = useState({});
const viewDetail = async ({ serviceNo }) => {
const data = await orderDetail({ serviceNo });
setDetailInfo(data || []);
......@@ -53,12 +56,58 @@ export default () => {
setAppealDetailModal(true);
setSelectedRow(detailData);
};
const renderContent = (record, index, action) => {
if (!time[record.serviceNo]) {
const serviceTime = moment(record.serviceTime).valueOf() + 24 * 3600 * 1000;
const nowTime = moment(record.nowTime).valueOf();
let timeNumber = (serviceTime - nowTime) / 1000;
time[record.serviceNo] = setInterval(() => {
if (timeNumber > 0) {
timeNumber -= 1;
// eslint-disable-next-line radix
const hours = parseInt((timeNumber / 3600) % 24)
.toString()
.padStart(2, '0');
// eslint-disable-next-line radix
const minutes = parseInt((timeNumber / 60) % 60)
.toString()
.padStart(2, '0');
// eslint-disable-next-line radix
const seconds = parseInt(timeNumber % 60)
.toString()
.padStart(2, '0');
const str = `${hours}${minutes}${seconds}秒`;
timeString[record.serviceNo] = str;
const strings = _.cloneDeep(timeString);
setTimeString(strings);
} else {
clearInterval(time[record.serviceNo]);
timeString[record.serviceNo] = '0时0分0秒';
const strings = _.cloneDeep(timeString);
setTimeString(strings);
}
}, 1000);
}
};
const columns = [
{
title: '审核倒计时',
dataIndex: 'serviceTime',
key: 'serviceTime',
hideInSearch: true,
width: 150,
render: (val, record, index, action) => [
<span style={{ color: 'red' }}>
{renderContent(record, index, action)}
{timeString[record.serviceNo]}
</span>,
],
},
{
title: '售后状态',
dataIndex: 'serviceStatus',
hideInSearch: true,
width: 100,
width: 120,
},
...columnSticData,
{
......@@ -66,7 +115,7 @@ export default () => {
dataIndex: 'proofs',
hideInSearch: true,
width: 100,
render: (_, r) => <a onClick={() => viewProofs(r.proofs)}>查看凭证</a>,
render: (val, r) => <a onClick={() => viewProofs(r.proofs)}>查看凭证</a>,
},
{
title: '售后申诉',
......@@ -87,7 +136,7 @@ export default () => {
dataIndex: 'action',
width: 250,
fixed: 'right',
render: (_, r) => [
render: (val, r) => [
<Button key="link1" onClick={() => openAudit(r)} className="mr10" type="primary">
审核
</Button>,
......
......@@ -46,10 +46,7 @@ export async function orderDetail(params) {
// 售后审核
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',
},
data: params,
prefix: kdspApi,
});
}
......
import { Form, Button, Input, Select, notification, Upload, Cascader, InputNumber } from 'antd';
import {
Form,
Button,
Input,
Select,
notification,
Upload,
Cascader,
InputNumber,
Popover,
Divider,
} from 'antd';
import React, { Component } from 'react';
import { connect } from 'dva';
import styles from '../style.less';
......@@ -44,8 +55,8 @@ class goodsManage extends Component {
this.props.addSpu();
};
setArea = isAll => {
this.props.setArea(isAll);
setArea = (isAll, type) => {
this.props.setArea(isAll, type);
};
render() {
......@@ -53,6 +64,25 @@ class goodsManage extends Component {
const selectW = { width: 250 };
const iptNumWidth = { width: 118 };
const that = this;
const content = (
<div>
<Button style={{ border: 'none' }} onClick={() => this.setArea(1, 'distribution')}>
全部商品配送区域设置
</Button>
<br />
<Button style={{ border: 'none' }} onClick={() => this.setArea(0, 'distribution')}>
勾选商品配送区域设置
</Button>
<br />
<Button style={{ border: 'none' }} onClick={() => this.setArea(1, 'after')}>
全部商品售后地址设置
</Button>
<br />
<Button style={{ border: 'none' }} onClick={() => this.setArea(0, 'after')}>
勾选商品售后地址设置
</Button>
</div>
);
// const uploadProps = {
// name: 'file',
// async customRequest(info) {
......@@ -138,12 +168,11 @@ class goodsManage extends Component {
</Button>
</FormItem>
<FormItem style={{ float: 'right' }}>
<Button type="primary" className={styles.button} onClick={() => this.setArea(1)}>
全部配送不支持配置区域
</Button>
<Button type="primary" className={styles.button} onClick={() => this.setArea(0)}>
批量设置不支持配送区域
</Button>
<Popover content={content} onVisibleChange={this.handleVisibleChange}>
<Button type="primary" className={styles.button}>
批量操作
</Button>
</Popover>
<Button type="primary" className={styles.button} onClick={this.addSpu}>
新增商品
</Button>
......
......@@ -3,7 +3,7 @@ import '@ant-design/compatible/assets/index.css';
import { Modal, Input, Select, Cascader, Tag, notification } from 'antd';
import { da } from 'date-fns/locale';
import React, { useState, useEffect } from 'react';
import { getTemplate } from '../service';
import { getTemplate, getAfterAddress } from '../service';
const { Option } = Select;
......@@ -14,54 +14,106 @@ const TempleatModal = props => {
selectedRowKeys,
templateList,
isALL,
isType,
total,
} = props;
const formItemLayout = {
labelCol: {
span: 5,
span: 6,
},
wrapperCol: {
span: 16,
},
};
const setAfterAddress = async fieldsValue => {
const data = await getAfterAddress({
skuIds: selectedRowKeys || [],
afterAddressId: fieldsValue.templateId.key,
});
if (data.businessCode === '0000') {
notification.success({ message: '配置成功!' });
resetFields();
props.onCancel();
}
};
const setTemplate = async fieldsValue => {
const data = await getTemplate({
isAll: isALL,
skuIdList: selectedRowKeys,
templateId: fieldsValue.templateId.key,
});
if (data.businessCode === '0000') {
notification.success({ message: '配置成功!' });
resetFields();
props.onCancel();
}
};
const handleOk = async () => {
console.log(props);
validateFields(async (error, fieldsValue) => {
if (!error) {
const data = await getTemplate({
isAll: isALL,
skuIdList: selectedRowKeys,
templateId: fieldsValue.templateId.key,
});
if (data.businessCode === '0000') {
notification.success({ message: '配置成功!' });
resetFields();
props.onCancel();
if (isType === 'after') {
setAfterAddress(fieldsValue);
}
if (isType === 'distribution') {
setTemplate(fieldsValue);
}
}
});
};
useEffect(() => {}, []);
const title = () => {
if (isType === 'distribution') {
if (isALL) {
return '全部商品配送区域设置';
}
return '勾选商品配送区域设置';
}
if (isType === 'after') {
if (isALL) {
return '全部商品售后地址设置';
}
return '勾选商品售后地址设置';
}
return '';
};
return (
<Modal
title={isALL ? '全部设置不支持配送区域' : '批量设置不支持配送区域'}
title={title()}
visible={visible}
width="500px"
onCancel={props.onCancel}
onOk={() => handleOk()}
>
{!isALL && <p>已选择{selectedRowKeys.length}个商品</p>}
{isALL > 0 && isType === 'after' && <p>已选择{total}个商品</p>}
<Form {...formItemLayout}>
<Form.Item label="选择模板">
<Form.Item label={isType === 'after' ? '选择售后地址' : '选择模板'}>
{getFieldDecorator('templateId', {
rules: [{ required: true, message: '请选择模板!' }],
rules: [
{
required: true,
message: `${isType === 'after' ? '请选择售后地址' : '请选择模板'}`,
},
],
})(
<Select placeholder="请选择模板" labelInValue allowClear>
{templateList.map(item => (
<Option label={item.templateName} value={item.id} key={item.id}>
{item.templateName}
</Option>
))}
<Select
placeholder={isType === 'after' ? '选择售后地址' : '选择模板'}
labelInValue
allowClear
>
{isType === 'distribution' &&
templateList.map(item => (
<Option label={item.templateName} value={item.id} key={item.id}>
{item.templateName}
</Option>
))}
{isType === 'after' &&
templateList.map(item => (
<Option label={item.addressName} value={item.id} key={item.id}>
{item.addressName}
</Option>
))}
</Select>,
)}
</Form.Item>
......
......@@ -30,7 +30,14 @@ import {
validateSpuInfo,
createEditData,
} from './mixin';
import { getSpecList, getJdPicList, getBrandList, addGoods, editGoods } from '../service';
import {
getSpecList,
getJdPicList,
getBrandList,
addGoods,
editGoods,
queryAllAfterAddress,
} from '../service';
import styles from '../style.less';
import Upload from '../../components/sortablUpload';
import BatchSetting from './batchSetting';
......@@ -60,17 +67,27 @@ class goodsManage extends Component {
skuNameVisible: false,
productType: 1,
confirmLoading: false,
afterAddressList: {},
};
componentDidMount() {
this.getSpecData();
this.getBrandData();
this.getAfterAddressData();
}
componentWillReceiveProps() {
this.setState({ isCancel: false });
}
// 获取售后地址
getAfterAddressData = async () => {
const data = await queryAllAfterAddress();
if (data) {
this.setState({ afterAddressList: data.data.records });
}
};
// 获取规格列表
getSpecData = async () => {
try {
......@@ -119,7 +136,6 @@ class goodsManage extends Component {
i.imageList = colorImg[i.firstSpecValue];
return i;
});
console.log(editData);
this.setState(
() => ({ colorImg, editData }),
() => {
......@@ -342,8 +358,12 @@ class goodsManage extends Component {
specListData,
} = this.props;
const { editData, productType } = this.state;
validateFields(async (errors, values) => {
if (!values.afterAddressId) {
notification.error({
message: '请选择售后地址',
});
}
let imgErr = false;
if (!errors) {
if (validateSpuInfo(values, initData, editData, productType)) return;
......@@ -385,6 +405,7 @@ class goodsManage extends Component {
type: values.productType,
specs,
character: values.character,
afterAddressId: values.afterAddressId,
},
}));
const data = initData.id
......@@ -494,7 +515,14 @@ class goodsManage extends Component {
labelCol: { span: 2 },
wrapperCol: { span: 22 },
};
const { colorImg, productType, normalBrandList, brandList, confirmLoading } = this.state;
const {
colorImg,
productType,
normalBrandList,
brandList,
confirmLoading,
afterAddressList,
} = this.state;
const skuSpeFirstKeys = initData.firstSpecList || [];
const skuSpeSecondKeys = initData.secondSpecList || [];
const treeDataArray = productType === 2 ? virtualTreeData : treeData;
......@@ -658,6 +686,28 @@ class goodsManage extends Component {
)}
</FormItem>
</Col>
<Col span={10}>
<FormItem label="售后地址" {...formItemLayout}>
{getFieldDecorator('afterAddressId', {
initialValue: this.state.initForm.afterAddressId,
rules: [
{
required: true,
message: '请选择售后地址',
},
],
})(
<Select allowClear style={{ width: 280 }} placeholder="请选择售后地址">
{afterAddressList?.length &&
afterAddressList.map(item => (
<Option key={item.id} value={item.id}>
{item.addressName}
</Option>
))}
</Select>,
)}
</FormItem>
</Col>
{specListData.length
? specListData.map((item, index) => (
<Col span={24}>
......
......@@ -11,7 +11,14 @@ import LocalStroage from '@/utils/localStorage';
import configApi from '../../../config/env.config';
import UpdateStock from './UpdateStock';
import { spuDetail, categoryList, getVirtualCategory, getTemplateList, specList } from './service';
import {
spuDetail,
categoryList,
getVirtualCategory,
getTemplateList,
specList,
queryAllAfterAddress,
} from './service';
import LogModal from './LogModal';
import CreateModal from './createModal';
import { column, JDSHOPID } from './staticdata';
......@@ -40,6 +47,7 @@ class goodsManage extends Component {
selectedRowKeys: [],
isAll: 0,
templateList: [],
isType: '',
};
currentLog = null;
......@@ -187,20 +195,23 @@ class goodsManage extends Component {
});
};
setArea = async isAll => {
setArea = async (isAll, type) => {
// distribution配送区域 after售后地址
if (!this.state.selectedRowKeys.length && !isAll) {
notification.error({ message: '请选择商品' });
return;
}
const data = await getTemplateList();
if (data.code === '0000' && data.data.length) {
const data = type === 'distribution' ? await getTemplateList() : await queryAllAfterAddress();
const length = type === 'distribution' ? data.data.length : data.data.records.length;
if (data.code === '0000' && length) {
this.setState({
templeatModalVisible: true,
isAll,
templateList: data.data,
templateList: type === 'distribution' ? data.data : data.data.records,
isType: type,
});
} else {
notification.error({ message: '暂无模板数据,请先添加模板' });
notification.error({ message: '暂无数据' });
}
};
......@@ -283,7 +294,6 @@ class goodsManage extends Component {
const {
goodsManage: { tableData = {} },
} = this.props;
const rowSelection = {
selectedRowKeys: this.state.selectedRowKeys,
onChange: this.onSelectChange,
......@@ -303,7 +313,7 @@ class goodsManage extends Component {
treeData={this.state.treeData}
shopList={this.shopList}
addSpu={() => this.setState({ createVisible: true, initData: {} })}
setArea={isALL => this.setArea(isALL)}
setArea={(isALL, type) => this.setArea(isALL, type)}
/>
</Card>
<Spin spinning={this.state.loading}>
......@@ -379,11 +389,13 @@ class goodsManage extends Component {
<TempleatModal
visible={this.state.templeatModalVisible}
selectedRowKeys={this.state.selectedRowKeys}
total={tableData.total || 0}
onCancel={() => {
this.setState({ templeatModalVisible: false, selectedRowKeys: [] });
this.handleSearch();
}}
isALL={this.state.isAll}
isType={this.state.isType}
templateList={this.state.templateList}
/>
</Spin>
......
......@@ -4,7 +4,7 @@ import config from '../../../config/env.config';
import { stringify } from 'qs';
import _ from 'lodash';
const { goodsApi } = config;
const { goodsApi, kdspApi } = config;
const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
......@@ -173,3 +173,27 @@ export async function getTemplateList(params) {
});
return data;
}
// 查询供应商售后地址
export async function queryAllAfterAddress() {
const params = {
pageSize: 100,
pageNo: 1,
};
const data = request.post('/api/kdsp/supplier/after-sales-addrs-page', {
prefix: kdspApi,
data: stringify(_.omitBy(params, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return data;
}
// 商品售后地址设置
export async function getAfterAddress(params) {
const data = await request.post('/api/kdsp/updateSkuAfterAddress', {
data: params,
prefix: goodsApi,
});
return data;
}
import { Upload, Icon, Modal, message, Button } from 'antd';
import { Upload, Icon, Modal, message, Button, notification } from 'antd';
import React, { useState, useEffect } from 'react';
import config from '../../../../config/env.config';
import styles from '../style.less';
......@@ -57,12 +57,19 @@ class PicturesWall extends React.Component {
}
const vm = this;
const name = file.name + file.uid;
const observable = qiniu.upload(file, file.name, token);
// eslint-disable-next-line new-cap
const data = file.name + new Date().getTime();
const observable = qiniu.upload(file, data, token);
const observer = {
next() {
// ...
},
error() {
error(t) {
if (t.code === 614) {
notification.error({
message: '文件名重复,请更换文件名。',
});
}
onError(file);
// ...
},
......
......@@ -169,6 +169,17 @@ const AddressForm = props => {
onOk={handleSubmit}
>
<Form {...layout} name="formData">
<FormItem label="售后地址名称">
{getFieldDecorator('addressName', {
initialValue: formData.addressName,
rules: [
{
required: true,
message: '请输入售后地址名称!',
},
],
})(<Input placeholder="请输入售后地址名称" maxLength={20} />)}
</FormItem>
<FormItem label="供应商名称">
{getFieldDecorator('shopId', {
initialValue: formData.shopId,
......
......@@ -33,10 +33,12 @@ const TableList = () => {
actionRef.current.reload();
};
const delAction = async ({ id }) => {
const businessCode = await del(id);
if (businessCode === '0000') {
const data = await del(id);
if (data.businessCode === '0000') {
reload();
notification.success({ message: '删除成功!' });
} else {
notification.error({ message: data.msg || '删除失败' });
}
};
const editAction = async row => {
......@@ -54,6 +56,12 @@ const TableList = () => {
}
};
const columns = [
{
title: '售后地址名称',
dataIndex: 'addressName',
key: 'addressName',
hideInSearch: true,
},
{
title: '供应商名称',
dataIndex: 'shopId',
......
......@@ -42,14 +42,14 @@ export async function supplier() {
}
export async function del(id) {
const { businessCode } = await request.post('/api/kdsp/supplier/after-sales-addrs-delete', {
const data = await request.post('/api/kdsp/supplier/after-sales-addrs-delete', {
prefix: config.kdspApi,
data: stringify({ id }),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return businessCode;
return data;
}
export async function update(params) {
......
......@@ -5,12 +5,12 @@ import { Button, notification } from 'antd';
import ProTable from '@ant-design/pro-table';
import _ from 'lodash';
// import { searchList, auditInfoApi, orderDetail } from './service';
import moment from 'moment';
import { searchList, orderDetail } from './service';
import { column } from './data';
import AuditModal from './components/auditModal';
import DetailTable from './components/detailTable';
import { dateDiff } from './common';
import moment from 'moment';
export default () => {
const table = useRef();
......@@ -104,6 +104,8 @@ export default () => {
timeString[record.orderId] = '0时0分0秒';
const strings = _.cloneDeep(timeString);
setTimeString(strings);
// eslint-disable-next-line no-unused-expressions
table.current?.reload?.();
}
}, 1000);
}
......
......@@ -48,7 +48,6 @@ const ImageInfo = file =>
file.width = width;
file.height = height;
file.LtMB = LtMB;
console.log(file);
resolve(file);
});
image.addEventListener('error', () => {
......
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