Commit 885f1aaa authored by 武广's avatar 武广

Merge branch 'master' of git.quantgroup.cn:ui/merchant-manage-ui into feature/draft

parents 12399379 a53c3146
......@@ -146,6 +146,13 @@ export default {
icon: 'smile',
component: './AfterSaleManage/index',
},
{
title: '商户管理后台',
path: '/afterSalesClaimForm',
name: 'afterSalesClaimForm',
icon: 'smile',
component: './AfterSalesClaimForm/index',
},
{
title: '商户管理后台',
path: '/afterSaleManageOld',
......
const isProduction = process.env.NODE_ENV === 'production';
const isPre = process.env.PRE_ENV === 'pre';
const environment = 'sc';
const environment = 'yxm2';
const envAPi = {
api: `https://security-${environment}.liangkebang.net`, //'https://security-xyqb.liangkebang.net',
kdspOpApi: `https://sc-merchant-api-${environment}.liangkebang.net`,
......
......@@ -139,7 +139,7 @@ const Simple = props => {
}
const params = {
pageNo: 1,
pageSize: 1000,
pageSize: 200,
};
const data = {
channelId: CHANNEL_ID,
......
import React, { useState, forwardRef, useImperativeHandle, useEffect } from 'react';
import { Modal, Timeline, notification, Spin } from 'antd';
import { getLogisticsRecord } from '../services';
import { Modal, Timeline, notification, Spin, Collapse } from 'antd';
import { getLogisticsRecord, apiDeliveriesTraceList } from '../services';
import styles from '../index.less';
const { Panel } = Collapse;
const LogisticsRecordModal = (props, ref) => {
// const { } = props;
const [visible, setVisible] = useState(false);
const [result, setResult] = useState({});
const [loading, setLoading] = useState(false);
const [dataList, setDataList] = useState([]);
const [isSingle, setIsSingle] = useState(false);
const getRecordList = async orderNo => {
const tempObj = {
detailList: [],
key: Date.now(),
};
const getRecordList = async params => {
setLoading(true);
const res = await getLogisticsRecord({ orderNo });
const res = await apiDeliveriesTraceList(params);
setLoading(false);
if (!res) {
notification.info({ message: '暂无物流信息' });
return;
}
const { logisticsName, logisticsBillNo, logisticsList = [] } = res.data;
tempObj.expressCompanyName = logisticsName;
tempObj.deliveryNo = logisticsBillNo;
logisticsList.forEach(v => {
tempObj.detailList = [...tempObj.detailList, ...v.detailList];
});
setResult(tempObj);
setDataList(res.data);
};
const open = orderNo => {
const open = (record, type) => {
setVisible(true);
getRecordList(orderNo);
getRecordList(record);
if (type === 'single') {
setIsSingle(true);
}
};
const onCancel = () => {
setVisible(false);
setResult([]);
setTimeout(() => {
setDataList([]);
}, 1000);
};
useImperativeHandle(ref, () => ({
open,
......@@ -59,18 +59,70 @@ const LogisticsRecordModal = (props, ref) => {
return (
<Modal {...modalProps}>
<Spin spinning={loading}>
{result.detailList?.length ? (
{dataList.length ? (
dataList.map((dataItem, dataIndex) => (
<div className={styles['logistics-record']}>
{!isSingle ? (
<div className={styles['logistics-record__topbar']}>包裹{dataIndex + 1}</div>
) : (
''
)}
<div className={styles['logistics-record__head']}>
<span className={styles.name}>物流公司:{dataItem.expressCompanyName}</span>
<span className={styles.no}>物流单号:{dataItem.expressNo}</span>
</div>
{dataItem.traceList.map(traceItem => (
<Timeline>
{result?.detailList?.map((item, index) => (
{traceItem?.detailList.length ? (
traceItem?.detailList?.map((item, index) => (
<Timeline.Item color={index > 0 ? 'gray' : 'blue'} key={index.toString()}>
<p>{item.desc}</p>
<p>{item.logisticsTime}</p>
<p>{item.time}</p>
</Timeline.Item>
))
) : (
<div style={emptyStyle}>暂无物流信息</div>
)}
</Timeline>
))}
</div>
))
) : (
<div style={emptyStyle}>暂无物流信息</div>
)}
{/* 折叠 */}
{/* <Collapse defaultActiveKey={['0']} ghost >
{dataList.length ? (
dataList.map((dataItem, dataIndex) => (
<Panel showArrow={false} header={<div className={styles['logistics-record__topbar']}>包裹{dataIndex + 1}</div>} key={dataIndex.toString()}>
<div className={styles['logistics-record']}>
<div className={styles['logistics-record__head']}>
<span className={styles.name}>物流公司:{dataItem.expressCompanyName}</span>
<span className={styles.no}>物流单号:{dataItem.expressNo}</span>
</div>
{dataItem.traceList.map(traceItem => (
<Timeline>
{traceItem?.detailList.length ? (
traceItem?.detailList?.map((item, index) => (
<Timeline.Item color={index > 0 ? 'gray' : 'blue'} key={index.toString()}>
<p>{item.desc}</p>
<p>{item.time}</p>
</Timeline.Item>
))
) : (
<div style={emptyStyle}>暂无物流信息</div>
)}
</Timeline>
))}
</div>
</Panel>
))
) : (
<div style={emptyStyle}>暂无物流信息</div>
)}
</Collapse> */}
</Spin>
</Modal>
);
......
......@@ -105,7 +105,8 @@ const AuditModal = props => {
};
const openLogisticsRecord = () => {
logisticsRecordModalRef.current.open(formData.orderNo);
const { orderNo, skuNo } = formData;
logisticsRecordModalRef.current.open({ orderNo, skuNo });
};
const layout = {
......
......@@ -16,6 +16,7 @@ import RejectModal from './components/rejectModal';
import LogisticsCom from '../orderManage/pendingDeliveryOrder/components/LogisticsCom';
import CancelAuditModal from './components/CancelAuditModal';
import CancelDetailTable from './components/CancelDetailTable';
import LogisticsRecordModal from './components/LogisticsRecordModal';
import { getColumns, getFormConfig, TAB_MAPPING_DATA } from './data.js';
import { getDetail } from '@/pages/afterSale/appeal/services';
......@@ -44,6 +45,7 @@ const AfterSale = props => {
const actionRef = useRef();
const formRef = useRef();
const logisticsRecordModalRef = useRef();
const [tableParams, setTableParams] = useState({});
const [currentTab, setCurrentTab] = useState('');
// const [appealDetailModal, setAppealDetailModal] = useState(false);
......@@ -125,32 +127,35 @@ const AfterSale = props => {
};
// 审核
const openAudit = async ({ serviceNo, serviceType, orderNo }) => {
const openAudit = async ({ serviceNo, serviceType, orderNo, skuNo }) => {
const data = await auditInfoApi({ serviceNo });
setAuditInfo({ ...data?.data, serviceNo, serviceType, orderNo });
setAuditInfo({ ...data?.data, serviceNo, serviceType, orderNo, skuNo });
setVisible(true);
};
// 查看物流
const handleCom = async ({ expressCompanyCode, deliveryNo }) => {
const tempObj = {
detailList: [],
key: Date.now(),
};
const data = await trackInfo({ expressCompanyCode, logisticsNo: deliveryNo });
if (!data) {
notification.info({ message: '暂无物流信息' });
return;
}
tempObj.expressCompanyName = data.logisticsName;
tempObj.deliveryNo = data.logisticsBillNo;
if (data.logisticsList?.length) {
data.logisticsList.forEach(v => {
tempObj.detailList = [...tempObj.detailList, ...v.detailList];
});
}
setLogisticsComModalVisible(true);
setLogisticsComList(tempObj);
const handleCom = async record => {
console.log('record', record);
const { expressCompanyCode, deliveryNo: expressNo } = record;
logisticsRecordModalRef.current.open({ expressCompanyCode, expressNo }, 'single');
// const tempObj = {
// detailList: [],
// key: Date.now(),
// };
// const data = await trackInfo({ expressCompanyCode, logisticsNo: deliveryNo });
// if (!data) {
// notification.info({ message: '暂无物流信息' });
// return;
// }
// tempObj.expressCompanyName = data.logisticsName;
// tempObj.deliveryNo = data.logisticsBillNo;
// if (data.logisticsList?.length) {
// data.logisticsList.forEach(v => {
// tempObj.detailList = [...tempObj.detailList, ...v.detailList];
// });
// }
// setLogisticsComModalVisible(true);
// setLogisticsComList(tempObj);
};
// 物流拦截
......@@ -439,6 +444,8 @@ const AfterSale = props => {
onCancel={closeModal}
dataSource={cancelDetailInfo}
/>
<LogisticsRecordModal ref={logisticsRecordModalRef} />
</PageHeaderWrapper>
);
};
......
......@@ -59,3 +59,24 @@
}
}
}
.logistics-record {
&__topbar {
margin-bottom: 15px;
padding-left: 10px;
color: #000;
font-weight: bold;
font-size: 16px;
border-left: 10px solid #2391fe;
}
&__head {
padding-bottom: 25px;
color: #000;
font-weight: 500;
font-size: 14px;
line-height: 1;
.no {
margin-left: 20px;
}
}
}
......@@ -131,6 +131,21 @@ export async function getLogisticsRecord(params) {
});
}
/**
* 多物流发货-查询物流轨迹
* @see http://yapi.quantgroups.com/project/389/interface/api/46120
*/
export function apiDeliveriesTraceList(data) {
return request.post('/api/merchants/deliveries/trace/list', {
data: stringify(data),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
prefix: kdspApi,
});
}
// 查询售后待办数量
export function getAfterPendingNum(data) {
return request.post('/api/kdsp/op/afs/getPendingNum', {
......
import React, { useState, forwardRef, useImperativeHandle } from 'react';
import { Form } from '@ant-design/compatible';
import { Modal, Button } from 'antd';
// 0: 待财务审核
// 1: 审核通过
// 2: 审核拒绝,
const ExamineFormModal = forwardRef((props, ref) => {
const { form, claimVisible, closeClaimDescription, refresh } = props;
const [detailData, setDetailData] = useState({});
useImperativeHandle(ref, () => ({
setDetailData,
...form,
}));
const onCancelEvent = () => {
closeClaimDescription();
};
const footerButton = [
<Button type="primary" key="cancel" onClick={onCancelEvent}>
关闭
</Button>,
];
return (
<Modal
title="赔款说明"
centered
visible={claimVisible}
footer={detailData.audit ? footerButton : null}
bodyStyle={{ padding: '12px 0' }}
onCancel={onCancelEvent}
></Modal>
);
});
export default Form.create()(ExamineFormModal);
import React, { useState, useRef, useEffect } from 'react';
import ProTable from '@ant-design/pro-table';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { Button, Image } from 'antd';
import { saveAs } from 'file-saver';
import { columnsConfig, getLastItem, checkImage } from './staticdata';
import { getQueryClamsList } from './service';
import ClaimDescription from './ClaimDescription';
/**
* 售后赔款单
* 售后赔款单
* @param {*} router options
* @returns ReactDOM
*/
const AfterSalesClaimForm = ({ route }) => {
const ref = useRef();
const actionRef = useRef();
const [claimVisible, setclaimVisible] = useState(false);
const [claimLoading, setclaimLoading] = useState(false);
const [imagePreView, setImagePreView] = useState({
src: '',
visible: false,
});
const search = {
collapsed: false,
collapseRender: () => null,
};
const query = async params => {
const { createdAt, singleType, singleNumber, current: pageNo, ...other } = params;
const queryParams = { ...other, pageNo };
if (singleType && singleNumber) {
if (+singleType === 0) queryParams.id = singleNumber;
if (+singleType === 1) queryParams.orderNo = singleNumber;
}
if (createdAt?.length) {
const [startTime, endTime] = createdAt;
queryParams.startTime = startTime;
queryParams.endTime = endTime;
}
const { data } = await getQueryClamsList(queryParams);
return {
total: data.total,
data: data?.records,
};
};
const openSetclaimModal = async (row, audit) => {
setclaimLoading(true);
setclaimVisible(true);
setclaimLoading(false);
};
const closeClaimDescription = () => {
setclaimVisible(false);
};
const refreshTable = () => {
ref.current.reload();
};
// eslint-disable-next-line consistent-return
const downLoadFile = async url => {
if (await checkImage(url)) {
setImagePreView({ src: url, visible: true });
return null;
}
saveAs(url, getLastItem(url));
};
// const toolBarRender = () => [
// <Button type="link" onClick={openSetclaimModal}>
// 赔款说明
// </Button>,
// ];
// toolBarRender={toolBarRender}
const onReset = () => {};
return (
<PageHeaderWrapper>
<ProTable
bordered
loading={claimLoading}
type="cardList"
request={query}
tableClassName="capital"
columns={columnsConfig({ downLoadFile })}
rowKey={row => row.id}
search={search}
options={false}
actionRef={ref}
pagination={{ pageSize: 10 }}
onReset={onReset}
scroll={{ x: '100%', y: 430 }}
/>
<ClaimDescription
refresh={refreshTable}
claimVisible={claimVisible}
wrappedComponentRef={actionRef}
closeClaimDescription={closeClaimDescription}
/>
<Image
width={200}
style={{ display: 'none' }}
src={imagePreView.src}
preview={{
visible: imagePreView.visible,
scaleStep: '1',
src: imagePreView.src,
onVisibleChange: value => {
setImagePreView({
src: '',
visible: false,
});
},
}}
/>
</PageHeaderWrapper>
);
};
export default AfterSalesClaimForm;
import request from '@/utils/request';
import config from '@/../config/env.config';
export const getQueryClamsList = params =>
request.post('/api/kdsp/queryClams', {
prefix: config.goodsApi,
data: params || {},
});
import React from 'react';
import { Image as ImageComponent, Button } from 'antd';
export const getLastItem = thePath => thePath.substring(thePath.lastIndexOf('/') + 1);
export const isImageType = url => {
const index = url.lastIndexOf('.');
const ext = url.substr(index + 1);
return ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp', 'psd', 'svg', 'tiff'].includes(
ext.toLowerCase(),
);
};
export const checkImage = url =>
new Promise(resolve => {
const image = new Image();
image.onload = () => {
resolve(true);
};
image.onerror = () => {
resolve(false);
};
image.src = url;
});
const csListStyle = {
color: '#1890FF',
textAlign: 'left',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
cursor: 'pointer',
};
export const typeConfig = {
1: {
text: '退运费',
},
2: {
text: '商品破损',
},
};
const singleNumberSearch = {
0: {
text: '赔款单号',
},
1: {
text: '订单编号',
},
};
export const columnsConfig = props => [
{
title: '单号搜索',
key: 'singleType',
dataIndex: 'singleType',
hideInTable: true,
valueEnum: singleNumberSearch,
},
{
key: 'singleNumber',
dataIndex: 'singleNumber',
hideInTable: true,
},
{
title: '赔款单号',
key: 'id',
dataIndex: 'id',
align: 'center',
hideInSearch: true,
},
{
title: '订单号',
key: 'orderNo',
dataIndex: 'orderNo',
align: 'center',
hideInSearch: true,
},
{
title: '赔款金额',
key: 'amount',
dataIndex: 'amount',
align: 'center',
width: 100,
hideInSearch: true,
},
{
title: '赔款类型',
key: 'type',
dataIndex: 'type',
align: 'center',
valueEnum: typeConfig,
},
{
title: '索赔原因',
key: 'reason',
dataIndex: 'reason',
align: 'center',
hideInSearch: true,
},
{
title: '退款说明',
key: 'remark',
dataIndex: 'remark',
align: 'center',
ellipsis: true,
hideInSearch: true,
},
{
title: '创建时间',
key: 'createdAt',
dataIndex: 'createdAt',
align: 'center',
valueType: 'dateRange',
},
{
title: '用户凭证',
key: 'proof',
dataIndex: 'proof',
align: 'center',
width: 190,
hideInSearch: true,
render: value =>
(value.userList || []).map(item => (
<ImageComponent style={{ paddingLeft: 5, marginTop: 5 }} key={item} width={50} src={item} />
)),
},
{
title: '审核意见',
key: 'proof',
dataIndex: 'proof',
align: 'center',
width: 190,
hideInSearch: true,
render: value =>
(value.csList || []).map(item => (
<div style={csListStyle} onClick={() => props.downLoadFile(item)}>
{item}
</div>
)),
},
];
......@@ -2,6 +2,7 @@ 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';
import UUID from '../../../utils/uuid';
import { qiniuToken } from '@/services/qiniu';
const qiniu = require('@/utils/qiniu.min.js');
......@@ -60,7 +61,7 @@ class PicturesWall extends React.Component {
}
const vm = this;
// eslint-disable-next-line new-cap
const data = `${filename}-${new Date().getTime()}.${suffix}`;
const data = `${UUID.createUUID()}.${suffix}`;
const observable = qiniu.upload(file, data, token);
const observer = {
next() {
......
......@@ -104,8 +104,14 @@ export function columns(res, pages) {
align: 'center',
},
{
title: '蓝字金额',
title: () => (
<div style={{ textAlign: 'center' }}>
<div>蓝字金额</div>
<div style={{ color: '#f00' }}>(开票金额)</div>
</div>
),
dataIndex: 'currentRefundAmount',
key: 'currentRefundAmount',
width: '100px',
hideInSearch: true,
align: 'center',
......@@ -113,9 +119,16 @@ export function columns(res, pages) {
{
title: '应付金额',
dataIndex: 'payableAmount',
width: '100px',
width: '140px',
hideInSearch: true,
align: 'center',
render: (value, row) => (
<div>
<div>{row.payableAmount}</div>
<div style={{ color: '#f00' }}>赔付 ({row.claimAmount})</div>
<div>(实付 {row.needPayAmount})</div>
</div>
),
},
{
title: '是否缺少红字发票',
......
......@@ -68,6 +68,15 @@ export function columns(pages) {
width: '100px',
hideInSearch: true,
align: 'center',
render: (value, row) => {
const msg = row.billType === 20 ? <div>包含赔付金额</div> : null;
return (
<div>
<div>{value}</div>
{msg}
</div>
);
},
},
{
title: '笔数',
......@@ -75,6 +84,15 @@ export function columns(pages) {
width: '100px',
hideInSearch: true,
align: 'center',
render: (value, row) => {
const msg = row.billType === 20 ? <div>包含赔付笔数</div> : null;
return (
<div>
<div>{value}</div>
{msg}
</div>
);
},
},
{
title: '状态',
......
......@@ -75,6 +75,11 @@
.pictureWrapper {
position: relative;
:global {
.ant-form-item-label > label {
word-break: break-all;
}
}
}
.pullImage {
position: absolute;
......
import React, { useState } from 'react';
import React from 'react';
import { Select, Form, InputNumber, Input, Button, Popover } from 'antd';
import commonStyle from '../common.less';
......@@ -108,7 +108,12 @@ export const CreateFormInput = props => {
return (
<>
{record.skuLink && (
<Button type="primary" size="small" onClick={() => onClickEvent('cloneImg', record)}>
<Button
type="primary"
size="small"
disabled={props.disabled}
onClick={() => onClickEvent('cloneImg', record)}
>
拉图片
</Button>
)}
......
......@@ -120,7 +120,18 @@ const EditFormTable = forwardRef((props, ref) => {
tableList: newDataSource,
});
};
const onClickEvent = () => {};
const onClickEvent = (type, row) => {
// 点击拉取京东图片功能
if (type === 'cloneImg') {
customer.onEventBus(type, row);
return;
}
// 修改sku名称
if (type === 'updateName') {
rowOnClickEvent(row);
}
};
useImperativeHandle(ref, () => ({
onCheck,
......@@ -179,8 +190,8 @@ const EditFormTable = forwardRef((props, ref) => {
<Form form={form} scrollToFirstError component={false} onValuesChange={getFormValues}>
<EditableContext.Provider value={form}>
<Table
scroll={{ y: 300, x: 1000 }}
height={300}
scroll={{ y: 320, x: 1000 }}
height={320}
pagination={false}
size="small"
bordered
......
......@@ -93,6 +93,9 @@ const FormAttr = forwardRef((props, ref) => {
setInitAttrData(initValue.initValue);
setCategoryAttrs(res.data);
});
} else {
setInitAttrData({});
setCategoryAttrs([]);
}
};
......@@ -169,6 +172,9 @@ const FormAttr = forwardRef((props, ref) => {
const ids = props.categoryIds;
if (ids && ids.length) {
getAttribute(ids[ids.length - 1]);
} else {
setInitAttrData({});
setCategoryAttrs([]);
}
}, [props.categoryIds]);
......
......@@ -97,6 +97,7 @@ const FormInformationBasic = forwardRef((props, ref) => {
>
<Cascader
placeholder="请选择商品类目!"
disabled={customer.isEdit && customer.isNormal}
showSearch={{ filter: filterCategoryOptions }}
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
onChange={props.onCategoryChange}
......@@ -111,8 +112,8 @@ const FormInformationBasic = forwardRef((props, ref) => {
extra="若需新增品牌请联系业务员"
>
<Select
disabled={customer.isService}
showSearch
disabled={customer.isDisabled}
placeholder="请选择商品品牌"
filterOption={fileterBrandOptions}
>
......@@ -131,14 +132,9 @@ const FormInformationBasic = forwardRef((props, ref) => {
{ required: true, min: 2, message: '请输入最少两个字符的商品名称!', whitespace: true },
]}
>
<Input placeholder="请输入商品名称" />
<Input placeholder="请输入商品名称" disabled={customer.isDisabled} />
</Form.Item>
</Popover>
{customer.isJDGoods && (
<Button key="jdMsg" danger type="text">
*本列表的商品名称仅供搜索使用,不在前端作展示。若要修改APP端展示的商品名称,请在商品信息中修改。
</Button>
)}
{!customer.isCard && (
<Form.Item
name="character"
......
......@@ -59,7 +59,7 @@ const SpecificationTemplate = (props, _) => {
const handleChange = (val, option) => {
const optionSpecName = option ? option.specName : null;
form.setFieldsValue({ [selectName]: optionSpecName });
// onChange();
onChange();
};
const inputOnblurEvent = event => {
......@@ -78,11 +78,14 @@ const SpecificationTemplate = (props, _) => {
return;
}
addCallback();
onChange();
};
const bundlePlusRemoveSpecEvent = (removeCallback, fieldName) => {
removeCallback(fieldName);
form.setFieldsValue({
bacthFirst: undefined,
bacthSecon: undefined,
});
const timer = setTimeout(() => {
onChange();
clearTimeout(timer);
......@@ -93,7 +96,7 @@ const SpecificationTemplate = (props, _) => {
<>
<Form.Item name={formName} label={label}>
<Select
disabled={customer.isEdit}
disabled={customer.isEdit && customer.isNormal}
allowClear
options={specList}
style={{ width: 200 }}
......@@ -115,8 +118,9 @@ const SpecificationTemplate = (props, _) => {
{fields.map((field, index) => (
<Form.Item key={field.key} noStyle shouldUpdate={(prevValues, curValues) => false}>
{() => (
<Space key={field.key} align="baseline">
<Space key={`space_${field.key}`} align="baseline">
<Form.Item
key={`item_${field.key}`}
style={{ marginLeft: 10 }}
name={[field.name, specName]}
rules={[
......@@ -144,6 +148,7 @@ const SpecificationTemplate = (props, _) => {
</Form.Item>
{!(specDataList[index] && specDataList[index].id) && (
<MinusCircleOutlined
key={`minus_${field.key}`}
onClick={() => bundlePlusRemoveSpecEvent(remove, field.name)}
/>
)}
......@@ -151,8 +156,8 @@ const SpecificationTemplate = (props, _) => {
)}
</Form.Item>
))}
{fields.length < 3 && (
<Form.Item noStyle>
{(!customer.isCard || fields.length < 3) && !customer.isDisabled && (
<Form.Item noStyle key="btnpush">
<Button
style={{ marginLeft: 10, marginBottom: 24 }}
type="dashed"
......@@ -203,7 +208,12 @@ const CreateBatchFormItems = ({ specInitValue, batchChange, editRef, defaultColu
fieldNames={{ label: 'secondSpecValue', value: 'secondSpecValue' }}
/>
{formItems.concat(
<Button key="batch" type="primary" onClick={batchChange}>
<Button
key="batch"
type="primary"
disabled={customer.isEdit && customer.isNormal}
onClick={batchChange}
>
批量设置
</Button>,
)}
......@@ -226,6 +236,24 @@ const FormPriceOrStock = forwardRef((props, ref) => {
const [tableData, setTableData] = useState([]);
const [mergeTable, setMergeTable] = useState(false);
const insertSpecID = items => {
const sids = items.filter(item => item.id);
if (sids.length !== skuList.length) {
let i = 0;
const ids = skuList.reduce((prev, cur) => {
if (!sids.includes(cur.id)) {
prev.push(cur.id);
}
return prev;
}, []);
items.forEach(item => {
if (!item.id && i < ids.length) {
item.id = ids[i++];
}
});
}
};
const onCheck = async () => {
try {
let setMealContent = '';
......@@ -233,6 +261,14 @@ const FormPriceOrStock = forwardRef((props, ref) => {
setMealContent = await packageRef.current.onCheck();
}
const values = await form.validateFields();
if (!values.firstSpecValue || !values.firstSpecValue.length) {
values.firstSpecId = null;
values.firstSpec = '';
}
if (!values.secondSpecValue || !values.secondSpecValue.length) {
values.secondSpecId = null;
values.secondSpec = '';
}
const items = await editRef.current.onCheck();
if (customer.isCard && !setMealContent) {
notification.open({
......@@ -241,7 +277,10 @@ const FormPriceOrStock = forwardRef((props, ref) => {
});
return null;
}
if (items) {
if (items && items.length) {
if (customer.isEdit && skuList.length) {
insertSpecID(items);
}
const obj = { ...values, items, temp: 'infoSpecData' };
customer.isCard && setMealContent && (obj.setMealContent = setMealContent);
return obj;
......@@ -278,7 +317,6 @@ const FormPriceOrStock = forwardRef((props, ref) => {
inputType: 'text',
});
}
const dynamicColumns = [...columsData, ...StaticColumns(customer)];
setDefaultColumns(dynamicColumns);
};
......@@ -289,8 +327,10 @@ const FormPriceOrStock = forwardRef((props, ref) => {
const cleanValues = {
firstValues: cleanArray(values.firstSpecValue || []),
secondValues: cleanArray(values.secondSpecValue || []),
firstSpecId: values.firstSpecId,
secondSpecId: values.secondSpecId,
firstSpecId:
values.firstSpecValue && values.firstSpecValue.length ? values.firstSpecId : null,
secondSpecId:
values.secondSpecValue && values.secondSpecValue.length ? values.secondSpecId : null,
};
const { inIdList: fisrtInIdList, noIdList: fisrtNoIdList } = filterSkuNotIdList(
......@@ -299,7 +339,6 @@ const FormPriceOrStock = forwardRef((props, ref) => {
const { inIdList: secndInIdList, noIdList: secndNoIdList } = filterSkuNotIdList(
cleanValues.secondValues,
);
const createSkuList = createProductData(
{
firstSpecId: cleanValues.firstSpecId,
......@@ -310,23 +349,25 @@ const FormPriceOrStock = forwardRef((props, ref) => {
secndNoIdList,
},
customer.isEdit,
skuList,
);
CreateColumnsEvent(values);
// 没有规格默认创建一条数据
if (!cleanValues.firstSpecId && !createSkuList.secondSpecId) {
setTableData([...createSkuList]);
return;
}
// 是否合并单元格
setMergeTable(Boolean(cleanValues.secondValues.length));
setTableData(fliterSkuListSortData([...skuList, ...createSkuList]));
setTableData(fliterSkuListSortData([...createSkuList]));
} catch (error) {
console.log(error);
}
};
const batchChange = () => {
const batchItem = form.getFieldValue('batchItem');
const bacthFirst = form.getFieldValue('bacthFirst');
const bacthSecon = form.getFieldValue('bacthSecon');
const values = form.getFieldsValue();
const { batchItem, bacthFirst, bacthSecon } = values;
const resetObject = batchTableSourceData({ batchItem, tableData, bacthSecon, bacthFirst });
props.onValuesChange({ tableList: resetObject });
setTableData(resetObject);
......@@ -335,6 +376,8 @@ const FormPriceOrStock = forwardRef((props, ref) => {
const onSpecificationEvent = async () => {
try {
const values = await form.validateFields();
console.log('values :>> ', values);
console.log('cleanArray(values.secondSpecValue) :>> ', cleanArray(values.secondSpecValue));
const cleanValues = {
firstSpec: values.firstSpec,
firstSpecId: values.firstSpecId,
......@@ -360,10 +403,12 @@ const FormPriceOrStock = forwardRef((props, ref) => {
);
onSpecChange(firstSpecValueList);
}
onFinish();
};
const seconOnChangeEvent = async () => {
await onSpecificationEvent();
onFinish();
};
useImperativeHandle(ref, () => ({
......@@ -417,16 +462,18 @@ const FormPriceOrStock = forwardRef((props, ref) => {
specList={specList}
specDataList={specInitValue.secondSpecValue}
/>
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: 20 }}>
{/* <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 20 }}>
<Button type="primary" onClick={onFinish}>
生成商品信息
</Button>
</div>
</div> */}
<div>
<CreateBatchFormItems
batchChange={batchChange}
specInitValue={specInitValue}
defaultColumns={defaultColumns}
/>
</div>
</Form>
<EditFormTable
ref={editRef}
......
......@@ -98,19 +98,14 @@ const FormRuleSetting = forwardRef((props, ref) => {
>
<Form.Item name="purchaseTime" label="购买时间" {...rangeConfig}>
<RangePicker
showTime={{
defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')],
}}
format="YYYY-MM-DD HH:mm:ss"
defaultPickerValue={[nowDateTime, nowDateTimeEnd]}
/>
</Form.Item>
<Form.Item name="useTime" label="有效期" {...rangeConfig}>
<RangePicker
showTime={{
defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')],
}}
format="YYYY-MM-DD HH:mm:ss"
defaultPickerValue={[nowDateTime, nowDateTimeEnd]}
/>
</Form.Item>
<Form.Item
......
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
import React, { useState, useEffect, useContext, forwardRef, useImperativeHandle } from 'react';
import { Form, Button } from 'antd';
import { ServiceContext } from '../context';
......@@ -73,7 +75,16 @@ const FormRuleVPictures = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
onCheck,
setFieldsValue: form.setFieldsValue,
setFieldsValue: obj => {
setDetailImageList(obj.detailImageList || []);
for (const key in obj.imageList) {
if (!obj.imageList[key]) {
obj.imageList[key] = [];
}
}
setImageList(obj.imageList || {});
form.setFieldsValue(obj);
},
getFieldsValue: form.getFieldsValue,
reset: () => {
form.resetFields();
......@@ -106,6 +117,14 @@ const FormRuleVPictures = forwardRef((props, ref) => {
});
};
// 拉取公共图片
const onPullCommonImg = key => {
const cimg = form.getFieldValue('commonImageList');
const obj = { ...imageList };
obj[key] = cimg;
setImageList(obj);
};
const onDetailSuccessImageList = imgList => {
setDetailImageList(imgList);
form.setFieldsValue({
......@@ -116,7 +135,10 @@ const FormRuleVPictures = forwardRef((props, ref) => {
return (
<Form
form={form}
{...formItemLayout}
labelCol={{ flex: '110px' }}
labelAlign="right"
labelWrap
colon
initialValues={{
commonImageList: [],
cardImageList: [],
......@@ -150,9 +172,12 @@ const FormRuleVPictures = forwardRef((props, ref) => {
]}
>
<UploadImage
disabled={customer.isService}
multiple={false}
multiple={!customer.isCard}
maxWidth={customer.isCard ? 0 : 1200}
maxHeight={customer.isCard ? 0 : 1200}
superTips="公共滑动图尺寸不可大于 1200*1200"
name="commonImageList"
disabled={customer.isDisabled}
limit={imgConfig.commonImageList.limit}
pictures={commonImageList}
setPictureList={list => onCommonSuccessEvent(list)}
......@@ -172,9 +197,9 @@ const FormRuleVPictures = forwardRef((props, ref) => {
]}
>
<UploadImage
disabled={customer.isService}
name="cardImageList"
limit={imgConfig.cardImageList.limit}
disabled={customer.isDisabled}
pictures={cardImageList}
setPictureList={list => onCardSuccessImageList(list)}
/>
......@@ -193,16 +218,23 @@ const FormRuleVPictures = forwardRef((props, ref) => {
extra={imgConfig.imageList.renderExtra()}
>
<UploadImage
disabled={customer.isService}
name={key}
limit={11}
disabled={customer.isDisabled}
pictures={imageList[key]}
setPictureList={list => onPictureSuccessEvent(list, key)}
/>
</Form.Item>
<Button className={commonStyle.pullImage} type="primary">
{!imageList[key]?.length && (
<Button
disabled={imageList[key]?.length}
className={commonStyle.pullImage}
onClick={() => onPullCommonImg(key)}
type="primary"
>
拉取公共图
</Button>
)}
</div>
))}
<Form.Item
......@@ -218,9 +250,11 @@ const FormRuleVPictures = forwardRef((props, ref) => {
extra={imgConfig.detailImageList.renderExtra()}
>
<UploadImage
disabled={customer.isService}
limit={imgConfig.detailImageList.limit}
maxWidth={customer.isCard ? 0 : 800}
superTips="详情图宽度不可大于800"
name="detailImageList"
disabled={customer.isDisabled}
pictures={detailImageList}
setPictureList={list => onDetailSuccessImageList(list)}
/>
......
......@@ -72,26 +72,83 @@ const UploadImage = forwardRef((props, ref) => {
const freshFiles = fileList?.filter(ele => ele.uid !== file.uid);
bundleChange(freshFiles);
};
const checkFile = file =>
new Promise(resolve => {
const curType = file.name.substr(file.name.lastIndexOf('.') + 1).toLowerCase();
const fileType = ['jpg', 'jpeg', 'png'];
if (!fileType.includes(curType)) {
notification.open({
message: file.name,
description: '图片格式须为jpg、jpeg、png!',
const cleanArray = (actual = []) =>
actual.reduce((prev, cur) => {
cur && prev.push(cur);
return prev;
}, []);
const warningTip = description => {
notification.warning({
message: '图片上传失败',
description,
});
return resolve(null);
};
const getBase64 = (img, callback) => {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
};
const ImageInfo = file =>
new Promise((resolve, reject) => {
const LtMB = file.size / 1024 / 1024;
if (LtMB > 2) {
warningTip(`[${file.name}] 图片不可以大于2MB`);
resolve(null);
}
if (file.size > MAX_FILE_SIZE * UNIT) {
notification.open({
message: file.name,
description: `单个图片大小不能超过${MAX_FILE_SIZE}M!`,
getBase64(file, url => {
const image = new Image();
image.addEventListener('load', () => {
const { width } = image;
const { height } = image;
file.width = width;
file.height = height;
file.LtMB = LtMB;
resolve(file);
});
image.addEventListener('error', () => {
warningTip(`${file.name}图片上传失败!`);
resolve(null);
});
image.src = url;
});
return resolve(null);
});
const CheckImageInfoList = async files => {
const promiseImage = files.map(file => ImageInfo(file));
const clearImage = await Promise.all(promiseImage);
return cleanArray(clearImage);
};
const isUploadNext = async imgFileList => {
const filterImage = imgFileList.filter(img => {
if (
(imgOptions.maxWidth && img.width > imgOptions.maxWidth) ||
(imgOptions.maxHeight && img.height > imgOptions.maxHeight)
) {
warningTip(`[${img.name}] ${imgOptions.superTips}`);
return false;
}
return resolve(file);
return true;
});
return filterImage;
};
const checkFile = files => {
const fileType = ['jpg', 'jpeg', 'png'];
const filterImage = files.filter(file => {
const curType = file.name.substr(file.name.lastIndexOf('.') + 1).toLowerCase();
if (!fileType.includes(curType)) {
warningTip('图片格式须为jpg、jpeg、png!');
return false;
}
return true;
});
return filterImage;
};
const imageLoading = (file, ret) =>
new Promise(resolve => {
......@@ -119,12 +176,14 @@ const UploadImage = forwardRef((props, ref) => {
});
return Upload.LIST_IGNORE;
}
const fileAll = fileArray.map(item => checkFile(item));
const checkFiles = (await Promise.all(fileAll)).filter(item => item !== null);
const flies = checkFile(fileArray);
const optionsArray = await CheckImageInfoList(flies);
const checkFiles = await isUploadNext(optionsArray);
try {
if (checkFiles.length) {
setUploadLoading(true);
const res = await merchantUpload(checkFiles);
console.log('res :>> ', res);
if (res.data) {
const proFiles = (res.data || []).map((urlItem, urlIndex) =>
imageLoading(checkFiles[urlIndex], urlItem),
......@@ -176,10 +235,12 @@ const UploadImage = forwardRef((props, ref) => {
onClick={() => handlePreview(item)}
/> */}
<EyeOutlined className={styles.maskIcon} onClick={() => handlePreview(item)} />
{!disabled && (
<DeleteOutlined
className={styles.maskIcon}
onClick={() => handleRemove(item)}
/>
)}
{/* <Icon
type="delete"
className={styles.maskIcon}
......
......@@ -58,34 +58,6 @@ export const TaskList = (canAddService, canAddNormal) => [
},
},
},
// {
// name: '电子卡卷',
// type: 3,
// desc: '无需物流',
// hide: true,
// imgConfig: {
// commonImageList: {
// title: '封面图片',
// rule: true,
// limit: 11,
// renderExtra(leng) {
// return `建议尺寸: ##宽##高 (${leng} / 1) `;
// },
// },
// imageList: {
// rule: true,
// limit: 1,
// },
// detailImageList: {
// title: '商品图片',
// rule: true,
// limit: null,
// renderExtra() {
// return '请上传商品图!';
// },
// },
// },
// },
{
name: '电子卡卷',
type: 4,
......@@ -166,7 +138,7 @@ export const StaticColumns = customer => [
min: 0,
},
roleRules: { required: true },
disabeldRender: () => customer.isJDGoods,
disabeldRender: () => customer.isDisabled,
},
{
title: '佣金费率',
......@@ -189,7 +161,7 @@ export const StaticColumns = customer => [
min: 0,
},
roleRules: { required: true },
// disabeldRender: () => customer.isService,
disabeldRender: () => customer.isDisabled,
},
{
title: '销售价',
......@@ -202,6 +174,7 @@ export const StaticColumns = customer => [
precision: 2,
min: 0,
},
disabeldRender: () => customer.isDisabled,
},
{
title: '重量(kg)',
......@@ -220,7 +193,7 @@ export const StaticColumns = customer => [
precision: 3,
max: 999999.999,
},
// disabeldRender: () => customer.isService,
disabeldRender: () => customer.isDisabled,
},
{
title: '库存',
......@@ -237,7 +210,10 @@ export const StaticColumns = customer => [
min: 0,
},
roleRules: { required: true },
disabeldRender: v => v.id && customer.isService,
disabeldRender: record => {
if (record.stock === null) return false;
return customer.isEdit && customer.isNormal;
},
},
{
title: '库存预警',
......@@ -245,13 +221,12 @@ export const StaticColumns = customer => [
editable: true,
batchRole: [1],
role: [1, 4],
roleRules: { required: true },
roleProps: {
min: 0,
precision: 0,
maxLength: 5,
},
// disabeldRender: () => customer.isService,
disabeldRender: () => customer.isDisabled,
},
{
title: '商品自编码',
......@@ -260,7 +235,7 @@ export const StaticColumns = customer => [
role: [1, 2],
inputType: 'input',
roleRules: { required: true },
disabeldRender: () => customer.isService,
disabeldRender: () => customer.isDisabled,
},
{
title: '京东链接',
......@@ -269,7 +244,7 @@ export const StaticColumns = customer => [
role: [1, 2],
inputType: 'input',
roleRules: { required: false },
disabeldRender: () => customer.isService,
disabeldRender: () => customer.isDisabled,
},
{
title: 'sku名称',
......@@ -278,6 +253,7 @@ export const StaticColumns = customer => [
role: customer.isEdit && customer.isJDGoods ? [1, 2] : [],
inputType: 'btnText',
roleRules: { required: false },
disabeldRender: () => customer.isDisabled,
},
{
title: '操作',
......@@ -287,8 +263,10 @@ export const StaticColumns = customer => [
inputType: 'option',
roleProps: {
isJDGoods: customer.isJDGoods,
disabled: customer.isDisabled,
min: 0,
},
roleRules: { required: false },
disabeldRender: () => customer.isDisabled,
},
];
......@@ -73,6 +73,7 @@ const ServiceGoods = options => {
const productChange = task => {
setProductType(task.type);
setPageLoading(true);
setCategoryIds([]);
const timer = setTimeout(() => {
setPageLoading(false);
resetForm();
......@@ -154,6 +155,7 @@ const ServiceGoods = options => {
const submitEvent = async () => {
const checkPromiseList = clearCurrent(checkFormList).map(({ current }) => current.onCheck());
const resuslt = await Promise.all(checkPromiseList);
console.log('resuslt :>> ', resuslt);
if (!resuslt.includes(null)) {
const params = resuslt.reduce((origin, item) => {
const { temp, ...other } = item;
......@@ -176,9 +178,8 @@ const ServiceGoods = options => {
const detailList = result.detailList || [];
const newImageList = imageList[result.firstSpecValue];
const carouseList = result.carouseList || [];
imageList[result.firstSpecValue] = newImageList
? [...newImageList, ...carouseList]
: carouseList;
imageList[result.firstSpecValue] =
(newImageList ? [...newImageList, ...carouseList] : carouseList) || [];
picturesRef.current.setFieldsValue({
imageList,
detailImageList: [...detailImageList, ...detailList],
......@@ -254,7 +255,10 @@ const ServiceGoods = options => {
canAddService, // 是否可以添加服务商品(电子卡券)
canAddNormal, // 是否可以添加实物商品
isCard: productType === 4,
isService: SourceData.state && SourceData.state !== 4,
// 0, "商品删除" 1, "新建" 2, "提交审核" 3, "待审核" 4, "驳回" 5, "未上架" 6, "已上架" 7, "已下架"
isNormal: SourceData.state && SourceData.state !== 4, // 商品不是驳回状态
// 当商品进行编辑 & 类型不为电子卡券 & 商品状态不为驳回 禁用当前功能
isDisabled: isEdit && productType !== 4 && SourceData.state && SourceData.state !== 4,
isJDGoods: isEdit && SourceData.pageProductType && +SourceData.pageProductType !== 1,
onEventBus,
};
......
......@@ -184,29 +184,38 @@ const createInitSkuItems = () => ({
name: null,
});
export const createSkuListData = (first, second, firstSpecId, secondSpecId) => {
const getSecordValue = (firstSpecValue, sec, skuList = []) =>
skuList.find(
sku =>
sku.firstSpecValue === firstSpecValue &&
(!sku.secondSpecValue || sku.secondSpecValue === sec.secondSpecValue),
);
export const createSkuListData = (first, second, firstSpecId, secondSpecId, skuList) => {
const list = [];
const skuItem = createInitSkuItems();
console.log(first, second, firstSpecId, secondSpecId);
if (first && first.length) {
// 一级规格有值时,生成的编辑表格
first.forEach(fir => {
const copy = { ...skuItem };
let copy = { ...skuItem };
copy.firstSpecId = firstSpecId;
copy.firstSpecValue = fir.firstSpecValue;
// console.log(copy);
if (second.length) {
second.forEach(sec => {
const copySec = { ...copy };
const v = getSecordValue(fir.firstSpecValue, sec, skuList);
const copySec = v || { ...copy };
copySec.secondSpecId = secondSpecId;
copySec.secondSpecValue = sec.secondSpecValue;
delete copySec.rowSpanCount;
list.push(copySec);
});
return;
}
const v = getSecordValue(`${fir.firstSpecValue}`, {}, skuList);
if (v) {
copy = { ...v };
}
list.push(copy);
});
} else if (second && second.length) {
......@@ -217,6 +226,8 @@ export const createSkuListData = (first, second, firstSpecId, secondSpecId) => {
copy.secondSpecValue = sec.secondSpecValue;
list.push(copy);
});
} else if (skuList && skuList.length) {
list.push(...skuList);
} else {
// 缺少一级和二级规格时生成的编辑表格
list.push(skuItem);
......@@ -224,7 +235,7 @@ export const createSkuListData = (first, second, firstSpecId, secondSpecId) => {
return list;
};
export const createProductData = (props, isEdit) => {
export const createProductData = (props, isEdit, skuList) => {
const {
firstSpecId,
secondSpecId,
......@@ -233,21 +244,14 @@ export const createProductData = (props, isEdit) => {
fisrtInIdList,
secndInIdList,
} = props;
const newFirstList = fisrtNoIdList.concat(fisrtInIdList || []);
let list = [];
// if (!isFirstSame && !isSecondSame) {
if (!isEdit) {
list = createSkuListData(fisrtNoIdList, secndNoIdList, firstSpecId, secondSpecId);
if (isEdit) {
const newFirstList = fisrtNoIdList.concat(fisrtInIdList || []);
const newSecondList = secndInIdList.concat(secndNoIdList || []);
list = createSkuListData(newFirstList, newSecondList, firstSpecId, secondSpecId, skuList);
} else {
const list1 = fisrtNoIdList.length
? createSkuListData(fisrtNoIdList, secndInIdList, firstSpecId, secondSpecId)
: [];
const list2 = secndNoIdList.length
? createSkuListData(newFirstList, secndNoIdList, firstSpecId, secondSpecId)
: [];
list = [...list1, ...list2];
list = createSkuListData(fisrtNoIdList, secndNoIdList, firstSpecId, secondSpecId);
}
// }
return list;
};
......
......@@ -97,6 +97,12 @@ export function columns(pages) {
width: '100px',
hideInSearch: true,
align: 'center',
render: (value, row) => (
<div>
<div>{value}</div>
<div style={{ color: '#f00' }}>(赔付{row.claimAmount})</div>
</div>
),
},
{
title: '结算金额',
......
......@@ -31,7 +31,7 @@ const LogisticsCom = props => {
// eslint-disable-next-line react/no-array-index-key
<Timeline.Item color={index > 0 ? 'gray' : 'blue'} key={index}>
<p>{item.desc}</p>
<p>{item.logisticsTime}</p>
<p>{item.time}</p>
</Timeline.Item>
))}
</Timeline>
......
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();
}
});
};
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;
}
}
......@@ -12,6 +12,8 @@ import LogisticsForm from './components/LogisticsForm';
import PopoverDom from './components/PreviewImage';
import LogisticsCom from './components/LogisticsCom';
import DelayDeliverGoods from './components/DelayDeliverGoods';
import MultiLogisticsModal from './components/MultiLogisticsModal';
import {
queryToSend,
queryExpress,
......@@ -19,6 +21,8 @@ import {
getLogistics,
downOrder,
getJDLogisticsInfo,
apiQueryOrderInfo,
apiDeliveriesTraceList,
} from './service';
const { confirm } = Modal;
......@@ -58,25 +62,34 @@ const TableList = props => {
// 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 (record, skuItem) => {
const handleCom = async (skuInfo, expressInfo) => {
const tempObj = {
expressCompanyCode: skuItem?.expressCompanyCode ?? '',
expressCompanyName: skuItem.expressCompanyName ?? '',
deliveryNo: skuItem?.deliveryNo ?? '',
expressCompanyCode: expressInfo?.expressCompanyCode ?? '',
expressCompanyName: expressInfo.expressCompanyName ?? '',
deliveryNo: expressInfo?.expressNo ?? '',
detailList: [],
key: Date.now(),
};
const data = await getJDLogisticsInfo(skuItem.orderSkuId);
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?.logisticsName || tempObj.expressCompanyName;
tempObj.deliveryNo = data?.logisticsBillNo || tempObj.deliveryNo;
if (data.logisticsList?.length) {
data.logisticsList.forEach(v => {
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];
});
}
......@@ -109,8 +122,13 @@ const TableList = props => {
const renderContent = (record, key) => {
if (record.mchOrderSkuVoList) {
return record?.mchOrderSkuVoList.map((item, index) => (
<p
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,
......@@ -118,21 +136,46 @@ const TableList = props => {
key={item.orderSkuId}
>
{key === 'skuName' ? <PopoverDom name={item[key]} url={item.imageUrl} /> : ''}
{key === 'action' && props.type === 2 ? (
<Button
size="small"
type="primary"
{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(record, item);
handleCom(item, info);
}}
className={[
'subContent',
idx < item.expressList?.length - 1 ? 'border' : null,
].join(' ')}
>
查看物流
</Button>
{info.expressNo}
</a>
))}
</div>
) : (
item[key]
''
)}
</p>
));
{item[key]}
</div>
);
});
}
return '';
};
......@@ -148,7 +191,7 @@ const TableList = props => {
const columns = [
{
title: '订单ID',
title: '订单',
dataIndex: 'orderNo',
key: 'orderNo',
order: 6,
......@@ -322,7 +365,7 @@ const TableList = props => {
title: '物流公司',
dataIndex: 'expressCompanyName',
key: 'expressCompanyName',
width: 100,
width: 120,
className: 'colStyle',
hideInSearch: true,
render: (_, record) => renderContent(record, 'expressCompanyName'),
......@@ -331,20 +374,20 @@ const TableList = props => {
title: '物流单号',
dataIndex: 'deliveryNo',
key: 'deliveryNo',
width: 180,
width: 210,
className: 'colStyle',
hideInSearch: true,
render: (_, record) => renderContent(record, 'deliveryNo'),
},
{
title: '物流信息',
dataIndex: 'action',
key: 'action',
width: 150,
hideInSearch: true,
className: 'colStyle',
render: (_, record) => renderContent(record, 'action'),
},
// {
// title: '物流信息',
// dataIndex: 'action',
// key: 'action',
// width: 150,
// hideInSearch: true,
// className: 'colStyle',
// render: (_, record) => renderContent(record, 'action'),
// },
{
title: '订单状态',
dataIndex: 'orderStatusDesc',
......@@ -400,19 +443,30 @@ const TableList = props => {
marginBottom: '10px',
}}
onClick={async () => {
const skuListData = await getGoods(record?.orderId);
let logisticsData = [{}];
setSkuList(skuListData);
const data = await getLogistics(record?.orderId);
logisticsData = data.map(item => ({
selectedGoods: item?.skus?.map(sku => sku?.orderSkuId),
selectedCompany: item.expressCompanyCode
? `${item?.expressCompanyCode}-${item?.expressCompanyName}`
: null,
orderNum: item?.deliveryNo,
}));
setLogisticsData(logisticsData);
handleModalVisible(true);
// const skuListData = await getGoods(record?.orderId);
// let logisticsData = [{}];
// setSkuList(skuListData);
// const data = await getLogistics(record?.orderId);
// logisticsData = data.map(item => ({
// selectedGoods: item?.skus?.map(sku => sku?.orderSkuId),
// selectedCompany: item.expressCompanyCode
// ? `${item?.expressCompanyCode}-${item?.expressCompanyName}`
// : null,
// orderNum: item?.deliveryNo,
// }));
// setLogisticsData(logisticsData);
// handleModalVisible(true);
const res = await apiQueryOrderInfo({
orderNo: record.orderNo,
});
multiLogisticsModalRef.current.open(
{
...record,
packageList: res.data.packageList || [],
},
actionRef,
);
}}
>
{props.type === 2 ? '更新物流信息' : '填写物流信息'}
......@@ -566,6 +620,8 @@ const TableList = props => {
orderId={delayOrderIDs}
onCancel={e => onCancelDelay(e)}
/>
<MultiLogisticsModal companys={companys} ref={multiLogisticsModalRef} />
</PageHeaderWrapper>
);
};
......
......@@ -5,12 +5,30 @@
.tableContent {
display: flex;
align-items: center;
height: 60px;
padding: 16px;
// 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;
}
......
......@@ -62,7 +62,7 @@ export async function getLogistics(orderId) {
export async function uploadFile(file) {
const params = new FormData();
params.append('file', file);
const data = await request.post('/api/kdsp/op/mch-order/order-logistics-batch-import', {
const data = await request.post('/api/merchants/orders/deliveries/batches/import', {
data: params,
prefix: config.kdspApi,
});
......@@ -71,7 +71,8 @@ export async function uploadFile(file) {
export function downTemplate() {
window.location.href =
// 'https://kdspstatic.q-gp.com/%E8%AE%A2%E5%8D%95%E7%89%A9%E6%B5%81%E4%BF%A1%E6%81%AF%E5%AF%BC%E5%85%A5%E6%A8%A1%E6%9D%BF-v1.xlsx';
'https://kdspstatic.q-gp.com/order_import_templatev2.xlsx';
// 'https://kdspstatic.q-gp.com/order_import_templatev2.xlsx';
'https://sc-img.q-gp.com/orders/templates/batch_deliveriesV2.xlsx';
}
export async function downOrder(params) {
const data = await request.post('/api/kdsp/op/mch-order/order-export', {
......@@ -151,3 +152,86 @@ export function apiDelayDeliverGoods(data) {
prefix: config.kdspApi,
});
}
/**
* 多物流发货-查询多物流订单信息
* @param {*} params
* @returns
* @see http://yapi.quantgroups.com/project/389/interface/api/45840
*/
export function apiQueryOrderInfo(params) {
// return new Promise(resolve => {
// setTimeout(() => {
// resolve({
// packageList: [
// {
// expressCompanyCode: 'yunda',
// expressCompanyName: '',
// expressNo: 'YUNDA012345678',
// serialNumber: '0011223344',
// skuInfoList: [
// {
// skuNo: ['529355424931841'],
// quantity: '2',
// },
// {
// skuNo: ['530724210084865'],
// quantity: '12',
// },
// ],
// },
// {
// expressCompanyCode: 'yunda',
// expressCompanyName: '',
// expressNo: 'YUNDA012345678',
// serialNumber: '0011223344',
// skuInfoList: [
// {
// skuNo: ['529355424931841'],
// quantity: '2',
// },
// ],
// },
// ],
// });
// }, 1000);
// });
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/trace/list', {
data: stringify(data),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
prefix: config.kdspApi,
});
}
......@@ -45,7 +45,7 @@ const CustomTable = props => {
subDataItem && subDataItem[key] !== undefined ? subDataItem[key] : dataSourceItem[key];
let rowSpan = 1;
// 设置了自动合并 && 只设置第一条数据的值,其他的返回<></</>
// 设置了自动合并 && 只设置第一条数据的值,其他的返回<></>
if (column.rowSpanMode === 'auto' && subData) {
rowSpan = subData.length;
if (subDataIndex > 0) {
......
import { Form, Tabs, Input, Button, Pagination } from 'antd';
import { Form, Tabs, Input, Button, Pagination, notification } from 'antd';
import React, { useState, useEffect, useRef } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { connect } from 'dva';
import { QUERY_ORDER } from '@/../config/permission.config';
import { values } from 'lodash';
import style from './index.less';
import { CustomTable } from './components/CustomTable/index';
import { FormSearch, SEARCH_TYPE } from './components/FormSearch/index';
import LogisticsForm from './components/LogisticsFormModel';
import MultiLogisticsModal from '../pendingDeliveryOrder/components/MultiLogisticsModal';
import LogisticsCom from '../pendingDeliveryOrder/components/LogisticsCom';
import DetailModal from './components/DetailModal/index';
import { queryOrderList, getGoods, getLogistics, queryExpress, queryToSend } from './service';
import { apiQueryOrderInfo, apiDeliveriesTraceList } from '../pendingDeliveryOrder/service';
import { ORDER_SEARCH_TYPE, ORDER_TYPE, ORDER_STATUS } from './const';
const { TabPane } = Tabs;
......@@ -20,16 +23,15 @@ const OrderList = props => {
const canEditable = permissions[QUERY_ORDER.EDITABLE];
const [loading, setLoading] = useState(false);
const detailModalRef = useRef();
const multiLogisticsModalRef = useRef();
/** @module 发货弹框 */
// 物流公司数据
const [companys, setCompanys] = useState([]);
// 物流弹出框展示
const [LogisticsModalVisible, handleModalVisible] = useState(false);
// 订单下商品列表
const [skuList, setSkuList] = useState([]);
// 当前物流数据
const [LogisticsData, setLogisticsData] = useState([{}]);
/** @module 查看物流记录 */
const [LogisticsComList, setLogisticsComList] = useState({});
const [LogisticsComModalVisible, handleComModalVisible] = useState(false);
/** @module 表格区域 */
const [tableData, setTableData] = useState([]);
......@@ -95,12 +97,50 @@ const OrderList = props => {
});
}
// 获取物流记录信息
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 reload = () => {
handleModalVisible(false);
getOrderList();
};
const actionRef = {
current: {
reload,
},
};
// 获取物流公司列表
const getCompanys = async () => {
const res = await queryExpress();
......@@ -267,9 +307,27 @@ const OrderList = props => {
},
{
title: '配送方式',
dataIndex: 'deliveryType',
rowSpanMode: 'auto',
dataIndex: 'deliveryMethodList',
// rowSpanMode: 'auto',
width: 150,
render: ({ value, record, subRecord }) => {
console.log(value);
const info = {
orderNo: record.orderNoStr,
};
return value?.map(item => (
<p>
{item.expressCompanyName}/
<a
onClick={() => {
handleCom(info, item);
}}
>
{item.expressNo}
</a>
</p>
));
},
},
{
title: '售后',
......@@ -315,19 +373,23 @@ const OrderList = props => {
<Button
type="primary"
onClick={async () => {
const skuListData = await getGoods(record?.orderId);
let logisticsData = [{}];
setSkuList(skuListData);
const res = await getLogistics(record?.orderId);
logisticsData = res.map(item => ({
selectedGoods: item?.skus?.map(sku => sku?.orderSkuId),
selectedCompany: item.expressCompanyCode
? `${item?.expressCompanyCode}-${item?.expressCompanyName}`
: null,
orderNum: item?.deliveryNo,
}));
setLogisticsData(logisticsData);
handleModalVisible(true);
const res = await apiQueryOrderInfo({
orderNo: record.orderNoStr,
});
multiLogisticsModalRef.current.open(
{
...record,
orderNo: record.orderNoStr,
mchOrderSkuVoList: record.skuVos.map(item => ({
...item,
skuNo: item.skuId?.toString(),
orderNo: record.orderNoStr,
})),
packageList: res.data.packageList || [],
},
actionRef,
);
}}
>
{text}
......@@ -406,15 +468,14 @@ const OrderList = props => {
)}
</div>
<LogisticsForm
<LogisticsCom
onSubmit={reload}
skuList={skuList}
companys={companys}
onCancel={() => handleModalVisible(false)}
modalVisible={LogisticsModalVisible}
value={LogisticsData}
onCancel={() => handleComModalVisible(false)}
modalVisible={LogisticsComModalVisible}
value={LogisticsComList}
key={LogisticsComList.key}
/>
<MultiLogisticsModal companys={companys} ref={multiLogisticsModalRef} />
<DetailModal ref={detailModalRef} />
</PageHeaderWrapper>
);
......
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