Commit cd134bc7 authored by shida.liu's avatar shida.liu

feat: 为审计新增的页面,无实际用处

parent a3bb5b11
...@@ -304,6 +304,24 @@ export default { ...@@ -304,6 +304,24 @@ export default {
{ {
component: './404', component: './404',
}, },
{
title: '商户管理后台-POP商品管理-待发货订单',
path: '/popOrderManage/popPendingDeliveryOrder',
name: 'popPendingDeliveryOrder',
component: './PopOrderManage/pendingDeliveryOrder',
},
{
title: '商户管理后台-POP商品管理-已发货订单',
path: '/popOrderManage/popDeliveryOrder',
name: 'popDeliveryOrder',
component: './PopOrderManage/deliveryOrder',
},
{
title: '商户管理后台-POP商品管理-批量发货',
path: '/popOrderManage/popBatchDelivery',
name: 'popBatchDeliveryOrder',
component: './PopOrderManage/batchDelivery',
},
], ],
}, },
{ {
......
...@@ -34,6 +34,24 @@ export const BATCH_DELIVERY = { ...@@ -34,6 +34,24 @@ export const BATCH_DELIVERY = {
EDITABLE: '020502', // 新增/修改 EDITABLE: '020502', // 新增/修改
}; };
// POP待发货订单-(应付审计的,没有实际用处)
export const POP_PENDING_DELIVERY_ORDER = {
LIST: '0203011',
EDITABLE: '0203021',
};
// POP已发货订单-(应付审计的,没有实际用处)
export const POP_DELIVERY_ORDER = {
LIST: '0204011',
EDITABLE: '0204021',
};
// POP批量发货-(应付审计的,没有实际用处)
export const POP_BATCH_DELIVERY = {
LIST: '0205011',
EDITABLE: '0205021', // 新增/修改
};
// 订单查询 // 订单查询
export const QUERY_ORDER = { export const QUERY_ORDER = {
LIST: '020601', // 订单查询列表 LIST: '020601', // 订单查询列表
......
import { Button, Upload, notification } from 'antd';
import React, { useRef } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import { connect } from 'dva';
import { BATCH_DELIVERY } from '@/../config/permission.config';
import {
queryToBatchSend,
uploadFile,
downTemplate,
downUploadeOrder,
} from '../pendingDeliveryOrder/service';
const TableList = props => {
const actionRef = useRef(null);
const { permissions } = props;
const canEditable = permissions[BATCH_DELIVERY.EDITABLE];
const columns = [
{
title: '批次号',
dataIndex: 'batchNo',
key: 'batchNo',
align: 'center',
},
{
title: '时间',
key: 'dateTimeRange',
dataIndex: 'createdAtRange',
valueType: 'dateTimeRange',
width: 300,
hideInTable: true,
initialValue: [],
align: 'center',
},
{
title: '操作时间',
dataIndex: 'createdAt',
key: 'createdAt',
hideInSearch: true,
align: 'center',
},
{
title: '操作人',
dataIndex: 'userName',
key: 'userName',
hideInSearch: true,
width: 120,
align: 'center',
},
{
title: '发货单数',
dataIndex: 'totalNum',
key: 'totalNum',
hideInSearch: true,
width: 120,
align: 'center',
},
{
title: '成功发货单数',
dataIndex: 'succNum',
key: 'succNum',
hideInSearch: true,
width: 120,
align: 'center',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 120,
align: 'center',
filters: [],
valueEnum: {
0: { text: '全部失败' },
1: { text: '全部成功' },
2: { text: '部分成功' },
99: { text: '全部' },
},
initialValue: 1,
},
{
title: '操作',
dataIndex: 'option',
key: 'option',
valueType: 'option',
align: 'center',
render: (_, record) => (
<React.Fragment>
<Button
type="primary"
style={{
marginRight: '10px',
}}
onClick={async () => {
downUploadeOrder({ batchNo: record.batchNo, status: 2 });
}}
>
导出全部数据
</Button>
{record.status === 1 ? (
''
) : (
<Button
type="primary"
style={{
marginRight: '10px',
}}
onClick={() => {
downUploadeOrder({ batchNo: record.batchNo, status: 0 });
}}
>
导出失败数据
</Button>
)}
</React.Fragment>
),
},
];
const reload = () => {
if (actionRef.current) {
actionRef.current.reload();
}
};
const uploadProps = {
name: 'file',
async customRequest(info) {
const result = await uploadFile(info.file);
if (result.businessCode === '0000') {
reload();
notification.success({ message: '导入成功' });
}
},
accept: '.xlsx',
showUploadList: false,
};
const searchRender = ({ searchText, resetText }, { form }) => {
const exportBtn = [
<Upload {...uploadProps}>
<Button type="primary" style={{ marginRight: '10px' }} key="export">
批量发货
</Button>
</Upload>,
<Button
type="primary"
key="exportTemplate"
onClick={downTemplate}
style={{ marginRight: '10px' }}
>
模板下载
</Button>,
];
if (!canEditable) {
exportBtn.splice(0, 1);
}
return [
<Button
key="search"
type="primary"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
form?.submit();
}}
>
{searchText}
</Button>,
<Button
key="rest"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
form?.resetFields();
// eslint-disable-next-line no-unused-expressions
form?.submit();
}}
>
{resetText}
</Button>,
[...exportBtn],
];
};
return (
<PageHeaderWrapper>
<ProTable
actionRef={actionRef}
request={params => queryToBatchSend({ ...params })}
columns={columns}
rowKey={r => r.batchNo}
search={{
collapsed: false,
optionRender: searchRender,
}}
bordered
scroll={{ x: 1500 }}
/>
</PageHeaderWrapper>
);
};
export default connect(({ menu }) => ({
permissions: menu.permissions,
}))(TableList);
import React from 'react';
import { Table } from 'antd';
import style from './style.less';
const GoodsRemark = props => {
const columns = [
{
title: '商品',
dataIndex: 'skuName',
width: 350,
render: (value, record) => {
const { skuName, imageUrl } = record;
return (
<div className={style.popoverGoods}>
<img src={imageUrl} alt="" className={style['popoverGoods-img']} />
<span>{skuName}</span>
</div>
);
},
},
{
title: '备注',
dataIndex: 'content',
width: 150,
},
{
title: '操作时间',
dataIndex: 'time',
width: 150,
},
];
return (
<div>
<Table columns={columns} dataSource={props.dataSource} pagination={false} />
</div>
);
};
export default GoodsRemark;
.popoverGoods {
display: flex;
align-items: center;
&-img {
width: 50px;
height: 50px;
margin-right: 10px;
}
}
import React from 'react';
import DeliverOrder from '../pendingDeliveryOrder';
// 已发货type:2;待发货type: 1
const TableList = () => <DeliverOrder type={2} />;
export default TableList;
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Modal, notification, Input } from 'antd';
import React, { useState } from 'react';
import { apiDelayDeliverGoods } from '../service';
const UpdateStock = props => {
const { getFieldDecorator, validateFields, resetFields } = props.form;
const valueInfo = {};
const [loading, setLoading] = useState(false);
const submit = async () => {
validateFields(async (err, { remark }) => {
if (err) return;
setLoading(true);
const params = {
orderIds: [props.orderId],
operationType: 5,
remark,
};
console.log('params :>> ', params);
const res = await apiDelayDeliverGoods(params);
if (res?.businessCode === '0000') {
notification.success({ message: '提交成功!' });
props.onCancel('success');
resetFields();
}
setLoading(false);
});
};
const onCancel = () => {
props.onCancel();
resetFields();
};
const formItemLayout = {
labelCol: {
span: 7,
},
wrapperCol: {
span: 15,
},
};
return (
<Modal
title="异常订单报备"
visible={props.visible}
okButtonProps={{ loading }}
onCancel={onCancel}
onOk={submit}
width={500}
>
<Form {...formItemLayout}>
<Form.Item label="异常原因:">
{getFieldDecorator('remark', {
rules: [{ required: true, message: '请输入异常原因!' }],
initialValue: valueInfo.remark,
})(<Input.TextArea maxLength={30} showCount />)}
</Form.Item>
</Form>
</Modal>
);
};
export default Form.create()(UpdateStock);
import React from 'react';
const Image = ({ width, url }) => <img width={width} src={url} alt="" />;
export default Image;
/* eslint-disable no-restricted-syntax */
/* eslint-disable guard-for-in */
import React, { useState, useEffect } from 'react';
import { Modal, Timeline } from 'antd';
const LogisticsCom = props => {
const { modalVisible, onCancel } = props;
const [result, setResult] = useState({});
useEffect(() => {
setResult(props.value);
});
return (
<Modal
destroyOnClose
title={`${
result?.expressCompanyName
? `${result?.expressCompanyName}-${result?.deliveryNo}`
: '物流信息'
}`}
visible={modalVisible}
onCancel={() => onCancel()}
onOk={() => onCancel()}
afterClose={() => setResult({})}
bodyStyle={{ maxHeight: '600px', minHeight: '200px', overflow: 'auto' }}
footer={[]}
>
{result?.detailList?.length ? (
<Timeline>
{result?.detailList?.map((item, index) => (
// eslint-disable-next-line react/no-array-index-key
<Timeline.Item color={index > 0 ? 'gray' : 'blue'} key={index}>
<p>{item.desc}</p>
<p>{item.time}</p>
</Timeline.Item>
))}
</Timeline>
) : (
'暂无物流信息'
)}
</Modal>
);
};
export default LogisticsCom;
import React, { useState, useEffect } from 'react';
import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Modal, Select, Input, Card, notification } from 'antd';
import style from '../index.less';
import { apiDeliveriesAdd } from '../service';
const FormItem = Form.Item;
const { Option } = Select;
const LogisticsForm = props => {
const { modalVisible, onCancel, company = [], skuList, onSubmit } = props;
const { getFieldDecorator } = props.form;
const [result, setResult] = useState(() => props.value);
const formData = async (formDataList, fieldsValue, suffixes) => {
// 数据聚合
suffixes.forEach(suffixe => {
const formObj = {};
// eslint-disable-next-line no-restricted-syntax
for (const key in fieldsValue) {
if (key.startsWith(suffixe)) {
formObj[key.split('-')[1]] = fieldsValue[key];
}
}
formDataList.push(formObj);
});
// 参数整合
const resultData = [];
formDataList.forEach(data => {
data.selectedGoods.forEach(orderSkuId => {
resultData.push({
orderSkuId,
expressCompanyCode: data.selectedCompany.split('-')[0],
expressCompanyName: data.selectedCompany.split('-')[1],
deliveryNo: data.orderNum,
serialNumber: data.serialNumber || '',
});
});
});
// 校验重复商品
let orderSkuIds = [];
formDataList.forEach(data => {
orderSkuIds = [...orderSkuIds, ...data.selectedGoods];
});
if (orderSkuIds.length !== Array.from(new Set(orderSkuIds)).length) {
notification.error({ message: '商品选择重复!' });
return;
}
if (skuList.length !== orderSkuIds.length) {
notification.error({ message: '该订单下的所有商品必须设置物流信息!' });
return;
}
await apiDeliveriesAdd(resultData);
onSubmit();
};
const handleSubmit = () => {
props.form.validateFields((err, fieldsValue) => {
const suffixes = [];
const formDataList = [];
if (err) return;
Object.keys(fieldsValue).forEach(key => {
const suffixe = key.split('-')[0];
if (!suffixes.includes(suffixe)) suffixes.push(suffixe);
});
formData(formDataList, fieldsValue, suffixes);
});
};
const addForm = () => {
setResult([...result, {}]);
};
const rmForm = () => {
if (result.length === 1) return;
setResult(result.slice(0, result.length - 1));
};
useEffect(() => {
setResult(props.value);
}, props.value);
return (
<Modal
destroyOnClose
title="物流操作确认"
visible={modalVisible}
onCancel={() => onCancel()}
onOk={handleSubmit}
afterClose={() => setResult([{}])}
>
<Form>
{result &&
result.length &&
result.map((v, i) => (
// eslint-disable-next-line react/no-array-index-key
<Card key={i}>
<FormItem>
{getFieldDecorator(`${i}-selectedGoods`, {
initialValue: v.selectedGoods,
rules: [
{
required: true,
message: '请选择该订单下的商品!',
},
],
})(
<Select mode="multiple" placeholder="请选择该订单下的商品">
{skuList.map(item => (
<Option value={item.orderSkuId} key={item.orderSkuId}>
{item.skuName + (item.skuAttr || '')}
</Option>
))}
</Select>,
)}
</FormItem>
<FormItem>
{getFieldDecorator(`${i}-selectedCompany`, {
initialValue: v.selectedCompany,
rules: [
{
required: true,
message: '请选择物流公司!',
},
],
})(
<Select showSearch placeholder="请选择物流公司">
{company?.map(item => (
<Option
value={`${item.expressCompanyCode}-${item.expressCompanyName}`}
key={item.expressCompanyCode}
>
{item.expressCompanyName}
</Option>
)) || []}
</Select>,
)}
</FormItem>
<FormItem>
{getFieldDecorator(`${i}-orderNum`, {
initialValue: v.orderNum,
rules: [
{
required: true,
message: '请填写物流单号!',
},
],
})(<Input maxLength={30} placeholder="请填写物流单号" />)}
</FormItem>
<FormItem>
{getFieldDecorator(`${i}-serialNumber`, {
initialValue: v.serialNumber,
rules: [
{
pattern: new RegExp(/^[0-9a-zA-Z-_]{6,32}$/, 'g'),
message: '序列号格式不正确!',
},
],
})(<Input placeholder="请填写序列号" />)}
</FormItem>
</Card>
))}
<div className={style.logistics}>
<PlusCircleOutlined className={style.logisticsIcon} onClick={addForm} />
<MinusCircleOutlined className={style.logisticsIcon} onClick={rmForm} />
</div>
</Form>
</Modal>
);
};
export default Form.create()(LogisticsForm);
import React, { forwardRef, useImperativeHandle, useState, useRef, useEffect } from 'react';
import { Modal, Select, notification, Form, Input, Button, Checkbox } from 'antd';
import { PlusCircleOutlined, MinusCircleOutlined } from '@ant-design/icons';
import { userEnabled, userDisabled, userDelete } from '@/pages/systemManage/service';
import { apiDeliveriesAdd, apiDeliveriesEdit } from '../../service';
import styles from './index.less';
const FormComponent = (props, ref) => {
const { record = {}, companys = [], minPackageCount = 1, maxPackageCount = 10 } = props;
const { mchOrderSkuVoList, packageList } = record;
const [form] = Form.useForm();
const [addPackageList, setAddPackageList] = useState({
expressCompanyCode: null,
expressCompanyName: '',
expressNo: '',
serialNumber: '',
skuInfoList: mchOrderSkuVoList.map(item => ({})),
});
const [initPackageList, setInitPackageList] = useState(() => {
// 新增
if (packageList.length === 0) {
return [
{
// 默认选中所有商品
skuInfoList: mchOrderSkuVoList.map(item => ({
skuNo: [item.skuNo],
quantity: item.count,
})),
},
];
}
// 编辑
return packageList.map(item => ({
...item,
// 渲染所有商品选项,通过查找editData 是否存在判断是否选中
skuInfoList: mchOrderSkuVoList.map((skuInfo, index) => {
const skuNo = skuInfo.skuNo?.toString();
const editData = item.skuInfoList?.find(ele => ele.skuNo?.toString() === skuNo);
if (editData) {
return {
...editData,
skuNo: [skuInfo.skuNo],
};
}
return {};
}),
}));
});
// eslint-disable-next-line consistent-return
const onCheckSkuInfoList = async (_, names) => {
let hasNotCount = 0;
const count = names.filter(item => {
if (item.skuNo && item.skuNo.length > 0 && !item.quantity) {
hasNotCount++;
}
return item.skuNo && item.skuNo.length;
}).length;
if (hasNotCount) {
return Promise.reject(new Error('请填写选择发货商品的数量'));
}
if (count === 0) {
return Promise.reject(new Error('至少选择一个商品'));
}
};
const formLayout = {
labelCol: { span: 0 },
wrapperCol: { span: 24 },
};
const formRules = {
skuInfoList: [{ validator: onCheckSkuInfoList }],
expressCompanyCode: [{ required: true, message: '请选择物流公司' }],
expressNo: [{ required: true, message: '请填写物流单号' }],
// serialNumber: [{ required: true, message: '请填写序列号' }],
};
useImperativeHandle(ref, () => ({
form,
}));
const getSkuOptions = (index, parentIndex) => {
const { skuNo, skuName } = mchOrderSkuVoList[index];
return (
<Checkbox.Group>
<Checkbox value={skuNo}>{skuName}</Checkbox>
</Checkbox.Group>
);
};
const onInput = e => {
if (e.target.value === '') {
return;
}
if (e.target.value <= 0) {
e.target.value = 1;
}
};
const SkuFormList = skuProps => {
const { skuFields, parentFields, errors } = skuProps;
return (
<div className={styles['sku-list-box']}>
{skuFields.map(field => (
<div key={field.key} className={styles['sku-list']}>
<Form.Item className={styles['sku-list__goods-name']} name={[field.name, 'skuNo']}>
{getSkuOptions(field.name, parentFields.name)}
</Form.Item>
<Form.Item className={styles['sku-list__goods-count']} name={[field.name, 'quantity']}>
<Input type="number" onInput={onInput} placeholder="数量" />
</Form.Item>
</div>
))}
<Form.ErrorList errors={errors} />
</div>
);
};
// 选择物流可搜索
const filterOption = (inputValue, option) => option.expressCompanyName.includes(inputValue);
return (
<Form
form={form}
{...formLayout}
initialValues={{
packageList: initPackageList,
}}
>
<Form.List name="packageList" rules={[]}>
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field, index) => (
<div key={field.key} className={styles['sku-list-module']}>
<div className={styles['sku-list-module__index']}>包裹 {index + 1}</div>
<Form.List name={[field.name, 'skuInfoList']} rules={formRules.skuInfoList}>
{(skuFields, actions, { errors: err }) => (
<SkuFormList
key={field.key}
skuFields={skuFields}
parentFields={field}
errors={err}
/>
)}
</Form.List>
<Form.Item
name={[field.name, 'expressCompanyCode']}
rules={formRules.expressCompanyCode}
>
<Select
placeholder="请选择物流公司"
showSearch
filterOption={filterOption}
fieldNames={{ label: 'expressCompanyName', value: 'expressCompanyCode' }}
options={companys}
></Select>
</Form.Item>
<Form.Item name={[field.name, 'expressNo']} rules={formRules.expressNo}>
<Input placeholder="请填写物流单号" />
</Form.Item>
<Form.Item name={[field.name, 'serialNumber']} rules={formRules.serialNumber}>
<Input placeholder="请填写序列号" />
</Form.Item>
</div>
))}
<div className={styles.operation}>
<PlusCircleOutlined
style={{ color: fields.length >= maxPackageCount ? '#ccc' : '' }}
onClick={() => {
if (fields.length >= maxPackageCount) return;
const currentPackageList = form.getFieldValue('packageList');
form.setFieldsValue({
packageList: [...currentPackageList, addPackageList],
});
}}
/>
<MinusCircleOutlined
style={{ color: fields.length <= minPackageCount ? '#ccc' : '' }}
onClick={() => {
if (fields.length <= minPackageCount) return;
remove(fields[fields.length - 1].name);
}}
/>
</div>
</>
)}
</Form.List>
</Form>
);
};
const FormWarpper = forwardRef(FormComponent);
const UpdateStatusModal = (props, ref) => {
const { companys } = props;
const formRef = useRef();
const [visible, setVisible] = useState(false);
const [renderModal, setRenderModal] = useState(true);
const [confirmLoading, setConfirmLoading] = useState(false);
const [record, setRecord] = useState();
const [oldPackageList, setOldPackageList] = useState();
const [actionRef, setActionRef] = useState();
// 提交前处理数据结构将skuNo的值从[skuNo]=>skuNo
const onFilterParams = packageList => {
const currentPackageList = JSON.parse(JSON.stringify(packageList));
return currentPackageList.map(item => {
const { skuInfoList } = item;
/**
* 参数结构
* @var {object} item { expressCompanyCode, expressNo, serialNumber, skuInfoList }
* @var {array} skuInfoList [{skuNo, quantity}]
* @var {object} company { expressCompanyCode, expressCompanyName }
*/
const company = companys.find(ele => ele.expressCompanyCode === item.expressCompanyCode);
return {
...item,
...company,
skuInfoList: skuInfoList
.filter(skuInfo => skuInfo.skuNo?.length)
.map(skuInfo => ({
skuNo: Number(skuInfo.skuNo[0]),
quantity: Number(skuInfo.quantity),
})),
};
});
};
const open = (item, tableRef) => {
setRenderModal(true);
setActionRef(tableRef);
setVisible(true);
setOldPackageList(JSON.parse(JSON.stringify(item.packageList)));
setRecord(item);
};
const onCancel = () => {
formRef.current.form.resetFields();
setConfirmLoading(false);
setVisible(false);
setTimeout(() => {
setRenderModal(false);
setRecord(null);
}, 500);
};
const onOk = async () => {
const submitApi = oldPackageList.length === 0 ? apiDeliveriesAdd : apiDeliveriesEdit;
formRef.current.form
.validateFields()
.then(async values => {
const packageList = onFilterParams(values.packageList);
// 对比修改差异
oldPackageList.forEach((item, index) => {
if (!packageList[index]) return;
const updateAttr = () => {
packageList[index].preExpressCompanyCode = item.expressCompanyCode;
packageList[index].preExpressCompanyName = item.expressCompanyName;
packageList[index].preExpressNo = item.expressNo;
};
// eslint-disable-next-line no-restricted-syntax
// for (const key in item) {
// if (typeof item[key] !== 'object' && item[key] !== packageList[index][key]) {
// console.log('修改了物流信息');
// updateAttr();
// break;
// } else if (
// typeof item[key] === 'object' &&
// JSON.stringify(item[key]) !== JSON.stringify(packageList[index][key])
// ) {
// console.log('修改了选择的商品');
// updateAttr();
// break;
// }
// }
updateAttr();
});
const params = {
orderNo: record.orderNo,
packageList,
};
setConfirmLoading(true);
const res = await submitApi(params);
setConfirmLoading(false);
if (res?.code === '0000' && res?.businessCode === '0000') {
notification.success({
message: '提交成功',
});
onCancel();
actionRef.current.reload();
}
})
.catch(err => {});
};
useImperativeHandle(ref, () => ({
open,
}));
const modalProps = {
title: '填写物流信息',
height: 500,
visible,
confirmLoading,
onCancel,
onOk,
};
const formProps = {
record,
companys,
minPackageCount: oldPackageList?.length || 1,
};
return renderModal ? (
<Modal {...modalProps}>{record ? <FormWarpper ref={formRef} {...formProps} /> : ''}</Modal>
) : (
''
);
};
export default forwardRef(UpdateStatusModal);
.sku-list-module {
margin-bottom: 20px;
padding: 10px 20px;
border: 1px solid #f2f2f2;
&__index {
margin-bottom: 5px;
font-weight: 700;
font-size: 14px;
}
}
.sku-list-box {
padding-bottom: 10px;
.sku-list {
display: flex;
// padding-bottom: 19px;
&__goods-name {
flex: 1;
margin-bottom: 0 !important;
}
&__goods-count {
width: 100px;
margin-bottom: 5px !important;
}
}
}
.operation {
font-size: 24px;
text-align: center;
> * {
margin: 0 10px;
}
}
import React from 'react';
import { Popover } from 'antd';
import Image from './Image';
const PopoverDom = ({ name, url }) => {
const content = <Image width={300} url={url} />;
return (
<Popover content={content} title={name}>
{Image({ width: 50, url })}
</Popover>
);
};
export default PopoverDom;
import { Button, notification, Modal, Popover } from 'antd';
import React, { useState, useEffect, useRef } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { useLocation } from 'react-router-dom';
import ProTable from '@ant-design/pro-table';
import { FormInstance } from 'antd/lib/form';
import moment from 'moment';
import { connect } from 'dva';
import { POP_PENDING_DELIVERY_ORDER, DELIVERY_ORDER } from '@/../config/permission.config';
import style from './index.less';
import LogisticsForm from './components/LogisticsForm';
import PopoverDom from './components/PreviewImage';
import LogisticsCom from './components/LogisticsCom';
import DelayDeliverGoods from './components/DelayDeliverGoods';
import GoodsRemark from '../components/GoodsRemark';
import MultiLogisticsModal from './components/MultiLogisticsModal';
import {
queryToSend,
queryExpress,
getGoods,
downOrder,
apiQueryOrderInfo,
apiDeliveriesTraceList,
} from './service';
const { confirm } = Modal;
const TableList = props => {
const location = useLocation();
const dateFormat = 'YYYY-MM-DD';
const { permissions } = props;
// 因待发货 和 已发货都使用当前组件,功能且相同,所以只要授权其中一个均可获得发货物流权限
const canEditable =
permissions[POP_PENDING_DELIVERY_ORDER.EDITABLE] || permissions[DELIVERY_ORDER.EDITABLE];
const [companys, setCompanys] = useState([]);
const [LogisticsModalVisible, handleModalVisible] = useState(false);
const [skuList, setSkuList] = useState([]);
const [LogisticsData, setLogisticsData] = useState([{}]);
const [ShowUpdateBtn] = useState([2, 5]);
const [LogisticsComList, setLogisticsComList] = useState({});
const [LogisticsComModalVisible, handleComModalVisible] = useState(false);
const [startTime, setStartTime] = useState(moment().add(-1, 'months'));
const [startTimeStr, setStartTimeStr] = useState(
moment()
.add(-1, 'months')
.format(dateFormat),
);
const [endTime, setEndTime] = useState(moment());
const [orderStatus, setorderStatus] = useState(1);
const [orderNo, setOrderNo] = useState('');
const [endTimeStr, setEndTimeStr] = useState(moment().format(dateFormat));
const [visibleDelay, setVisibleDelay] = useState(false);
const [delayOrderIDs, setDelayOrderIDs] = useState(0);
// const startDisabledDate = current =>
// current && (endTime.diff(current, 'days') > 30 || endTime.diff(current, 'days') < 0);
// const endDisabledDate = current =>
// current && (current.diff(startTime, 'days') > 30 || current.diff(startTime, 'days') < 0);
const multiLogisticsModalRef = useRef();
const actionRef = useRef();
const ref = useRef(FormInstance);
const handleCom = async (skuInfo, expressInfo) => {
const tempObj = {
expressCompanyCode: expressInfo?.expressCompanyCode ?? '',
expressCompanyName: expressInfo.expressCompanyName ?? '',
deliveryNo: expressInfo?.expressNo ?? '',
detailList: [],
key: Date.now(),
};
const res = await apiDeliveriesTraceList({
expressCompanyCode: expressInfo.expressCompanyCode,
expressNo: expressInfo.expressNo,
orderNo: skuInfo.orderNo,
});
const data = res.data[0];
console.log(data);
if (!data) {
notification.info({ message: '暂无物流信息' });
return;
}
tempObj.expressCompanyName = data?.expressCompanyName || tempObj.expressCompanyName;
tempObj.deliveryNo = data?.expressNo || tempObj.deliveryNo;
if (data.traceList?.length) {
data.traceList.forEach(v => {
tempObj.detailList = [...tempObj.detailList, ...v.detailList];
});
}
handleComModalVisible(true);
setLogisticsComList(tempObj);
};
const onDelay = (id, state, content) => {
if (state === 1) {
confirm({
title: '提示',
content,
cancelButtonProps: {
style: {
display: 'none',
},
},
});
} else {
setDelayOrderIDs(id);
setVisibleDelay(true);
}
};
const onCancelDelay = e => {
setVisibleDelay(false);
if (e && actionRef.current) {
actionRef.current.reload();
}
};
const renderContent = (record, key) => {
if (record.mchOrderSkuVoList) {
return record?.mchOrderSkuVoList.map((item, index) => {
const tableContentHeight = item?.expressList?.length || 1;
return (
<div
// eslint-disable-next-line prefer-template
style={{ height: tableContentHeight * 60 + 'px' }}
className={[
'tableContent',
index < record?.mchOrderSkuVoList?.length - 1 ? 'border' : null,
].join(' ')}
key={item.orderSkuId}
>
{key === 'skuName' ? <PopoverDom name={item[key]} url={item.imageUrl} /> : ''}
{key === 'expressCompanyName' ? (
<div className="expressList">
{item.expressList?.map((info, idx) => (
<span
className={[
'subContent',
idx < item.expressList?.length - 1 ? 'border' : null,
].join(' ')}
>
{info.expressCompanyName}
</span>
))}
</div>
) : (
''
)}
{key === 'deliveryNo' ? (
<div className="expressList">
{item.expressList?.map((info, idx) => (
<a
onClick={() => {
handleCom(item, info);
}}
className={[
'subContent',
idx < item.expressList?.length - 1 ? 'border' : null,
].join(' ')}
>
{info.expressNo}
</a>
))}
</div>
) : (
''
)}
{item[key]}
</div>
);
});
}
return '';
};
const PopoverNotice = noticeProps => {
const { time, content } = noticeProps || {};
return (
<div className={style['popover-notice']}>
{time}{content}
</div>
);
};
const columns = [
{
title: '订单号',
dataIndex: 'orderNo',
key: 'orderNo',
order: 6,
width: 250,
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, platformRemarkList } = 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>
) : (
''
)}
{platformRemarkList && platformRemarkList.length ? (
<Popover
placement="top"
title="平台备注"
content={<GoodsRemark dataSource={platformRemarkList} />}
trigger="hover"
>
<Button block type="warning">
平台备注
</Button>
</Popover>
) : (
''
)}
</div>
);
},
},
{
title: '商品名称',
dataIndex: 'skuName',
key: 'skuName',
order: 6,
hideInTable: true,
},
{
title: '超时发货',
dataIndex: 'orderStatus',
key: 'orderStatus',
hideInTable: true,
formItemProps: {
// eslint-disable-next-line no-nested-ternary
value: orderStatus ? (orderStatus === 1 ? '' : '') : null,
onChange(status) {
setorderStatus(Number(status));
},
},
valueEnum: {
1: '',
2: '',
},
},
{
title: '交易时间',
dataIndex: 'orderTime',
key: 'orderTime',
hideInSearch: true,
width: 200,
},
{
title: '商品名称',
dataIndex: 'skuName',
key: 'skuName',
width: 400,
className: 'colStyle',
hideInSearch: true,
render: (_, record) => renderContent(record, 'skuName'),
},
{
title: '商品规格',
dataIndex: 'skuAttr',
key: 'skuAttr',
width: 200,
className: 'colStyle',
hideInSearch: true,
render: (_, record) => renderContent(record, 'skuAttr'),
},
{
title: '商品数量',
dataIndex: 'count',
key: 'count',
width: 100,
className: 'colStyle',
hideInSearch: true,
render: (_, record) => renderContent(record, 'count'),
},
{
title: '商品自编码',
dataIndex: 'thirdSpuNo',
key: 'thirdSpuNo',
width: 150,
hideInSearch: true,
className: 'colStyle',
render: (_, record) => renderContent(record, 'thirdSpuNo'),
},
{
title: '收货人手机',
dataIndex: 'receiverPhone',
key: 'receiverPhone',
order: 4,
width: 150,
},
{
title: '收货人姓名',
dataIndex: 'receiverName',
key: 'receiverName',
order: 3,
width: 120,
},
{
title: '收货地址',
dataIndex: 'fullAddress',
key: 'fullAddress',
hideInSearch: true,
width: 350,
},
{
title: '物流公司',
dataIndex: 'expressCompanyName',
key: 'expressCompanyName',
width: 120,
className: 'colStyle',
hideInSearch: true,
hideInTable: props.type !== 2,
render: (_, record) => renderContent(record, 'expressCompanyName'),
},
{
title: '物流单号',
dataIndex: 'deliveryNo',
key: 'deliveryNo',
width: 210,
className: 'colStyle',
hideInSearch: true,
hideInTable: props.type !== 2,
render: (_, record) => renderContent(record, 'deliveryNo'),
},
// {
// title: '物流信息',
// dataIndex: 'action',
// key: 'action',
// width: 150,
// hideInSearch: true,
// hideInTable: props.type !== 2,
// className: 'colStyle',
// render: (_, record) => renderContent(record, 'action'),
// },
{
title: '订单状态',
dataIndex: 'orderStatusDesc',
key: 'orderStatusDesc',
width: 120,
hideInSearch: true,
},
{
title: '订单开始时间',
dataIndex: 'startTime',
key: 'startTime',
valueType: 'date',
hideInTable: true,
formItemProps: {
value: startTime,
onChange(time, timeString) {
setStartTime(time);
setStartTimeStr(timeString);
},
// disabledDate: startDisabledDate,
allowClear: false,
},
},
{
title: '订单结束时间',
dataIndex: 'endTime',
key: 'endTime',
valueType: 'date',
hideInTable: true,
formItemProps: {
value: endTime,
onChange(time, timeString) {
setEndTime(time);
setEndTimeStr(timeString);
},
// disabledDate: endDisabledDate,
allowClear: false,
},
},
{
title: '操作',
dataIndex: 'option',
key: 'option',
valueType: 'option',
width: 150,
fixed: 'right',
render: (_, record) => (
<React.Fragment>
{canEditable && ShowUpdateBtn.includes(record?.skuSource?.value) ? null : (
<Button
type="primary"
style={{
marginBottom: '10px',
}}
onClick={async () => {
const res = await apiQueryOrderInfo({
orderNo: record.orderNo,
});
multiLogisticsModalRef.current.open(
{
...record,
packageList: res?.data?.packageList || [],
},
actionRef,
);
}}
>
{props.type === 2 ? '更新物流信息' : '填写物流信息'}
</Button>
)}
{canEditable && props.type !== 2 && (
<Button
type="primary"
className={+record.delayStatus === 1 ? style.btnWarning : ''}
onClick={() => onDelay(record.orderId, +record.delayStatus, record.delayRemark)}
>
{+record.delayStatus === 1 ? '异常订单已报备' : '异常订单报备'}
</Button>
)}
</React.Fragment>
),
},
];
const reload = () => {
handleModalVisible(false);
if (actionRef.current) {
actionRef.current.reload();
}
};
useEffect(() => {
const getCompanys = async () => {
const data = await queryExpress();
setCompanys(data);
};
getCompanys();
if (props.location?.state) {
const { status } = props.location?.state;
setorderStatus(status);
} else {
setorderStatus('');
}
}, []);
// 监听路由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 transformedParam = {
...params,
logisticsStatus: props.type || 1,
pageNo: params.current,
pageSize: params.pageSize || 20,
timeOutType: orderStatus || '',
orderNo,
};
return queryToSend(transformedParam);
};
const searchRender = ({ searchText, resetText }, { form }) => [
<Button
key="search"
type="primary"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
form?.submit();
}}
>
{searchText}
</Button>,
<Button
key="rest"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
form?.resetFields();
setorderStatus('');
setOrderNo('');
// eslint-disable-next-line no-unused-expressions
form?.submit();
}}
>
{resetText}
</Button>,
];
const toolBarRenderFn = () => [
<Button
type="primary"
onClick={() => {
if (ref.current) {
const obj = {
...ref.current.getFieldsValue(),
orderNo,
logisticsStatus: props.type || 1,
};
obj.startTime = startTimeStr;
obj.endTime = endTimeStr;
downOrder(obj);
}
}}
>
导出数据
</Button>,
];
return (
<PageHeaderWrapper>
<ProTable
className={style.protable}
actionRef={actionRef}
request={params =>
queryToSendFn({ ...params, startTime: startTimeStr, endTime: endTimeStr })
}
columns={columns}
rowKey={r => r.orderNo}
formRef={ref}
toolBarRender={toolBarRenderFn}
search={{
collapsed: false,
optionRender: searchRender,
}}
bordered
scroll={{ x: 1500 }}
/>
<LogisticsForm
onSubmit={reload}
skuList={skuList}
company={companys}
onCancel={() => handleModalVisible(false)}
modalVisible={LogisticsModalVisible}
value={LogisticsData}
/>
<LogisticsCom
onSubmit={reload}
onCancel={() => handleComModalVisible(false)}
modalVisible={LogisticsComModalVisible}
value={LogisticsComList}
key={LogisticsComList.key}
/>
<DelayDeliverGoods
visible={visibleDelay}
orderId={delayOrderIDs}
onCancel={e => onCancelDelay(e)}
/>
<MultiLogisticsModal companys={companys} ref={multiLogisticsModalRef} />
</PageHeaderWrapper>
);
};
export default connect(({ menu }) => ({
permissions: menu.permissions,
}))(TableList);
@import '~antd/es/style/themes/default.less';
.protable {
:global {
.tableContent {
display: flex;
align-items: center;
// height: 60px;
min-height: 60px;
padding: 0 12px;
}
.border {
border-bottom: 1px solid #e8e8e8;
}
.subContent {
flex: 1;
padding: 12px;
overflow: hidden;
line-height: 36px;
white-space: nowrap;
text-overflow: ellipsis;
}
.expressList {
display: flex;
flex-direction: column;
width: calc(100% + 24px);
height: 100%;
margin: -12px;
}
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) !important;
border-color: rgb(247, 143, 74) !important;
}
.btnWarning:hover,
.btnWarning:focus {
background-color: rgb(253, 168, 111) !important;
border-color: rgb(253, 168, 111) !important;
}
.tableList {
.tableListOperator {
margin-bottom: 16px;
button {
margin-right: 8px;
}
}
}
.tableListForm {
:global {
.ant-form-item {
display: flex;
margin-right: 0;
margin-bottom: 15px;
> .ant-form-item-label {
width: auto;
padding-right: 8px;
line-height: 32px;
}
.ant-form-item-control {
line-height: 32px;
}
}
.ant-form-item-control-wrapper {
flex: 1;
}
}
.submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
}
.button {
margin-right: 10px;
}
.buttonWrap {
margin: 10px 0 15px 0;
button {
margin-right: 10px;
}
}
.subContent {
height: 400px;
overflow: auto;
}
.wrapCont {
overflow: hidden;
}
.treeCont {
float: left;
width: 20%;
overflow: auto;
}
.tableCont {
float: right;
width: 78%;
}
.logistics {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 30px;
margin-top: 24px;
}
.logisticsIcon {
font-size: 25px;
&:first-child {
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;
}
import * as api from './service';
const tableDate = [{}];
const Model = {
namespace: 'popPendingDeliveryOrder',
state: {
tableData: [],
typeList: [],
organizationList: [],
},
effects: {
*getSubjectList({ payload }, { call, put }) {
yield put({
type: 'saveData',
payload: {
tableData: tableDate,
},
});
// const response = yield call(api.subjectList, payload);
// if (response.code === 2000) {
// yield put({
// type: 'saveData',
// payload: {
// tableData: response.data,
// },
// });
// }
},
*subjectTypeList({ payload }, { call, put }) {
// const response = yield call(api.subjectTypeList, payload);
// if (response.code === 2000) {
// yield put({
// type: 'saveData',
// payload: {
// typeList: response.data,
// },
// });
// }
},
*organizationList({ payload }, { call, put }) {
// const response = yield call(api.organizationList, payload);
// if (response.code === 2000) {
// yield put({
// type: 'saveData',
// payload: {
// organizationList: response.data,
// },
// });
// }
},
},
reducers: {
saveData(state, action) {
const data = action.payload;
return { ...state, ...data };
},
},
};
export default Model;
import { stringify } from 'querystring';
import _ from 'lodash';
import request from '@/utils/request';
import { saveAs } from 'file-saver';
import { format } from 'date-fns';
import config from '../../../../config/env.config';
// 待发货订单
export async function queryToSend(params) {
try {
const {
data: { current, records, total, size },
} = await request.post('/api/merchants/orders/list', {
prefix: config.kdspApi,
data: stringify(_.omitBy(params, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return {
current,
data: records.map(v => ({ ...v, logisticsStatus: `_${v.logisticsStatus}` })),
total,
pageSize: size,
};
} catch (error) {
return {};
}
}
// 快递公司
export async function queryExpress() {
try {
const { data } = await request.get('/api/merchants/companies/list', {
prefix: config.kdspApi,
});
return data;
} catch (error) {
return {};
}
}
export async function getGoods(orderId) {
const { data } = await request.get(`/api/merchants/orders/skus/list?orderId=${orderId}`, {
prefix: config.kdspApi,
});
return data;
}
export async function uploadFile(file) {
const params = new FormData();
params.append('file', file);
const data = await request.post('/api/merchants/orders/deliveries/batches/import', {
data: params,
prefix: config.kdspApi,
});
return data;
}
export function downTemplate() {
window.location.href = 'https://sc-img.q-gp.com/orders/templates/batch_deliveriesV2.xlsx';
}
export async function downOrder(params) {
const data = await request.post('/api/merchants/orders/export', {
data: stringify(_.omitBy(params, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
prefix: config.kdspApi,
responseType: 'arrayBuffer',
});
const blob = new Blob([data]);
saveAs(blob, `商户订单列表-${format(new Date(), 'yyyyMMddHHmmss')}.xlsx`);
}
// 批量发货订单
export async function queryToBatchSend(params) {
const transformedParam = {
pageNo: params.current,
pageSize: params.pageSize || 20,
startTime: params.dateTimeRange?.[0],
endTime: params.dateTimeRange?.[1],
status: params?.status ?? 99,
batchNo: params.batchNo,
};
const {
data: { current, records, total, size },
} = await request.get('/api/merchants/deliveries/batchlist', {
prefix: config.kdspApi,
params: _.omitBy(transformedParam, v => !v),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return {
current,
data: records.map(v => ({ ...v })),
total,
pageSize: size,
};
}
export async function downUploadeOrder(params) {
const data = await request.get('/api/merchants/deliveries/batchexport', {
params,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
prefix: config.kdspApi,
responseType: 'arrayBuffer',
});
const blob = new Blob([data]);
const status = {
0: '失败数据',
1: '成功数据',
2: '全部数据',
};
saveAs(blob, `批量发货-${status[params.status]}-${format(new Date(), 'yyyyMMddHHmmss')}.xlsx`);
}
// 延迟发货
export function apiDelayDeliverGoods(data) {
return request.post('/api/merchants/orders/logs/add', {
data,
prefix: config.kdspApi,
});
}
/**
* 多物流发货-查询多物流订单信息
* @param {*} params
* @returns
* @see http://yapi.quantgroups.com/project/389/interface/api/45840
*/
export function apiQueryOrderInfo(params) {
return request.get('/api/merchants/orders/deliveries/packages/detail', {
params,
prefix: config.kdspApi,
});
}
/**
* 多物流发货-新建发货/更新发货信息
* @param {*} data
* @returns
* @see http://yapi.quantgroups.com/project/389/interface/api/45816
*/
export function apiDeliveriesAdd(data) {
return request.post('/api/merchants/orders/deliveries/add', {
data,
prefix: config.kdspApi,
});
}
export function apiDeliveriesEdit(data) {
return request.post('/api/merchants/orders/deliveries/edit', {
data,
prefix: config.kdspApi,
});
}
/**
* 多物流发货-查询物流轨迹
* @see http://yapi.quantgroups.com/project/389/interface/api/46120
*/
export function apiDeliveriesTraceList(data) {
return request.post('/api/merchants/deliveries/traces/list', {
data: stringify(data),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
prefix: config.kdspApi,
});
}
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