Commit 32dd7d4a authored by shida.liu's avatar shida.liu

feat: 应付审计而增加的页面和判断,无真实用途

parent cd134bc7
......@@ -302,26 +302,33 @@ export default {
},
...groupMealRoute,
{
component: './404',
},
{
title: '商户管理后台-POP商品管理-待发货订单',
title: '商户管理后台-订单管理-待发货订单',
path: '/popOrderManage/popPendingDeliveryOrder',
name: 'popPendingDeliveryOrder',
component: './PopOrderManage/pendingDeliveryOrder',
},
{
title: '商户管理后台-POP商品管理-已发货订单',
title: '商户管理后台-订单管理-已发货订单',
path: '/popOrderManage/popDeliveryOrder',
name: 'popDeliveryOrder',
component: './PopOrderManage/deliveryOrder',
},
{
title: '商户管理后台-POP商品管理-批量发货',
title: '商户管理后台-订单管理-批量发货',
path: '/popOrderManage/popBatchDelivery',
name: 'popBatchDeliveryOrder',
component: './PopOrderManage/batchDelivery',
},
{
title: '商户管理后台-商品管理-商品库',
path: '/popGoodsManage',
name: 'popGoodsManage',
icon: 'smile',
component: './PopGoodsManage',
},
{
component: './404',
},
],
},
{
......
import React, { useState, useEffect } from 'react';
import { Modal, Table, message } from 'antd';
import { columns } from './staticdata';
import { apiDraftList, apiDraftDetail, apiDeleteDraft } from '../service';
const DraftModal = props => {
const [pageInfo, setPageInfo] = useState({
current: 1,
pageSize: 4,
});
const [total, setTotal] = useState(0);
const [dataSource, setdataSource] = useState([]);
const onClose = () => props.onCancel();
const onEdit = async record => {
const res = await apiDraftDetail(record.id);
if (res && res.data) {
const data = JSON.parse(res.data.content);
data.id = record.id;
props.onToDetail(data);
onClose();
}
};
const getDraftList = async params => {
const res = await apiDraftList(params);
if (res && res.data && res.data.records) {
setdataSource(res.data.records);
setTotal(res.data.total);
}
};
const onRefresh = () => {
getDraftList({
pageSize: pageInfo.pageSize,
pageNo: pageInfo.current,
});
};
const onDel = record => {
Modal.confirm({
title: '确认提示',
content: '操作后不可更改,确认是否删除?',
onOk: async () => {
console.log('record :>> ', record);
await apiDeleteDraft(record.id);
message.success('删除成功!');
onRefresh();
},
});
};
const onChange = (current, pageSize) => {
const json = {
current,
pageSize,
};
setPageInfo(json);
json.pageNo = current;
getDraftList(json);
};
const pagination = {
...pageInfo,
total,
showTotal: t => `共 ${t} 项数据`,
onChange,
pageSizeOptions: [4, 20, 50, 100],
showSizeChanger: !0,
onShowSizeChange: onChange,
};
useEffect(() => {
if (props.visible) {
onRefresh();
}
}, [props.visible]);
const res = {
onDel,
onEdit,
};
return (
<Modal
visible={props.visible}
title="草稿箱"
onCancel={onClose}
maskClosable={false}
width="1000px"
footer={[]}
>
<Table
columns={columns(res)}
pagination={pagination}
rowKey={record => record.id}
dataSource={dataSource}
/>
</Modal>
);
};
export default DraftModal;
import React from 'react';
import { Button } from 'antd';
import styles from '../style.less';
const productType = {
1: '普通商品',
2: '虚拟商品',
3: '电子卡卷',
4: '服务商品',
5: '外卖商品',
};
export const columns = ({ onDel, onEdit }) => [
{
title: '草稿ID',
dataIndex: 'id',
width: 85,
align: 'center',
},
{
title: '商品名称',
dataIndex: 'productName',
align: 'center',
render(text) {
return <div className={styles.draftName}>{text}</div>;
},
},
{
title: '所属类目',
dataIndex: 'leimu',
width: 240,
align: 'center',
render(text, record) {
if (record.firstCategoryName) {
return `${record.firstCategoryName}>${record.secondCategoryName}>${record.thirdCategoryName}`;
}
return '-';
},
},
{
title: '商品类型',
dataIndex: 'productType',
width: 100,
align: 'center',
render(text) {
return productType[text];
},
},
{
title: '创建时间',
dataIndex: 'createdAt',
width: 120,
align: 'center',
},
{
title: '操作',
dataIndex: 'action',
width: 130,
align: 'center',
render: (text, record) => (
<>
<Button key="edit" type="link" size="small" onClick={() => onEdit(record)}>
修改
</Button>
<Button key="viewP" type="link" size="small" onClick={() => onDel(record)}>
删除
</Button>
</>
),
},
];
import { Modal, Table, Button, Pagination, Tabs } from 'antd';
import React, { useState, useEffect } from 'react';
import styles from '../style.less';
import { changeLog, productMerchantLog } from '../service';
const LogModal = props => {
const [tabActiveKey, setTabActiveKey] = useState('0');
const [tableData, setTableData] = useState([]);
const [pageNo, setPageNo] = useState(1);
const [pageSize] = useState(20);
const [merchantList, setMerchantList] = useState([]);
const columns = [
{
title: '时间',
dataIndex: 'createdAt',
align: 'center',
},
{
title: '变更字段',
align: 'center',
dataIndex: 'changeType',
},
{
title: '变更后内容',
dataIndex: 'afterChangeValue',
align: 'center',
},
{
title: '变更前内容',
dataIndex: 'beforeChangeValue',
align: 'center',
},
{
title: '变更原因',
dataIndex: 'reason',
align: 'center',
},
{
title: '操作账号',
dataIndex: 'operator',
align: 'center',
},
];
const columnsMerchant = [
{
title: '时间',
dataIndex: 'createdAt',
align: 'center',
},
{
title: '审核结果',
align: 'center',
dataIndex: 'operation',
render: value => ([5, 6, 7].includes(value) ? '审核通过' : '驳回'),
},
{
title: '驳回原因',
dataIndex: 'rejectReason',
align: 'center',
},
{
title: '操作账号',
dataIndex: 'createdBy',
align: 'center',
},
];
const handleSearch = async (page = 1) => {
setPageNo(page);
const { data = {} } = await changeLog({ id: props.id, pageNo: page, pageSize });
setTableData(data);
};
const onPageChange = page => {
handleSearch(page);
};
const getProductMerchantLog = async () => {
const { data = [] } = await productMerchantLog(props.spuId);
setMerchantList(data); // merchantList
};
const bundleOnTabChange = key => {
if (key === '1') {
getProductMerchantLog();
} else {
onPageChange(1);
}
setTabActiveKey(key);
};
const bundleOnCancel = () => {
setMerchantList([]);
setTabActiveKey('0');
props.onCancel();
};
useEffect(() => {
if (!props.id) return;
// 20221108 临时隐藏商品详情,默认切换到审核详情 by liteng
// handleSearch();
bundleOnTabChange('1');
}, [props.id]);
const { visible } = props;
return (
<Modal title="日志详情" visible={visible} footer={null} onCancel={bundleOnCancel} width="800px">
<Tabs type="card" onChange={bundleOnTabChange} activeKey={tabActiveKey}>
{/* 20221108 临时隐藏商品详情 by liteng */}
{/* <Tabs.TabPane tab="商品详情" key="0">
<Table
dataSource={tableData.records}
bordered
columns={columns}
rowKey={record => record.id}
pagination={false}
scroll={{ y: 300 }}
/>
{tableData.records && (
<Pagination
onChange={onPageChange}
total={tableData.total}
showTotal={total => `共${total}条`}
current={pageNo}
pageSize={pageSize}
className={styles.pagination}
/>
)}
</Tabs.TabPane> */}
<Tabs.TabPane tab="审核详情" key="1">
<Table
dataSource={merchantList}
bordered
columns={columnsMerchant}
rowKey={record => record.id}
pagination={false}
scroll={{ y: 300 }}
/>
</Tabs.TabPane>
</Tabs>
<Button type="primary" onClick={bundleOnCancel} className={styles.logBtn}>
关闭
</Button>
</Modal>
);
};
export default LogModal;
import {
Form,
Button,
Input,
Select,
notification,
Upload,
Tag,
Cascader,
InputNumber,
Popover,
Divider,
} from 'antd';
import React, { Component, useState } from 'react';
import { SwapRightOutlined } from '@ant-design/icons';
import { connect } from 'dva';
import { saveAs } from 'file-saver';
import { format } from 'date-fns';
import styles from '../style.less';
import { stateList } from '../staticdata';
import { apiGoodsInfosExport } from '../service';
import { GOOD_MANAGE } from '@/../config/permission.config';
const FormItem = Form.Item;
const { Option } = Select;
@connect(({ goodsManage, menu }) => ({
goodsManage,
permissions: menu.permissions,
}))
class goodsManage extends Component {
formRef = React.createRef();
state = {
loading: false,
productType: null,
};
componentDidMount() {
this.props.onRef(this);
this.handleSearch();
}
getFieldsValue() {
const form = this.formRef.current;
return form.getFieldsValue();
}
handleSearch = () => {
this.props.handleSearch(1);
};
valueMin = value => {
const { getFieldValue, setFieldsValue } = this.formRef.current;
const minVal = getFieldValue('supplyPriceMin');
if (minVal && minVal > value) {
setFieldsValue({ supplyPriceMax: minVal });
}
};
onReset = () => {
const form = this.formRef.current;
form.resetFields();
this.props.onReset();
this.props.changeProductType(1);
this.setState({
productType: 1,
});
};
addSpu = () => {
this.props.addSpu();
};
setArea = (isAll, type) => {
this.props.setArea(isAll, type);
};
// 验证是否可以修改库存
checkEnableUpdateStock = () => {
if (this.props.selectNum) {
this.props.checkStock();
} else {
notification.info({ message: '请选择' });
}
};
onChangeProductType = (v = null) => {
const form = this.formRef.current;
form.setFieldsValue({
skuId: '',
skuName: '',
thirdSkuNo: '',
productCategoryId: null,
state: null,
supplyPriceMin: null,
supplyPriceMax: null,
productType: v,
});
this.props.changeProductType(v);
this.setState({
productType: v,
});
};
// 导出明细
onExportGoodsInfo = async () => {
this.setState({
loading: true,
});
const form = this.formRef?.current?.getFieldValue();
const params = Object.assign({}, form);
const productCategoryId = form?.productCategoryId || [];
if (productCategoryId.length) {
params.productCategoryId = productCategoryId[productCategoryId.length - 1] || '';
}
const res = await apiGoodsInfosExport(params);
this.setState({
loading: false,
});
if (res) {
const blob = new Blob([res]);
saveAs(blob, `商品明细-${format(new Date(), 'yyyyMMdd')}.xlsx`);
} else {
notification.error({ message: '下载失败' });
}
};
render() {
const { treeData, permissions } = this.props;
const selectW = { width: 250 };
const iptNumWidth = { width: 118 };
const canEditable = permissions[GOOD_MANAGE.EDITABLE];
const content = (
<div>
<Button style={{ border: 'none' }} onClick={() => this.setArea(1, 'distribution')}>
全部商品配送区域设置
</Button>
<br />
<Button style={{ border: 'none' }} onClick={() => this.setArea(0, 'distribution')}>
勾选商品配送区域设置
</Button>
<br />
<Button style={{ border: 'none' }} onClick={() => this.setArea(1, 'after')}>
全部商品售后地址设置
</Button>
<br />
<Button style={{ border: 'none' }} onClick={() => this.setArea(0, 'after')}>
勾选商品售后地址设置
</Button>
<br />
<Button style={{ border: 'none' }} onClick={() => this.checkEnableUpdateStock()}>
勾选商品库存修改
</Button>
</div>
);
const filterOption = (input, op) => op.props.children.includes(input);
return (
<Form
ref={this.formRef}
name="horizontal_login"
initialValues={{ productType: 1 }}
layout="inline"
className={styles.searchForm}
>
<FormItem label="SKU编码" name="skuId">
<InputNumber placeholder="请输入SKU编码" max={99999999999999999} style={selectW} />
</FormItem>
<FormItem label="商品名称" name="skuName">
<Input placeholder="请输入商品名称" allowClear style={selectW} />
</FormItem>
<FormItem label="商品类型" name="productType">
<Select style={selectW} placeholder="请选择商品类型" onChange={this.onChangeProductType}>
<Option value={1}>实体商品</Option>
<Option value={4}>服务类商品</Option>
<Option value={5}>外卖商品</Option>
</Select>
</FormItem>
<FormItem label="类目" name="productCategoryId">
<Cascader
placeholder="请选择类目"
style={selectW}
showSearch
changeOnSelect
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
options={treeData}
/>
</FormItem>
{this.state.productType !== 5 && (
<>
<FormItem label="审核状态" name="state">
<Select
style={selectW}
placeholder="请选择审核状态"
allowClear
filterOption={filterOption}
>
{stateList?.map(item => (
<Option key={item.value} value={item.value}>
{item.label}
</Option>
))}
</Select>
</FormItem>
<FormItem label="供货价区间">
<FormItem name="supplyPriceMin" className={styles.iptNumRight} noStyle>
<InputNumber placeholder="请输入" min={0} max={999999999} style={iptNumWidth} />
</FormItem>
<span>
<SwapRightOutlined />
</span>
<FormItem name="supplyPriceMax" className={styles.iptNumRight} noStyle>
<InputNumber
style={iptNumWidth}
min={0}
max={999999999}
placeholder="请输入"
onChange={this.valueMin}
/>
</FormItem>
</FormItem>
<FormItem name="thirdSkuNo" label="第三方SKU编码">
<Input placeholder="请输入第三方SKU编码" allowClear style={selectW} />
</FormItem>
</>
)}
<FormItem className={styles.queryBtn}>
<Button onClick={() => this.handleSearch()} type="primary" className={styles.button}>
查询
</Button>
<Button onClick={() => this.onReset()} className={styles.button}>
重置
</Button>
{this.state.productType !== 5 && (
<>
<Button
loading={this.state.loading}
onClick={() => this.onExportGoodsInfo()}
type="primary"
ghost
className={styles.button}
>
导出
</Button>
{/* {canEditable ? (
<FormItem style={{ float: 'right' }}>
<Popover content={content} onVisibleChange={this.handleVisibleChange}>
<Button type="primary" className={styles.button}>
批量设置
</Button>
</Popover>
{this.props.selectNum > 0 && (
<Tag color="green">已选商品 {this.props.selectNum}</Tag>
)}
</FormItem>
) : (
''
)} */}
</>
)}
</FormItem>
</Form>
);
}
}
export default goodsManage;
import React from 'react';
import { Button, Dropdown, Menu, message, Modal } from 'antd';
import { PlusOutlined, DownOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import { batchAction } from '../../staticdata';
import styles from '../../style.less';
import { apiGoodsActionBatch } from '../../service';
const ActionBar = options => {
// 上下架
const changeStatus = async state => {
console.log('options.shopId :>> ', options.shopId);
Modal.confirm({
icon: <ExclamationCircleOutlined />,
content: `确认${+state === 6 ? '下架' : '上架'}商品?`,
onOk: async () => {
const res = await apiGoodsActionBatch({
skuIds: options.selectedRowKeys,
type: 2,
shopId: options.shopId,
productStatus: +state === 6 ? 0 : 1, // 6:上架,7:下架
});
if (res.businessCode === '0000' && res.code === '0000') {
options.handleSearch();
message.success('处理成功!');
}
},
});
};
/**
* 批量操作
* up 上架
* down 下架
* stock 修改库存
* time 修改可售时间
* group 修改分组
* send 设置单点不送
* buy 修改最少购买数量
*/
const onChangeState = type => {
if (options.selectedRowKeys && options.selectedRowKeys.length) {
if (['up', 'down'].includes(type)) {
changeStatus(type === 'up' ? 7 : 6);
} else {
options.openModal(type);
}
} else {
message.warning('请选择商品!');
}
};
const eventObj = {
onChangeState,
};
const actions = batchAction(eventObj);
const menus = (
<Menu>
{actions.map(item => (
<Menu.Item key={item.key}>{item.label}</Menu.Item>
))}
</Menu>
);
return (
<div className={styles['action-bar-box']}>
{(options.canAddTakeaway && (
<Button type="primary" icon={<PlusOutlined />} onClick={options.newGoods}>
该分组下新增商品
</Button>
)) ||
''}
<Dropdown overlay={menus} className={styles['action-bar-box--down']} placement="bottomLeft">
<Button type="primary">
批量操作 <DownOutlined />
</Button>
</Dropdown>
</div>
);
};
export default ActionBar;
import React, { useRef, useState } from 'react';
import { useDrag, useDrop } from 'react-dnd';
import { Tag, Input, Popconfirm } from 'antd';
import { HolderOutlined, FormOutlined, CloseCircleOutlined } from '@ant-design/icons';
import styles from '../../style.less';
const ItemTypes = {
CARD: 'card',
};
const DragTag = ({ text, id, index, changePosition, endChangePosition, edit, del, selected }) => {
const [isEdit, setIsEdit] = useState(false);
const [inputValue, setInputValue] = useState('');
const refInput = useRef();
const handleEdit = () => {
edit(id);
};
const handleInputChange = e => {
setInputValue(e.target.value);
};
const handleInputConfirm = () => {
setIsEdit(false);
setInputValue('');
};
const handleClose = () => {
del(id);
};
const ref = useRef(null);
// 因为没有定义收集函数,所以返回值数组第一项不要
const [, drop] = useDrop({
accept: ItemTypes.CARD,
hover: (item, monitor) => {
if (!ref.current) return;
const dragIndex = item.index;
const hoverIndex = index;
if (dragIndex === hoverIndex) return; // 如果回到自己的坑,那就什么都不做
changePosition(dragIndex, hoverIndex); // 调用传入的方法完成交换
item.index = hoverIndex; // 将当前当前移动到Box的index赋值给当前拖动的box,不然会出现两个盒子疯狂抖动!
},
drop: (item, monitor) => {
endChangePosition(); // 调用传入的方法完成交换
},
});
const [{ isDragging }, drag] = useDrag({
type: ItemTypes.CARD,
item: { id, index, type: ItemTypes.CARD },
end: () => {},
isDragging: monitor => index === monitor.getItem().index,
collect: monitor => ({
isDragging: monitor.isDragging(),
}),
});
const inputRender = () => (
<Input
type="text"
size="small"
ref={refInput}
className={styles['groupBox-body--tag-input']}
value={inputValue}
onChange={handleInputChange}
onBlur={handleInputConfirm}
onPressEnter={handleInputConfirm}
/>
);
const groupEditRender = () => (
<Tag
className={[styles['groupBox-body--tag']]}
ref={drag(drop(ref))}
style={{
opacity: isDragging ? 0.3 : 1,
display: isEdit ? 'none' : 'inline-block',
}}
>
<HolderOutlined className={styles['groupBox-body--tag__move']} />
<span className={styles['groupBox-body--tag__text']}>{text}</span>
<span>
<FormOutlined className={styles['groupBox-body--tag__edit']} onClick={handleEdit} />
</span>
<Popconfirm title="确定删除该分组吗?" onConfirm={handleClose} okText="确定" cancelText="取消">
<CloseCircleOutlined className={styles['groupBox-body--tag__close']} />
</Popconfirm>
</Tag>
);
return (
<>
{isEdit && inputRender()}
{groupEditRender()}
</>
);
};
export default DragTag;
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
import { Button, Select, Tag } from 'antd';
import { DndProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import styles from '../../style.less';
import DragTag from './DragTag';
import InsertTag from './InsertTag';
import GroupInfo from './GroupInfo';
import { apiDelStorage, apiSortStorage, apiStorageList, apiSupplierShopList } from '../../service';
const GoodsGroup = forwardRef((options, ref) => {
const [groupEdit, setGroupEdit] = useState(false);
const [selected, setSelected] = useState(0);
const [storageId, setStorageId] = useState(0);
const [isModalOpen, setIsModalOpen] = useState(false);
const [shops, setShops] = useState([]);
const [tags, setTags] = useState([]);
const getShopList = async () => {
const res = await apiSupplierShopList({
state: 1,
productBusiness: 1,
});
if (res && res.data && res.data.length > 0) {
setShops(
res.data.map(item => ({
label: item.name,
value: +item.id,
})),
);
options.changeShop(+res.data[0].id);
} else {
options.changeShop(0);
}
};
const getGroupList = async () => {
if (options.shopId) {
const res = await apiStorageList({
shopId: options.shopId,
});
if (res && res.data && res.data.length > 0) {
const arr = res.data
.sort((x, y) => x.priority - y.priority)
.map(item => ({
text: item.name,
id: item.rackId,
}));
setTags(arr);
setSelected(res.data[0].rackId);
} else {
setTags([]);
setSelected(0);
}
} else {
setTags([]);
setSelected(0);
}
};
const handleEdit = async id => {
setStorageId(id || 0);
setIsModalOpen(true);
};
const handleDelete = async id => {
const res = await apiDelStorage({
shopId: options.shopId,
id,
});
if (res.businessCode === '0000' && res.code === '0000') {
getGroupList();
}
};
// 更换位置
const changePosition = async (dIndex, hIndex) => {
const data = tags.slice();
const temp = data[dIndex];
// 交换位置
data[dIndex] = data[hIndex];
data[hIndex] = temp;
setTags(data);
};
const endChangePosition = async () => {
const data = tags.slice();
const storageRankList = data.map((item, i) => ({
id: item.id,
priority: i + 1,
}));
const params = {
shopId: options.shopId,
storageRankList,
};
await apiSortStorage(params);
getGroupList();
};
const onSelect = i => {
setSelected(i);
};
useEffect(() => {
if (options.shopId) {
getGroupList();
}
}, [options.shopId]);
useEffect(() => {
getShopList();
}, []);
useEffect(() => {
options.changeGroup(selected);
}, [selected]);
useImperativeHandle(ref, () => ({
setSelected,
}));
return (
<div className={styles.groupBox}>
{(shops && shops.length && (
<>
<div className={styles['groupBox-title']}>
<div className={styles['groupBox-title--name']}>所属门店</div>
<Select
showSearch
value={options.shopId}
placeholder="请选择所属门店"
onChange={options.changeShop}
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={shops}
/>
</div>
<div className={styles['groupBox-title']}>
<div className={styles['groupBox-title--name']}>商品分组</div>
<Button onClick={() => setGroupEdit(!groupEdit)}>
{groupEdit ? '完成' : '编辑分组'}
</Button>
</div>
<div className={styles['groupBox-body']}>
{groupEdit ? (
<DndProvider backend={HTML5Backend}>
<div className={styles['groupBox-body--dragbox']}>
{tags.map((item, index) => (
<DragTag
changePosition={changePosition}
endChangePosition={endChangePosition}
index={index}
{...item}
selected={selected}
edit={handleEdit}
del={handleDelete}
key={item.id}
/>
))}
<InsertTag handleOpen={handleEdit} />
</div>
</DndProvider>
) : (
<div className={styles['groupBox-body--dragbox']}>
{tags.map(item => (
<Tag
key={item.id}
onClick={() => onSelect(item.id)}
className={[
styles['groupBox-body--tag-normal'],
selected === item.id ? styles['groupBox-body--tag__cur'] : '',
]}
>
<span className={styles['groupBox-body--tag__text']}>{item.text}</span>
</Tag>
))}
<InsertTag key="insert" handleOpen={handleEdit} />
</div>
)}
</div>
</>
)) ||
''}
<GroupInfo
isModalOpen={isModalOpen}
id={storageId}
shopId={options.shopId}
search={getGroupList}
handleClose={setIsModalOpen}
/>
</div>
);
});
export default GoodsGroup;
import React, { useEffect, useState } from 'react';
import { Form, Modal, Input, Switch, Alert, message } from 'antd';
import { apiCreateStorage, apiEditStorage, apiStorageInfo } from '../../service';
import { stringOrObjectTrim } from '@/utils/utils';
const GroupInfo = options => {
const [form] = Form.useForm();
const [isChecked, setIsChecked] = useState(false);
// 关闭分组信息弹窗
const handleCancel = () => {
options.handleClose(false);
};
// 添加/保存分组
const handleConfirm = async () => {
const { name, necessary } = await form.validateFields();
const api = options.id ? apiEditStorage : apiCreateStorage;
const res = await api({
name: stringOrObjectTrim(name),
necessary: necessary ? 1 : 0,
shopId: options.shopId,
id: options.id,
});
if (res.code === '0000' && res.businessCode === '0000') {
message.success('保存成功!');
handleCancel();
options.search();
}
};
const getInfo = async id => {
const res = await apiStorageInfo({
shopId: options.shopId,
id,
});
if (res && res.data && res.data.id) {
const { name, necessary } = res.data;
setIsChecked(+necessary === 1);
form.setFieldsValue({
name,
necessary: +necessary === 1,
});
}
};
useEffect(() => {
if (options.id && options.isModalOpen) {
getInfo(options.id);
}
}, [options.id, options.isModalOpen]);
const extra = <Alert message="选中后,顾客下单需至少选择1个“下单必选分组”" type="error" />;
return (
<Modal
title="分组信息"
visible={options.isModalOpen}
destroyOnClose
maskClosable={false}
width="600px"
onOk={handleConfirm}
onCancel={handleCancel}
>
<Form name="basic" form={form} labelCol={{ span: 6 }} wrapperCol={{ span: 16 }}>
<Form.Item
label="分组名称"
name="name"
rules={[{ required: true, message: '请输入分组名称!' }]}
>
<Input />
</Form.Item>
<Form.Item label="下单必选分组" name="necessary" extra={extra}>
<Switch
checkedChildren="开启"
checked={isChecked}
unCheckedChildren="关闭"
onChange={setIsChecked}
/>
</Form.Item>
</Form>
</Modal>
);
};
export default GroupInfo;
import React from 'react';
import { Tag } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import styles from '../../style.less';
const InsertTag = options => {
const showInput = () => {
options.handleOpen();
};
return (
<Tag
className={[styles['groupBox-body--tag'], styles['groupBox-body--new']]}
color="blue"
onClick={showInput}
>
<PlusOutlined /> 添加
</Tag>
);
};
export default InsertTag;
import React from 'react';
import { Modal, Form, InputNumber } from 'antd';
import styles from '../../style.less';
const MinimumPurchase = options => {
const [form] = Form.useForm();
const handleCancel = () => {
options.cancel(false);
};
const handleOk = async () => {
const values = await form.validateFields();
console.log('values :>> ', values);
options.confirm({
type: 5,
...values,
});
};
return (
<Modal
visible={options.visible}
title="修改最少购买数量"
onOk={handleOk}
maskClosable={false}
keyboard={false}
confirmLoading={options.loading}
destroyOnClose
onCancel={handleCancel}
>
<Form
name="basic"
form={form}
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}
initialValues={{ minPurchaseNum: 1 }}
autoComplete="off"
>
<Form.Item
label="最少购买/份"
name="minPurchaseNum"
rules={[{ required: true, message: '请输入最少购买数量!' }]}
>
<InputNumber min={1} max={999999} className={styles.inputWdith} />
</Form.Item>
</Form>
</Modal>
);
};
export default MinimumPurchase;
import React from 'react';
import { Modal, Form, Radio } from 'antd';
const SendModal = options => {
const [form] = Form.useForm();
const handleCancel = () => {
options.cancel(false);
};
const handleOk = async () => {
const values = await form.validateFields();
console.log('values :>> ', values);
options.confirm({
type: 6,
...values,
});
};
const radioOptions = [{ label: '单点送', value: 1 }, { label: '单点不送', value: 0 }];
const initialValues = Object.assign({}, options.initialValues);
return (
<Modal
visible={options.visible}
title="设置单点不送"
onOk={handleOk}
maskClosable={false}
keyboard={false}
confirmLoading={options.loading}
destroyOnClose
onCancel={handleCancel}
>
<Form
name="basic"
form={form}
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}
initialValues={initialValues}
autoComplete="off"
>
<Form.Item
label="是否单点不送"
name="isSingleDelivery"
rules={[{ required: true, message: '请选择!' }]}
>
<Radio.Group options={radioOptions} />
</Form.Item>
</Form>
<div>选择单点不送后顾客单独点这些商品不可下单</div>
</Modal>
);
};
export default SendModal;
import React, { useState, useEffect } from 'react';
import { Modal, Form, InputNumber, Checkbox, Switch } from 'antd';
import { deepClone } from '@/utils/utils';
import styles from '../../style.less';
import { apiProductStock } from '../../service';
import { isIntegerNotZero } from '@/utils/validator';
const StockModal = options => {
const [stockType, setStockType] = useState(0);
const [maxStock, setMaxStock] = useState(0);
const [isChecked, setIsChecked] = useState(false);
const [form] = Form.useForm();
const onChangeType = v => {
setStockType(v === stockType ? 0 : v);
if (v === 1) {
form.setFieldsValue({
productStock: 0,
});
}
};
const onChangeMaxStock = value => {
setMaxStock(value);
};
const handleCancel = () => {
options.cancel(false);
};
const handleOk = async () => {
const values = await form.validateFields();
const params = deepClone(values);
params.autoStock = values.autoStock ? 1 : 0;
options.confirm({
type: 7,
...params,
});
};
const getStockInfo = async () => {
const res = await apiProductStock({
skuId: options.skuIds[0],
shopId: options.shopId,
});
if (res && res.code === '0000' && res.businessCode === '0000' && res.data) {
const info = res.data;
form.setFieldsValue({
autoStockStep: info.autoStockStep,
productStock: info.stock,
autoStock: info.autoStock === 1,
});
setMaxStock(info.autoStockStep);
setIsChecked(info.autoStock === 1);
}
};
const initialValues = Object.assign(
{
productStock: '',
autoStockStep: '',
autoStock: false,
},
options.initialValues,
);
useEffect(() => {
if (stockType === 2) {
form.setFieldsValue({
productStock: maxStock,
});
}
}, [maxStock, stockType]);
useEffect(() => {
if (options.visible) {
setStockType(0);
setMaxStock(0);
setIsChecked(false);
form.resetFields();
if (options.skuIds && options.skuIds.length === 1) {
getStockInfo();
}
}
}, [options.visible]);
const maxStockRule = [{ validator: isIntegerNotZero, message: '请输入大于0的整数' }];
if (isChecked || stockType === 2) {
maxStockRule.push({ required: true, message: '请输入最大库存!' });
}
return (
<Modal
visible={options.visible}
title="修改库存"
onOk={handleOk}
maskClosable={false}
keyboard={false}
confirmLoading={options.loading}
destroyOnClose
onCancel={handleCancel}
>
<Form
name="basic"
form={form}
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}
initialValues={initialValues}
autoComplete="off"
className={styles['stock-box']}
>
<Form.Item
label="剩余库存"
name="productStock"
labelCol={{ span: 6 }}
wrapperCol={{ span: 8 }}
rules={[
{ required: true, message: '请输入剩余库存!' },
{ validator: isIntegerNotZero, message: '请输入大于0的整数' },
]}
>
<InputNumber
min={0}
max={999999999}
className={styles['stock-box--inputnum']}
disabled={stockType > 0}
/>
</Form.Item>
<div className={styles['stock-box--btns']}>
<Checkbox checked={stockType === 1} onChange={() => onChangeType(1)}>
清零
</Checkbox>
<Checkbox checked={stockType === 2} onChange={() => onChangeType(2)}>
最大
</Checkbox>
</div>
<Form.Item label="最大库存" name="autoStockStep" rules={maxStockRule}>
<InputNumber
min={0}
max={999999999}
className={styles['stock-box--inputnum']}
onChange={onChangeMaxStock}
/>
</Form.Item>
<Form.Item label="自动补足" name="autoStock">
<Switch
checkedChildren="开启"
checked={isChecked}
unCheckedChildren="关闭"
onChange={setIsChecked}
/>
</Form.Item>
</Form>
<div className={styles['stock-box--red']}>修改成功后,原库存将被替换,请谨慎操作</div>
</Modal>
);
};
export default StockModal;
import React from 'react';
import { Modal, Form, Select } from 'antd';
const SwitchGroupModal = options => {
const [form] = Form.useForm();
const handleCancel = () => {
options.cancel(false);
};
const handleOk = async () => {
const values = await form.validateFields();
console.log('values :>> ', values);
options.confirm({
type: 3,
...values,
});
};
const radioOptions = [{ label: '', value: 1 }, { label: '', value: 0 }];
const initialValues = Object.assign({}, options.initialValues);
return (
<Modal
visible={options.visible}
title="更改分组"
onOk={handleOk}
maskClosable={false}
keyboard={false}
confirmLoading={options.loading}
destroyOnClose
onCancel={handleCancel}
>
<Form
name="basic"
form={form}
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}
initialValues={initialValues}
autoComplete="off"
>
<Form.Item
label="分组"
name="storageRackId"
rules={[{ required: true, message: '请选择!' }]}
>
<Select options={radioOptions} />
</Form.Item>
</Form>
</Modal>
);
};
export default SwitchGroupModal;
import React, { useState, useEffect } from 'react';
import { Modal, Radio, Form, TimePicker, Checkbox } from 'antd';
import { MinusSquareOutlined, PlusSquareOutlined } from '@ant-design/icons';
import moment from 'moment';
import { deepClone } from '@/utils/utils';
import { saleWeeks } from '../../staticdata';
import styles from '../../style.less';
const WeekTime = options => {
const [form] = Form.useForm();
const [type, setType] = useState(0);
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
};
const formItemLayoutWithOutLabel = {
wrapperCol: {
xs: { span: 22, offset: 0 },
sm: { span: 16, offset: 6 },
},
};
const onChangeType = ({ target: { value } }) => {
setType(value);
};
const radioOptions = [{ label: '全时段', value: 0 }, { label: '自定义售卖时间', value: 1 }];
const handleCancel = () => {
options.cancel(false);
};
const handleOk = async () => {
const params = await form.validateFields();
// const params = values;
if (params.saleTimes && params.saleTimes.length) {
params.saleTimes = params.saleTimes.map(item => ({
startTime: moment(item[0]).format('HH:mm'),
endTime: moment(item[1]).format('HH:mm'),
}));
}
options.confirm({
type: 4,
...params,
});
};
const onTimeValidator = (rule, value, callback) => {
if (value && value.length === 2) {
if (moment(value[0]).format('HH:mm') === moment(value[1]).format('HH:mm')) {
callback(new Error('请输入大于0的数字'));
} else {
callback();
}
} else {
callback();
}
};
const initialValues = Object.assign(
{
saleTimeType: 0,
saleDates: [],
saleTimes: [[]],
},
options.initialValues,
);
useEffect(() => {
options.visible && setType(0);
}, [options.visible]);
return (
<Modal
visible={options.visible}
title="售卖时间"
onOk={handleOk}
confirmLoading={options.loading}
maskClosable={false}
keyboard={false}
destroyOnClose
onCancel={handleCancel}
>
<Form
name="basic"
form={form}
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}
initialValues={initialValues}
autoComplete="off"
>
<Form.Item
label="售卖时间段类型"
name="saleTimeType"
rules={[{ required: true, message: '请选择售卖时间段类型!' }]}
>
<Radio.Group
options={radioOptions}
onChange={onChangeType}
value={type}
optionType="button"
buttonStyle="solid"
/>
</Form.Item>
{type === 1 ? (
<>
<Form.Item
label="售卖日期"
name="saleDates"
rules={[{ required: true, message: '请选择售卖日期!' }]}
>
<Checkbox.Group options={saleWeeks} />
</Form.Item>
<Form.List
label="售卖时间"
name="saleTimes"
rules={[
{
validator: async (_, saleTimes) => {
if (!saleTimes || saleTimes.length < 1) {
return Promise.reject(new Error('请选择售卖时间!'));
}
return Promise.resolve();
},
},
]}
>
{(fields, { add, remove }) => (
<>
{fields.map((field, index) => (
<Form.Item
label={index === 0 ? '售卖日期' : ''}
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
required
key={field.key}
>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
rules={[
{
required: true,
message: '请选择售卖时间',
},
{
validator: onTimeValidator,
message: '结束时间不能和开始时间相同',
},
]}
noStyle
>
<TimePicker.RangePicker format="HH:mm" minuteStep={30} />
</Form.Item>
{index > 0 ? (
<MinusSquareOutlined
className={[styles['week-time-box--icon'], styles.error]}
onClick={() => remove(field.name)}
/>
) : (
<PlusSquareOutlined
className={[styles['week-time-box--icon'], styles.primary]}
onClick={() => add()}
/>
)}
</Form.Item>
))}
</>
)}
</Form.List>
</>
) : (
''
)}
</Form>
</Modal>
);
};
export default WeekTime;
import React, { useState, useEffect, useRef } from 'react';
import { Spin, Table, Pagination, message, notification } from 'antd';
import { unstable_batchedUpdates } from 'react-dom';
import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
import { arrayMoveImmutable } from 'array-move';
import { GOOD_MANAGE } from '@/../config/permission.config';
import PubSub from 'pubsub-js';
import GoodsGroup from './components/GoodsGroup';
import {
apiTakeawayGoods,
apiGoodsActionBatch,
apiSortTakeawayGoods,
apiTopTakeawayGoods,
} from '../service';
import styles from '../style.less';
import { takeawayColumn } from '../staticdata';
// import VirtualTable from './components/VirtualTable';
import ActionBar from './components/ActionBar';
import WeekTime from './components/WeekTime';
import StockModal from './components/StockModal';
import SendModal from './components/SendModal';
import MinimumPurchaseModal from './components/MinimumPurchase';
import SwitchGroupModal from './components/SwitchGroupModal';
const Takeaway = options => {
const [tableData, setTableData] = useState([]);
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [shopId, setShopId] = useState(0);
const [groupId, setGroupId] = useState(0);
const [pageNo, setPageNo] = useState(1);
const [pageSize, setPageSize] = useState(50);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(false);
const [actionLoading, setActionLoading] = useState(false);
const [visibleWeekTime, setVisibleWeekTime] = useState(false);
const [visibleStock, setVisibleStock] = useState(false);
const [visibleBuy, setVisibleBuy] = useState(false);
const [visibleSend, setVisibleSend] = useState(false);
const [visibleSwitchGroup, setVisibleSwitchGroup] = useState(false);
const [scribeToken, setScribeToken] = useState('');
const groupRef = useRef(null);
const rowSelection = {
selectedRowKeys,
onChange: setSelectedRowKeys,
};
const getDataList = async (page = pageNo, size = pageSize, storageRackId = groupId) => {
setLoading(true);
const params = Object.assign({}, options.searchValue, {
pageNo: page || pageNo,
productType: 5,
pageSize: size || pageSize,
storageRackId,
});
const productCategoryId = options.searchValue?.productCategoryId || [];
params.productCategoryId =
(productCategoryId.length && productCategoryId[productCategoryId.length - 1]) || '';
const res = await apiTakeawayGoods(params);
setLoading(false);
if (res && res.data) {
setTableData(res.data.records || []);
setTotal(res.data.total || 0);
}
};
const onPageChange = (page, size) => {
unstable_batchedUpdates(() => {
setPageNo(page);
setPageSize(size);
});
getDataList(page, size);
};
const onSortEnd = async ({ oldIndex, newIndex }) => {
if (oldIndex !== newIndex) {
const sourceGoods = tableData[oldIndex];
const targetGoods = tableData[newIndex];
// const newData = arrayMoveImmutable(tableData.slice(), oldIndex, newIndex).filter(el => !!el);
// const skuSorts = newData.map((item, index) => ({
// skuId: item.skuId,
// sort: pageSize * (pageNo - 1) + index + 1,
// }));
if (sourceGoods && sourceGoods.skuId && targetGoods && targetGoods.skuId) {
const params = {
storageRackId: groupId,
shopId,
sourceSkuId: sourceGoods.skuId,
targetSkuId: targetGoods.skuId,
};
await apiSortTakeawayGoods(params);
getDataList(pageNo, pageSize);
}
}
};
const SortableItem = SortableElement(props => <tr {...props} />);
const SortableBody = SortableContainer(props => <tbody {...props} />);
const DraggableContainer = props => (
<SortableBody
useDragHandle
disableAutoscroll
helperClass={styles['row-dragging']}
onSortEnd={onSortEnd}
{...props}
/>
);
const DraggableBodyRow = ({ className, style, ...restProps }) => {
// function findIndex base on Table rowKey props and should always be a right array index
const index = tableData.findIndex(x => x.skuId === restProps['data-row-key']);
return <SortableItem index={index} {...restProps} />;
};
// 批量操作 type 1-是否列出 2-修改上下架 3-改货架 4-售卖时间更新 5-调整商品起购数量 6-调整商品是否单点不送 7-修改库存
const handleBatchAction = async params => {
const json = {
skuIds: selectedRowKeys,
shopId,
};
setActionLoading(true);
const res = await apiGoodsActionBatch(Object.assign({}, json, params));
setActionLoading(false);
if (res.businessCode === '0000' && res.code === '0000') {
message.success('处理成功!');
unstable_batchedUpdates(() => {
setActionLoading(false);
setVisibleWeekTime(false);
setVisibleStock(false);
setVisibleSwitchGroup(false);
setVisibleBuy(false);
setVisibleSend(false);
});
getDataList(pageNo, pageSize);
}
};
// 显示弹窗
const openModal = type => {
type === 'time' && setVisibleWeekTime(true);
type === 'stock' && setVisibleStock(true);
type === 'group' && setVisibleSwitchGroup(true);
type === 'buy' && setVisibleBuy(true);
type === 'send' && setVisibleSend(true);
};
// 单商品修改库存
const onShowStockModal = ({ skuId }) => {
setSelectedRowKeys([skuId]);
openModal('stock');
};
// 编辑
const onEdit = ({ spuId, skuId }) => {
options.handleEdit({
shopId,
spuId,
skuId,
});
};
// 新建商品
const onNew = () => {
options.handleEdit({
shopId,
groupId,
});
};
// 置顶
const toTop = async ({ skuId }) => {
// onSortEnd({ oldIndex, newIndex: 0 });
const res = await apiTopTakeawayGoods({
productItemId: skuId,
shopId,
storageRackId: groupId,
});
if (res.businessCode === '0000' && res.code === '0000') {
getDataList(pageNo, pageSize);
message.success('处理成功!');
}
};
useEffect(() => {
if (groupId) {
setPageNo(1);
getDataList(1, pageSize, groupId);
} else {
setTableData([]);
}
}, [groupId, options.refresh]);
useEffect(() => {
const stoken = PubSub.subscribe('refreshTakeAway', (_, data) => {
console.log('refreshTakeAway :>> ', data);
if (data.groupId && groupId !== data.groupId) {
setGroupId(data.groupId);
if (groupRef.current) {
groupRef.current.setSelected(`${data.groupId}`);
}
}
});
setScribeToken(stoken);
return () => {
PubSub.unsubscribe(scribeToken);
};
}, []);
const actions = {
onShowStockModal,
toTop,
onEdit,
getDataList,
shopId,
pageNo,
};
const canAddTakeaway = options.permissions[GOOD_MANAGE.ADD_TAKEAWAY_GOODS];
return (
<div className={styles.takeawayBox}>
<Spin spinning={loading}>
<GoodsGroup
ref={groupRef}
shopId={shopId}
changeShop={setShopId}
changeGroup={setGroupId}
/>
{(shopId && (
<ActionBar
selectedRowKeys={selectedRowKeys}
shopId={shopId}
canAddTakeaway={canAddTakeaway}
handleSearch={getDataList}
openModal={openModal}
newGoods={onNew}
/>
)) ||
''}
<Table
dataSource={tableData}
bordered
columns={takeawayColumn(actions)}
rowKey={record => record.skuId}
pagination={false}
scroll={{ x: '100%', y: 500 }}
rowSelection={rowSelection}
components={{
body: {
wrapper: DraggableContainer,
row: DraggableBodyRow,
},
}}
/>
<br />
{(tableData && (
<Pagination
className={styles['takeawayBox--page']}
onChange={onPageChange}
total={total}
showTotal={o => `共${o}条`}
current={pageNo}
pageSize={pageSize}
showSizeChanger
onShowSizeChange={onPageChange}
/>
)) ||
''}
</Spin>
<WeekTime
visible={visibleWeekTime}
loading={actionLoading}
confirm={handleBatchAction}
cancel={setVisibleWeekTime}
/>
<StockModal
visible={visibleStock}
loading={actionLoading}
skuIds={selectedRowKeys}
shopId={shopId}
confirm={handleBatchAction}
cancel={setVisibleStock}
/>
<SendModal
visible={visibleSend}
loading={actionLoading}
confirm={handleBatchAction}
cancel={setVisibleSend}
/>
<MinimumPurchaseModal
visible={visibleBuy}
loading={actionLoading}
confirm={handleBatchAction}
cancel={setVisibleBuy}
/>
<SwitchGroupModal
visible={visibleSwitchGroup}
loading={actionLoading}
confirm={handleBatchAction}
cancel={setVisibleSwitchGroup}
/>
</div>
);
};
export default Takeaway;
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Modal, Input, Select, Cascader, Tag, notification } from 'antd';
import { da } from 'date-fns/locale';
import React, { useState, useEffect } from 'react';
import { getTemplate, getAfterAddress } from '../service';
const { Option } = Select;
const TempleatModal = props => {
const {
visible,
form: { getFieldDecorator, validateFields, resetFields },
selectedRowKeys,
templateList,
isALL,
isType,
total,
} = props;
const formItemLayout = {
labelCol: {
span: 6,
},
wrapperCol: {
span: 16,
},
};
const setAfterAddress = async fieldsValue => {
const data = await getAfterAddress({
skuIds: selectedRowKeys || [],
afterAddressId: fieldsValue.templateId.key,
});
if (data.businessCode === '0000') {
notification.success({ message: '配置成功!' });
resetFields();
props.onCancel();
}
};
const setTemplate = async fieldsValue => {
const data = await getTemplate({
isAll: isALL,
skuIdList: selectedRowKeys,
templateId: fieldsValue.templateId.key,
});
if (data.businessCode === '0000') {
notification.success({ message: '配置成功!' });
resetFields();
props.onCancel();
}
};
const handleOk = async () => {
validateFields(async (error, fieldsValue) => {
if (!error) {
if (isType === 'after') {
setAfterAddress(fieldsValue);
}
if (isType === 'distribution') {
setTemplate(fieldsValue);
}
}
});
};
const title = () => {
if (isType === 'distribution') {
if (isALL) {
return '全部商品配送区域设置';
}
return '勾选商品配送区域设置';
}
if (isType === 'after') {
if (isALL) {
return '全部商品售后地址设置';
}
return '勾选商品售后地址设置';
}
return '';
};
return (
<Modal
title={title()}
visible={visible}
width="500px"
onCancel={props.onCancel}
onOk={() => handleOk()}
>
{!isALL && <p>已选择{selectedRowKeys.length}个商品</p>}
{isALL > 0 && isType === 'after' && <p>已选择{total}个商品</p>}
<Form {...formItemLayout}>
<Form.Item label={isType === 'after' ? '选择售后地址' : '选择模板'}>
{getFieldDecorator('templateId', {
rules: [
{
required: true,
message: `${isType === 'after' ? '请选择售后地址' : '请选择模板'}`,
},
],
})(
<Select
placeholder={isType === 'after' ? '选择售后地址' : '选择模板'}
labelInValue
allowClear
>
{isType === 'distribution' &&
templateList.map(item => (
<Option label={item.templateName} value={item.id} key={item.id}>
{item.templateName}
</Option>
))}
{isType === 'after' &&
templateList.map(item => (
<Option label={item.addressName} value={item.id} key={item.id}>
{item.addressName}
</Option>
))}
</Select>,
)}
</Form.Item>
</Form>
</Modal>
);
};
export default Form.create()(TempleatModal);
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Modal, InputNumber, notification, Input, Radio } from 'antd';
import React, { useState } from 'react';
import { apiCreateGoodsLog } from '../service';
import styles from '../style.less';
import { isNumberSection } from '@/utils/validator';
const UpdateStock = props => {
const { getFieldDecorator, validateFields, resetFields, getFieldValue } = props.form;
const valueInfo = props.info;
const [loading, setLoading] = useState(false);
const submit = async () => {
validateFields(async (err, { stock, changeReason, changeType }) => {
if (err) return;
setLoading(true);
const params = {
afterChange: stock,
supplierId: valueInfo.supplierId,
productIdType: 2,
changeType,
changeReason,
productIds: props.skuIds,
};
console.log('params :>> ', params);
const res = await apiCreateGoodsLog(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,
},
};
const validatorCallback = (rule, value, callback) => {
// 减库存存时,校验可售库存-输入值>=0,即可售库存不可为负;
const changeType = getFieldValue('changeType');
const increment = valueInfo.marketableStock - value;
console.log('value :>> ', value, valueInfo.marketableStock);
console.log('valueInfo :>> ', valueInfo, increment);
return +changeType === 29 && increment < 0 ? callback(new Error(rule.message)) : callback();
};
return (
<Modal
title="修改库存"
visible={props.visible}
okButtonProps={{ loading, disabled: valueInfo.status === 1 }}
onCancel={onCancel}
onOk={submit}
width={500}
>
<Form {...formItemLayout}>
{valueInfo.curStock ? (
<Form.Item label="当前库存:">
<span>{valueInfo.curStock}</span>
</Form.Item>
) : (
<Form.Item label="可修改库存商品数:">
<span>{props.skuIds.length}</span>
</Form.Item>
)}
<Form.Item label="变更类型:">
{getFieldDecorator('changeType', {
rules: [{ required: true, message: '请选择类型!' }],
initialValue: valueInfo.changeType || 28,
})(
<Radio.Group disabled={valueInfo.status === 1}>
<Radio value={28}>增库存</Radio>
<Radio value={29}>减库存</Radio>
<Radio value={30}>使用新值</Radio>
</Radio.Group>,
)}
<div className={styles.stockTip}>库存变更审批通过后生效</div>
</Form.Item>
<Form.Item label="库存数:">
{getFieldDecorator('stock', {
rules: [
{ required: true, message: '请输入库存!' },
{ validator: validatorCallback, message: '减库存,输入库存数不可大于可售库存!' },
{ validator: isNumberSection, min: 1, max: 500, message: '请输入1-500的整数' },
],
validateTrigger: ['onSubmit', 'onChange'],
initialValue: valueInfo.stock,
})(
<InputNumber
min={0}
precision={0}
placeholder="请输入库存"
disabled={valueInfo.status === 1}
style={{ width: 200 }}
/>,
)}
</Form.Item>
<Form.Item label="变更原因:">
{getFieldDecorator('changeReason', {
rules: [{ required: true, message: '请输入变更原因!' }],
initialValue: valueInfo.changeReason,
})(<Input.TextArea disabled={valueInfo.status === 1} maxLength={50} />)}
</Form.Item>
{valueInfo.stateDesc && (
<div className={styles.stockErrMsg}>
{valueInfo.stateDesc}{valueInfo.rejectReason}
</div>
)}
</Form>
</Modal>
);
};
export default Form.create()(UpdateStock);
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Card, Pagination, Table, notification, Drawer, Spin, Button, Modal } from 'antd';
import React, { Component } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { connect } from 'dva';
import styles from './style.less';
import UpdateStock from './UpdateStock';
import {
spuDetail,
categoryList,
apiCategoryListType,
getVirtualCategory,
getTemplateList,
specList,
queryAllAfterAddress,
apiEnableUpdataStock,
apiQueryLastChangeLog,
} from './service';
import LogModal from './LogModal';
import { column, JDSHOPID, ProcessEditData } from './staticdata';
import SearchForm from './SearchForm';
import TempleatModal from './TempleatModal';
import ServiceGoods from '../ServiceGoods';
import InfoAudit from './infoAudit';
import DraftModal from './DraftModal';
import Takeaway from './Takeaway';
import { GOOD_MANAGE } from '@/../config/permission.config';
import LocalStroage from '@/utils/localStorage';
import configApi from '@/../config/env.config';
import { GoldCategory } from '@/config/app.config';
@connect(({ goodsManage, menu }) => ({
goodsManage,
permissions: menu.permissions,
}))
class goodsManage extends Component {
state = {
pageNo: 1,
loading: false,
treeData: [],
categoryTree: [],
virtualTreeData: [],
pageSize: 20,
priceInfo: {},
logVisible: false,
previewVisible: false,
updateStockVisible: false,
createloading: false,
specListData: [],
templeatModalVisible: false,
selectedRowKeys: [],
isAll: 0,
templateList: [],
stockSkuIds: [],
isType: '',
serviceVisble: false,
serviceData: {},
visibleAuditModal: false,
auditRow: {}, // 查看审核信息使用
isVisibleDraft: false, // 显示隐藏草稿箱
isEditDraft: false, // 是否编辑草稿
productType: 1, // 商品类型
takeAway: {}, // 弹窗外卖商品参数
searchValue: {}, // 搜索条件
refresh: '', // 外卖刷新
};
currentLog = null;
supplierId = null;
shopList = [];
canEditable = false;
componentDidMount() {
this.props.goodsManage.tableData = {};
this.categoryList(this.state.productType);
this.categoryListByType(this.state.productType);
this.getVirtualCategory();
this.specList();
console.log('this.state.productType', this.state.productType);
}
handleSearch = page => {
const searchValue = this.searchForm.getFieldsValue() || {};
this.setState({ searchValue });
if (searchValue.productType !== 5) {
this.onSelectChange([]);
const currentPage = this.state.pageNo;
this.setState(
{
pageNo: page || currentPage,
loading: true,
},
() => {
const { dispatch } = this.props;
const { pageSize, pageNo } = this.state;
dispatch({
type: 'goodsManage/getList',
payload: {
pageNo,
pageSize,
...searchValue,
},
}).finally(() => {
this.setState({
loading: false,
});
});
},
);
} else {
this.setState({
refresh: new Date().getTime(),
searchValue,
});
}
};
onPageChange = page => {
this.handleSearch(page);
};
audit = skuId => {
this.setState({
previewVisible: true,
src: `${configApi.prologueDomain}/goods/${skuId}?h=0&token=${LocalStroage.get(
'token',
)}&hideReport=1&time=${Date.now()}`,
});
};
onPageSizeChange = (current, size) => {
this.setState(
{
pageSize: size,
},
() => this.handleSearch(),
);
};
onReset = () => {
this.setState({
pageNo: 1,
pageSize: 20,
selectedRowKeys: [],
});
this.handleSearch();
};
onLoad = error => {
if (!error) {
notification.success({ message: '操作成功' });
this.setState({
selectedRowKeys: [],
});
this.handleSearch();
}
};
viewLog = async rows => {
this.currentLog = rows;
this.setState({
logVisible: true,
});
};
setArea = async (isAll, type) => {
// distribution配送区域 after售后地址
if (!this.state.selectedRowKeys.length && !isAll) {
notification.error({ message: '请选择商品' });
return;
}
const data = type === 'distribution' ? await getTemplateList() : await queryAllAfterAddress();
const length = type === 'distribution' ? data.data.length : data.data.records.length;
if (data.code === '0000' && length) {
this.setState({
templeatModalVisible: true,
isAll,
templateList: type === 'distribution' ? data.data : data.data.records,
isType: type,
});
} else {
notification.error({ message: '暂无数据' });
}
};
filterShopList = (list = [], isEdit) =>
list.filter(item => isEdit || !JDSHOPID.includes(item.id));
// 验证是否可以修改库存
checkEnableUpdateStock = async () => {
const ids = this.state.selectedRowKeys.join(',');
if (ids) {
const res = await apiEnableUpdataStock(ids);
if (res.data) {
if (res.data.successSkuIds?.length) {
this.setState({
priceInfo: {},
stockSkuIds: res.data.successSkuIds,
});
this.openModal({}, 1);
} else {
const message = res.data.failedInfoList[0]?.message || '未存在可修改库存的商品';
notification.info({ message });
}
}
} else {
notification.info({ message: '请选择' });
}
};
openModal = (
{
skuId,
supplyPrice,
marketPrice,
salePrice,
marketableStock,
supplierId,
stock,
productStock,
state,
},
isStock,
) => {
let visible = {};
if (isStock) {
visible = { updateStockVisible: true };
}
this.setState({
...visible,
priceInfo: {
id: skuId,
stock,
productStock,
marketableStock,
supplyPrice,
marketPrice,
salePrice,
supplierId,
state,
},
});
};
onShowStockModal = async row => {
const res = await apiQueryLastChangeLog(row.skuId);
let priceInfo = {
id: row.skuId,
curStock: row.productStock,
supplierId: row.supplierId,
marketableStock: row.stock,
};
if (res.data && [1, 3].includes(+res.data.status)) {
priceInfo = Object.assign(
{
changeReason: res.data.changeReason,
stock: +res.data.afterChange,
changeType: res.data.changeType,
status: res.data.status,
stateDesc: res.data.statusDesc,
rejectReason: res.data.rejectReason,
},
priceInfo,
);
}
this.setState({
updateStockVisible: true,
priceInfo,
stockSkuIds: [row.skuId],
});
};
cancel = query => {
this.setState({ updateStockVisible: false });
if (query) {
this.handleSearch();
}
};
categoryList = async () => {
try {
const { data: treeData } = await categoryList({});
if (!treeData) return;
this.setState({ treeData });
} catch (e) {
console.log(e);
}
};
categoryListByType = async type => {
try {
const { data: categoryTree } = await apiCategoryListType(type);
if (!categoryTree) return;
this.setState({ categoryTree });
} catch (e) {
console.log(e);
}
};
changeProductType = e => {
this.setState({
productType: e || 1,
});
this.categoryListByType(e);
if (e !== 5) {
this.handleSearch(1);
}
};
getVirtualCategory = async () => {
try {
const { data: virtualTreeData } = await getVirtualCategory();
if (!virtualTreeData) return;
this.setState({ virtualTreeData });
} catch (e) {
console.log(e);
}
};
specList = async () => {
const data = await specList();
if (data.businessCode === '0000' && data.data.length) {
this.setState({ specListData: data.data });
}
};
onSelectChange = selectedRowKeys => {
this.setState({
selectedRowKeys,
});
};
serviceVisbleChange = async row => {
const { state, spuId, productType } = row;
this.setState({ createloading: true });
const { data, msg } = await spuDetail({ id: spuId }); // spuId
if (data) {
const SourceData = ProcessEditData(data, row);
console.log('index.js=============>', SourceData);
// 投资金商品重量单位转换 从g转换为kg
if (SourceData?.infoMation?.categoryId.includes(GoldCategory)) {
SourceData.skuList?.forEach(item => {
if (item.weight) {
item.weight = +(item.weight * 1000).toFixed(2);
}
});
}
this.setState({
serviceData: SourceData,
serviceVisble: true,
createloading: false,
isEditDraft: false,
});
} else {
this.setState({
createloading: false,
});
notification.warning({
message: msg,
});
}
};
// 编辑草稿
editDraft = data => {
this.setState({
serviceData: data,
serviceVisble: true,
isEditDraft: true,
});
};
// 显示新增商品弹窗
serviceVisbleClose = (flag, refresh) => {
this.setState({
serviceVisble: flag,
isEditDraft: false,
serviceData: {},
takeAway: {},
});
if (refresh) {
this.handleSearch();
}
};
// 打开草稿箱
openDraftModal = e => {
console.log('e :>> ', e);
this.setState({
isVisibleDraft: !!e,
});
};
onEdit = () => {
this.setState({ visibleAuditModal: false, auditRow: {} });
this.serviceVisbleChange(this.state.auditRow);
};
// 显示外卖商品弹窗
handleTakeawayEdit = params => {
this.setState({
takeAway: params,
serviceVisble: true,
});
};
render() {
const {
goodsManage: { tableData = {} },
permissions,
} = this.props;
const rowSelection = {
selectedRowKeys: this.state.selectedRowKeys,
onChange: this.onSelectChange,
};
const { pageNo, pageSize, selectedRowKeys } = this.state;
const canAddService = permissions[GOOD_MANAGE.ADD_SERVICE_GOODS];
const canAddNormal = permissions[GOOD_MANAGE.ADD_NORMAL_GOODS];
const canAddTakeaway = permissions[GOOD_MANAGE.ADD_TAKEAWAY_GOODS];
this.canEditable = permissions[GOOD_MANAGE.EDITABLE];
// console.log('serviceData', this.state.serviceData);
// console.log('shopList', this.shopList);
// console.log('treeData', this.state.treeData);
// console.log('treeData', this.state.treeData);
// console.log('treeData', this.state.treeData);
// console.log('3333333', permissions);
return (
<PageHeaderWrapper>
{/* {canAddNormal || canAddService || canAddTakeaway
? [
<Button
type="primary"
key="btnNew"
className={styles.button}
onClick={() => this.serviceVisbleClose(true)}
>
新增商品
</Button>,
<Button
type="link"
key="btnDraft"
className={styles.button}
onClick={this.openDraftModal}
>
草稿箱
</Button>,
]
: ''} */}
<Spin spinning={this.state.createloading}>
<Card>
<SearchForm
handleSearch={this.handleSearch}
onReset={this.onReset}
onLoad={this.onLoad}
onRef={ref => {
this.searchForm = ref;
}}
treeData={this.state.categoryTree}
shopList={this.shopList}
checkStock={this.checkEnableUpdateStock}
selectNum={selectedRowKeys.length}
changeProductType={this.changeProductType}
setArea={(isALL, type) => this.setArea(isALL, type)}
/>
</Card>
{this.state.productType === 5 ? (
<Takeaway
handleEdit={this.handleTakeawayEdit}
searchValue={this.state.searchValue}
permissions={permissions}
refresh={this.state.refresh}
/>
) : (
<>
<Spin spinning={this.state.loading}>
<Table
dataSource={tableData?.records}
bordered
columns={column.call(this)}
rowKey={record => record.skuId}
pagination={false}
className={styles.tabletop}
scroll={{ x: '100%', y: 500 }}
rowSelection={rowSelection}
/>
</Spin>
<br />
{tableData && (
<Pagination
style={{ marginBottom: 10 }}
onChange={this.onPageChange}
total={tableData.total}
showTotal={total => `共${total}条`}
current={pageNo}
pageSize={pageSize}
showSizeChanger
onShowSizeChange={this.onPageSizeChange}
/>
)}
<LogModal
visible={this.state.logVisible}
spuId={this.currentLog?.spuId}
id={this.currentLog?.skuId}
onCancel={() => {
this.currentLog = null;
this.setState({ logVisible: false });
}}
/>
<Drawer
visible={this.state.previewVisible}
width="450"
onClose={() => {
this.setState({ previewVisible: false });
}}
title="商品预览"
bodyStyle={{ height: '90%' }}
>
<iframe
src={this.state.src}
frameBorder="0"
height="100%"
width="375"
title="商品预览"
></iframe>
</Drawer>
<UpdateStock
visible={this.state.updateStockVisible}
skuIds={this.state.stockSkuIds}
info={this.state.priceInfo}
onCancel={this.cancel}
/>
<TempleatModal
visible={this.state.templeatModalVisible}
selectedRowKeys={this.state.selectedRowKeys}
total={tableData.total || 0}
onCancel={() => {
this.setState({ templeatModalVisible: false, selectedRowKeys: [] });
this.handleSearch();
}}
isALL={this.state.isAll}
isType={this.state.isType}
templateList={this.state.templateList}
/>
</>
)}
</Spin>
{this.state.serviceVisble && (
<ServiceGoods
visible={this.state.serviceVisble}
onChange={this.serviceVisbleClose}
SourceData={this.state.serviceData}
shopList={this.shopList}
categoryList={this.state.treeData}
virtualCategoryList={this.state.virtualTreeData}
specListData={this.state.specListData}
permissions={permissions}
isDraft={this.state.isEditDraft}
productType={this.state.productType}
takeAway={this.state.takeAway}
/>
)}
{this.state.visibleAuditModal && (
<InfoAudit
visible={this.state.visibleAuditModal}
skuInfo={this.state.auditRow}
canEditable={this.canEditable}
onCancel={() => {
this.setState({ visibleAuditModal: false, auditRow: {} });
}}
onEdit={this.onEdit}
/>
)}
{this.state.isVisibleDraft && (
<DraftModal
visible={this.state.isVisibleDraft}
onCancel={this.openDraftModal}
onToDetail={this.editDraft}
/>
)}
</PageHeaderWrapper>
);
}
}
export default Form.create()(goodsManage);
import React, { useState, useEffect } from 'react';
import { Modal, Button } from 'antd';
import { apiQueryLastAuditRecord } from '../service';
const InfoAudit = props => {
const [audit, setAudit] = useState({});
const getRecord = async () => {
const res = await apiQueryLastAuditRecord(props.skuInfo.skuId);
if (res && res.data) {
console.log('res :>> ', res);
setAudit(res.data);
}
};
const getContent = () => {
const obj = {
1: '-',
2: '审核通过',
3: `审核拒绝,${audit.rejectReason}`,
};
if (audit) {
return obj[audit.status] || '';
}
return '';
};
useEffect(() => {
if (props.visible) {
getRecord();
}
}, [props.visible]);
return (
<Modal
title="商品信息变更审核"
visible={props.visible}
closable={false}
footer={[
audit.status === 3 &&
props.canEditable &&
(props.skuInfo.state === 4 ||
(props.skuInfo.state >= 5 && props.skuInfo.updateState !== 1)) && (
<Button key="back" type="primary" onClick={() => props.onEdit()}>
再次编辑
</Button>
),
<Button key="close" onClick={() => props.onCancel()}>
关闭
</Button>,
]}
>
<p>审核状态:{audit.statusDesc}</p>
<p>申请时间:{audit.createdAt}</p>
<p>审核结果:{getContent()}</p>
</Modal>
);
};
export default InfoAudit;
import * as api from './service';
const Model = {
namespace: 'popGoodsManage',
state: {
tableData: {},
shopList: [],
statusList: [],
cid1List: [],
cid2List: [],
cid3List: [],
treeData: [],
},
effects: {
*getList({ payload }, { call, put }) {
const params = payload;
const productCategoryId = payload?.productCategoryId || [];
params.productCategoryId =
(productCategoryId.length && productCategoryId[productCategoryId.length - 1]) || '';
const res = yield call(api.searchList, params);
if (res && !res.data) return;
yield put({
type: 'saveData',
payload: {
tableData: res.data,
},
});
},
*categoryList({ payload }, { call, put }) {
const [data] = yield call(api.categoryList, payload.value);
if (!data) return;
yield put({
type: 'saveCategory',
payload: {
[payload.categoryNum]: data,
},
});
},
},
reducers: {
saveData(state, action) {
const data = action.payload;
return { ...state, ...data };
},
dataList(state, action) {
const data = action.payload;
return { ...state, ...data };
},
saveCategory(state, action) {
const data = action.payload;
return { ...state, ...data };
},
},
};
export default Model;
// import fileSaver from 'file-saver';
import request from '@/utils/request';
import config from '../../../config/env.config';
import { stringify } from 'qs';
import { saveAs } from 'file-saver';
import { format } from 'date-fns';
import _ from 'lodash';
const { goodsApi, kdspApi } = config;
const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
/**
* 商品列表
* yapi: http://yapi.quantgroups.com/project/389/interface/api/23814
*/
export async function searchList(params) {
return request.post('//api/merchants/pops/product/page', {
prefix: goodsApi,
data: stringify(params),
headers,
role: true,
});
}
// 新增商品
export async function addGoods(params) {
return request.post('/product/api/merchant/add', {
prefix: goodsApi,
data: params,
});
}
// 编辑商品
export async function editGoods(params) {
return request.post('/api/merchants/pops/product/edit', {
prefix: goodsApi,
data: params,
});
}
// 获取商品品牌
export async function getBrandList() {
return request.post('/product/brand/api/merchant/list', {
prefix: goodsApi,
});
}
// 获取类目关联属性
export async function apiGetAttribute(categoryId) {
const data = await request.get(
`/api/kdsp/category/template/ref/attribute/detail?categoryId=${categoryId}`,
{
prefix: goodsApi,
},
);
return data;
}
// 编辑--获取详情
export async function spuDetail(params) {
return request.post('//api/merchants/pops/product/detail', {
prefix: goodsApi,
params,
headers,
});
}
// 商品规格
export async function getSpecList() {
return request.post('/product/spec/api/merchant/list', {
prefix: goodsApi,
});
}
// 查询图片素材
export async function getImageInfo(spuNo) {
return request.post('/product/api/merchant/imageInfo', {
params: { spuNo },
prefix: goodsApi,
headers,
});
}
// 商品分类
export async function categoryList() {
return request.post('/product/category/api/merchant/getAll', {
prefix: goodsApi,
});
}
/**
* 商品分类
* type 商品类型:1-实物类,2-虚拟类,4-服务类 5 外卖
* */
export async function apiCategoryListType(type) {
return request.get(`/product/category/getByProductType/${type}`, {
prefix: goodsApi,
});
}
// 批量修改
export async function uploadFile(file) {
const params = new FormData();
params.append('file', file);
const data = await request.post('/product/api/merchant/BatchUpdateStock', {
prefix: goodsApi,
data: params,
notTip: true,
});
return data;
}
// 商品修改日志
export async function changeLog(params) {
return request.post('/product/logs/api/merchant/page', {
data: stringify(params),
prefix: goodsApi,
headers,
});
}
// 审核详情日志
export async function productMerchantLog(productId) {
return request.get('//api/merchants/pops/product/auditLog', {
params: {
productId,
},
prefix: goodsApi,
});
}
// 拉去京东图片
export async function getJdPicList(params) {
const { data } = await request.post('/product/api/merchant/item/getJdPicList', {
data: stringify(params),
prefix: goodsApi,
headers,
});
return data;
}
// 图片上传
export async function uploadImg(files) {
const params = new FormData();
files.forEach(file => params.append('file', file));
const data = await request.post('/image/api/merchant/upload', {
prefix: goodsApi,
data: params,
});
return data;
}
// 修改商品库存
export async function updateStock(params) {
const data = await request.post('/product/item/api/merchant/updateStock', {
prefix: goodsApi,
data: stringify(params),
headers,
});
if (data.businessCode === '0000') {
return null;
}
return data.msg;
}
// 获取虚拟商品类目
export async function getVirtualCategory() {
const data = await request.post('/product/category/api/merchant/getByParentId', {
prefix: goodsApi,
data: stringify({ id: 100018 }),
headers,
});
return data;
}
// 查询商品属性列表
export async function specList() {
return request.get('/product/api/merchant/spec', {
prefix: goodsApi,
});
}
// sku绑定模板
export async function getTemplate(params) {
const data = await request.post('/api/kdsp/sku/template', {
data: params,
prefix: goodsApi,
});
return data;
}
// 不配送区域模板列表
export async function getTemplateList(params) {
const data = await request.post('/api/kdsp/template/list', {
data: stringify(params),
prefix: goodsApi,
headers,
});
return data;
}
// 查询供应商售后地址
export async function queryAllAfterAddress() {
const params = {
pageSize: 100,
pageNo: 1,
};
const data = request.post('/api/merchants/aftersales/addresses/list', {
prefix: kdspApi,
data: stringify(_.omitBy(params, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return data;
}
// 商品售后地址设置
export async function getAfterAddress(params) {
const data = await request.post('/api/kdsp/updateSkuAfterAddress', {
data: params,
prefix: goodsApi,
});
return data;
}
/**
* 商品是否可以做库存变更
* skuIds: 多个用英文逗号隔开
*/
export const apiEnableUpdataStock = skuIds =>
request.get(`/api/kdsp/sku/can/stockChange?skuIds=${skuIds}`, {
prefix: goodsApi,
});
// 商品明细导出
export const apiGoodsInfosExport = params =>
request.post('//api/merchants/pops/product/sku/export', {
prefix: goodsApi,
data: params,
responseType: 'arrayBuffer',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
});
// 新建商品审核记录
export const apiCreateGoodsLog = params =>
request.post('//api/merchants/pops/product/audit/create', {
data: params,
prefix: goodsApi,
});
// 查询sku最后一条库存变更记录
export const apiQueryLastChangeLog = skuId =>
request.get(`/api/kdsp/sku/last/stockChange/record?skuId=${skuId}`, {
prefix: goodsApi,
});
// 商品上下架
export const apiChangeStateGoods = async params => {
const data = await request.post('/product/api/merchant/pushedOrOffline/batch', {
data: stringify(params),
prefix: goodsApi,
headers,
});
return data;
};
// 查询sku最后一条审核记录
export const apiQueryLastAuditRecord = skuId =>
request.get(`/api/kdsp/sku/last/audit/record?skuId=${skuId}`, {
prefix: goodsApi,
});
// 商品草稿详情
export const apiDraftDetail = draftId =>
request.get(`/api/merchants/drafts/detail?id=${draftId}`, {
prefix: goodsApi,
});
// 删除商品草稿
export const apiDeleteDraft = draftId =>
request.get(`/api/merchants/drafts/delete?id=${draftId}`, {
prefix: goodsApi,
});
// 商品草稿列表
export async function apiDraftList(data) {
return request.post('/api/merchants/drafts/list', {
prefix: goodsApi,
data,
});
}
// 批量操作
export async function apiGoodsActionBatch(data) {
return request.post('/api/merchants/products/sku/batchOperation', {
prefix: goodsApi,
data,
});
}
// 外卖商品列表
export async function apiTakeawayGoods(params) {
return request.post('//api/merchants/pops/product/page', {
prefix: goodsApi,
data: stringify(params),
headers,
});
}
// 外卖商品排序
// export async function apiSortTakeawayGoods(data) {
// return request.post('/api/merchants/products/sku/batchSort', {
// prefix: goodsApi,
// data,
// });
// }
/**
* 外卖商品排序
* yapi: http://yapi.quantgroups.com/project/389/interface/api/58174
*/
export async function apiSortTakeawayGoods(data) {
return request.post('/api/merchants/products/sku/range/sort', {
prefix: goodsApi,
data,
});
}
// 外卖商品置顶
export async function apiTopTakeawayGoods(data) {
return request.post('/api/merchants/products/sku/storageRack/topping', {
prefix: goodsApi,
data,
});
}
// 获取供应商门店列表
export async function apiSupplierShopList(params) {
return request.get(`/api/merchants/shops/getBySupplierId?${stringify(params)}`, {
prefix: goodsApi,
});
}
// 分组创建(货架—创建货架)
export async function apiCreateStorage(data) {
return request.post('/api/merchants/products/storageRack/create', {
prefix: goodsApi,
data,
});
}
// 编辑分组(货架—编辑货架)
export async function apiEditStorage(data) {
return request.post('/api/merchants/products/storageRack/edit', {
prefix: goodsApi,
data,
});
}
// 分组详情(货架—货架详情)
export async function apiStorageInfo(params) {
return request.post('/api/merchants/products/storageRack/queryByShopIdAndStorageRackId', {
prefix: goodsApi,
data: stringify(params),
headers,
});
}
// 删除分组(货架—删除货架)
export async function apiDelStorage(params) {
return request.post('/api/merchants/products/storageRack/removeByShopIdAndId', {
prefix: goodsApi,
data: stringify(params),
headers,
});
}
// 分组排序(货架—排序货架)
export async function apiSortStorage(data) {
return request.post('/api/merchants/products/storageRack/batchSort', {
prefix: goodsApi,
data,
});
}
// 分组列表(货架—货架列表)
export async function apiStorageList(params) {
return request.post('/api/merchants/products/storageRack/listByShopIdAndStorageRackIds', {
prefix: goodsApi,
data: stringify(params),
headers,
});
}
// 获取库存信息
export async function apiProductStock(data) {
return request.get('/api/merchants/products/sku/getStockInfo', {
prefix: goodsApi,
params: data,
});
}
/**
* 获取竞价详情
* yApi: http://yapi.quantgroups.com/project/389/interface/api/67169
* * */
export async function apiProductBiddingInfo(params) {
return request.get('/api/merchants/products/bidding/detail', {
prefix: goodsApi,
params,
});
}
/**
* 供应商更新商品供货价库存
* yApi: http://yapi.quantgroups.com/project/389/interface/api/67139
* * */
export async function apiProductBiddingUpdate(params) {
return request.post('/api/merchants/products/bidding/edit', {
prefix: goodsApi,
data: params,
duration: null,
notTip: true,
});
}
/**
* 可竞价sku列表
* yApi: http://yapi.quantgroups.com/project/389/interface/api/67164
* * */
export async function apiBiddingList(params) {
return request.post('/api/merchants/products/bidding/page', {
prefix: goodsApi,
data: stringify(params),
headers,
});
}
/**
* 导入竞价商品信息
* yApi: http://yapi.quantgroups.com/project/389/interface/api/45896
* * */
export async function apiUploadGoodsFile(file) {
const params = new FormData();
params.append('file', file);
params.append('type', 6);
const data = await request.post('/api/merchants/importFile/excel', {
data: params,
prefix: goodsApi,
});
return data;
}
/**
* 下载竞价商品模版
* yApi: http://yapi.quantgroups.com/project/389/interface/api/67269
* * */
export async function apiDownBiddingTemplate(params) {
const productCategoryId = params?.productCategoryId || [];
params.productCategoryId =
(productCategoryId.length && productCategoryId[productCategoryId.length - 1]) || '';
const res = await request.post('/api/merchants/products/bidding-template/export', {
data: stringify(_.omitBy(params, v => v === undefined && v === null && v === '')),
headers,
prefix: goodsApi,
responseType: 'arrayBuffer',
});
const blob = new Blob([res]);
saveAs(blob, `自营商品供货价更新表-${format(new Date(), 'yyyy-MM-dd')}.xlsx`);
}
import React from 'react';
import { Button, Badge, Switch, Modal, message } from 'antd';
import { SortableHandle } from 'react-sortable-hoc';
import { ExclamationCircleOutlined, MenuOutlined } from '@ant-design/icons';
import styles from './style.less';
import { resetTime } from '../../utils/utils';
import { apiChangeStateGoods, apiGoodsActionBatch } from './service';
const { confirm } = Modal;
export const NormalProduct = 1;
export const ServiceProduct = 4;
export const TakeawayProduct = 5;
export const GoodTypes = {
[NormalProduct]: '实体商品',
[ServiceProduct]: '服务类商品',
[TakeawayProduct]: '外卖商品',
};
export const productType = [
{
value: 1,
title: '自营',
},
{
value: 2,
title: '京东开普勒',
},
{
value: 3,
title: '京东联盟',
},
{
value: 4,
title: '众联',
},
{
value: 5,
title: '企业购',
},
{
value: 6,
title: '企业购直连',
},
];
export function column() {
const onChangeState = async ({ skuId, state }) => {
confirm({
icon: <ExclamationCircleOutlined />,
content: `确认${+state === 6 ? '下架' : '上架'}商品?`,
onOk: async () => {
const res = await apiChangeStateGoods({
ids: skuId,
productState: +state === 6 ? 7 : 6, // 6:上架,7:下架
});
if (res.businessCode === '0000' && res.code === '0000') {
this.handleSearch();
}
},
});
};
const onShowAudit = row => {
this.setState({
auditRow: row,
visibleAuditModal: true,
});
};
return [
{
title: 'SKU编码',
dataIndex: 'skuId',
width: 125,
align: 'center',
render: (_, row) => {
if (row.type === 2) {
return (
<Badge
count={
<div
style={{
color: '#fff',
borderRadius: '3px',
background: '#f5222d',
padding: '2px',
fontSize: '10px',
opacity: 0.7,
}}
>
虚拟
</div>
}
>
<div
style={{
background: '#fbfbfb',
borderRadius: '3px',
padding: '2px',
}}
>
{row.skuId}
</div>
</Badge>
);
}
return (
<div
style={{
background: '#fbfbfb',
borderRadius: '3px',
padding: '2px',
}}
>
{row.skuId}
</div>
);
},
},
{
title: 'SKU商品名称',
width: 135,
align: 'center',
dataIndex: 'skuName',
},
{
title: '供应商价格',
dataIndex: 'marketPrice',
width: 150,
align: 'center',
sorter: (a, b) => a.supplyPrice - b.supplyPrice,
render: (_, row) => (
<div className={styles.price}>
{/* <p>供货价:{(row.supplyPrice || 0).toFixed(2)}</p> */}
<p>市场价:{(row.marketPrice || 0).toFixed(2)}</p>
</div>
),
},
{
title: '库存',
width: 120,
dataIndex: 'stock',
align: 'center',
sorter: (a, b) => a.stock - b.stock,
render: (_, row) => {
// const stockView = row.productStock;
const stockView = (
<Button type="link" onClick={() => this.onShowStockModal(row)} style={{ padding: 0 }}>
{row.productStock}
</Button>
);
return (
<>
<p>当前库存:{stockView}</p>
<p>可售库存:{_}</p>
{row.type === 1 && row.productStockWarning > 0 && (
<p>预警值:{row.productStockWarning}</p>
)}
</>
);
},
},
{
title: '不支持配送区域',
dataIndex: 'areaTemplateName',
key: 'areaTemplateName',
width: 200,
align: 'center',
},
{
title: '上下架状态',
dataIndex: 'stateDesc', // 5:未上架 ,6 :上架,7:下架
width: 200,
align: 'center',
render: (_, row) => (
<div>
{row.type === 4 && row.state >= 5 ? (
<>
<Switch
checkedChildren="已上架"
checked={row.state === 6}
unCheckedChildren="已下架"
onClick={() => onChangeState(row)}
defaultChecked
/>
</>
) : (
'-'
)}
</div>
),
},
{
title: '审核状态',
dataIndex: 'stateDesc',
width: 200,
align: 'center',
render: (_, row) => (
<div>
<p>{row.state >= 5 ? '审核通过' : _}</p>
<div>
{row.updateState ? (
<Button onClick={() => onShowAudit(row)} type="link">
{row.updateStateDesc}
</Button>
) : (
'--'
)}
</div>
</div>
),
},
{
title: '操作',
dataIndex: 'action',
width: 120,
align: 'center',
render: (_, row) => (
<div className={styles.actionBtn}>
{this.canEditable && (row.state === 4 || (row.state >= 5 && row.updateState !== 1)) && (
<Button
key="edit"
type="primary"
size="small"
className={styles.button}
onClick={() => {
this.serviceVisbleChange(row);
}}
>
修改
</Button>
)}
<Button
key="viewP"
type="primary"
size="small"
className={styles.button}
onClick={() => this.audit(row.skuId)}
>
预览
</Button>
<Button
key="log"
size="small"
type="primary"
className={styles.button}
onClick={() => this.viewLog(row)}
>
查看日志
</Button>
</div>
),
},
];
}
export function takeawayColumn(actions) {
const onChangeState = async ({ skuId, state }) => {
confirm({
icon: <ExclamationCircleOutlined />,
content: `确认${+state === 6 ? '下架' : '上架'}商品?`,
onOk: async () => {
const res = await apiGoodsActionBatch({
skuIds: [skuId],
shopId: actions.shopId,
productStatus: +state === 6 ? 0 : 1, // 6:上架,7:下架
type: 2,
});
if (res.businessCode === '0000' && res.code === '0000') {
actions.getDataList();
message.success('处理成功!');
}
},
});
};
const DragHandle = SortableHandle(() => (
<MenuOutlined style={{ cursor: 'grab', color: '#999' }} />
));
return [
{
title: '排序',
dataIndex: 'sort',
align: 'center',
width: 70,
className: [styles['drag-visible']],
render: () => <DragHandle />,
},
{
title: 'SKU编码',
dataIndex: 'skuId',
width: 180,
align: 'center',
},
{
title: 'SKU商品名称',
// width: 200,
align: 'center',
dataIndex: 'skuName',
},
{
title: '售卖价格(元)',
dataIndex: 'salePrice',
width: 120,
align: 'center',
},
{
title: '库存',
width: 100,
dataIndex: 'stock',
align: 'center',
},
{
title: '上下架状态',
dataIndex: 'stateDesc', // 5:未上架 ,6 :上架,7:下架
width: 110,
align: 'center',
},
{
title: '操作',
dataIndex: 'action',
width: 390,
align: 'center',
render: (_, row, index) => (
<div className={styles.actionBtn}>
{(row.state === 4 || (row.state >= 5 && row.updateState !== 1)) && (
<Button
key="edit"
type="primary"
className={styles.button}
onClick={() => actions.onEdit(row)}
>
编辑
</Button>
)}
<Button
key="viewP"
type="primary"
className={styles.button}
onClick={() => onChangeState(row)}
>
{+row.state === 6 ? '下架' : '上架'}
</Button>
<Button
key="log"
type="primary"
className={styles.button}
onClick={() => actions.onShowStockModal(row)}
>
修改库存
</Button>
{(index > 0 || actions.pageNo > 1) && (
<Button key="top" className={styles.button} onClick={() => actions.toTop(row)}>
置顶
</Button>
)}
</div>
),
},
];
}
// 外卖商品批量操作
export const batchAction = event => [
{
key: 'up',
type: '2',
label: (
<a type="text" onClick={() => event.onChangeState('up')}>
上架
</a>
),
},
{
key: 'down',
type: '2',
label: (
<a type="text" onClick={() => event.onChangeState('down')}>
下架
</a>
),
},
{
key: 'stock',
type: '7',
label: (
<a type="text" onClick={() => event.onChangeState('stock')}>
修改库存
</a>
),
},
{
key: 'time',
type: '4',
label: (
<a type="text" onClick={() => event.onChangeState('time')}>
修改可售时间
</a>
),
},
// {
// key: 'group',
// type: '3',
// label: (
// <a type="text" onClick={() => event.onChangeState('group')}>
// 修改分组
// </a>
// ),
// },
{
key: 'send',
type: '6',
label: (
<a type="text" onClick={() => event.onChangeState('send')}>
设置单点不送
</a>
),
},
{
key: 'buy',
type: '5',
label: (
<a type="text" onClick={() => event.onChangeState('buy')}>
修改最少购买数量
</a>
),
},
];
// 外卖商品可售星期
export const saleWeeks = [
{ label: '周一', value: 1 },
{ label: '周二', value: 2 },
{ label: '周三', value: 3 },
{ label: '周四', value: 4 },
{ label: '周五', value: 5 },
{ label: '周六', value: 6 },
{ label: '周日', value: 7 },
];
export const disSelectStatus = [2, 5];
export const stateList = [
{ value: 3, label: '待审核' },
{ value: 4, label: '驳回' },
{ value: 5, label: '审核通过' },
];
// AUDITING(3, "待审核"),REJECTED(4, "驳回"),WAIT_SELL(5, "未上架"),ON_SELL(6, "已上架"),OFF_SHELVES(7, "已下架"))
export const productTypeList = [
{ value: 1, label: '实体商品' },
{ value: 2, label: '虚拟充值' },
{ value: 3, label: '虚拟卡券', disabled: true },
];
export const JDSHOPID = [3, 5, 6];
/** ********************************************************************************* */
/** ********************************************************************************* */
/** ********************************************************************************* */
// 编辑回显示时只获取用到的数据
const filterItem = skuItem => {
const { serviceItem, ...argsItem } = skuItem;
argsItem.disabled = true;
return argsItem;
};
const filterSpecData = skuList =>
skuList.reduce(
(orgin, skuItem) => {
const item = filterItem(skuItem);
const { firstSpecValue, secondSpecValue, id } = item;
if (firstSpecValue && !orgin.firstDuplicate.includes(firstSpecValue)) {
orgin.firstSpecValue.push(item);
orgin.firstDuplicate.push(firstSpecValue);
}
if (secondSpecValue && !orgin.secondDuplicate.includes(secondSpecValue)) {
orgin.secondSpecValue.push(item);
orgin.secondDuplicate.push(secondSpecValue);
}
// origin.skuItemResultList.push(item);
return orgin;
},
{
firstSpecValue: [],
secondSpecValue: [],
firstDuplicate: [],
secondDuplicate: [],
skuItemResultList: [],
},
);
const filterCarouseList = (carouseList = []) =>
carouseList.reduce((origin, itemImg) => {
if (itemImg.skuSpecImageList.length) {
origin[itemImg.specValue || 'NOT'] = itemImg.skuSpecImageList || [];
}
return origin;
}, {});
const filterServiceItem = (type, serviceItem) => {
if (type !== 4) {
return {};
}
console.log(serviceItem);
return {
serviceItem: {
useTime: [resetTime(serviceItem.useStartTime), resetTime(serviceItem.useEndTime)],
purchaseTime: [
resetTime(serviceItem.purchaseStartTime),
resetTime(serviceItem.purchaseEndTime),
], // 购买开始时间
validityPeriodType: serviceItem.validityPeriodType,
validityPeriodDays: serviceItem.validityPeriodDays,
shopIds: serviceItem.shopIds || [], // 适用门店列表
unavailableDate: serviceItem.unavailableDate, // 不可用日期
useTimeDescription: serviceItem.useTimeDescription, // 使用时间
useMethod: serviceItem.useMethod, // 使用方法
ruleDescription: serviceItem.ruleDescription, // 规则说明
applyScope: serviceItem.applyScope, // 适用范围
tips: serviceItem.tips, // 温馨提示
},
settlementItem: {
settlementMethod: 1,
limitPurchase: Boolean(serviceItem.limitPurchase), // 是否限购1:限购/0:不限购
limitPurchaseType: serviceItem.limitPurchaseType, // 限购类型,如果限购必填1:长期限购/2:周期限购
limitPurchaseCycle: serviceItem.limitPurchaseCycle, // 限购周期1:每天/7:7天/30:30天
limitPurchaseQuantity: serviceItem.limitPurchaseQuantity, // 限购数量
packageContent: serviceItem.packageContent,
appointment: serviceItem.appointment, // 预约
receptionVolume: serviceItem.receptionVolume, // 接待数量
},
};
};
export const ProcessEditData = (data, row) => {
const [oneItem = {}] = data.skuList;
const serviceItem = oneItem.serviceItem || {};
const orginSpecItem = filterSpecData(data.skuList);
const imageList = filterCarouseList(data.carouseList);
const servesItemParams = filterServiceItem(data.productType, serviceItem);
const specsParams = data.specs.reduce((origin, item) => {
origin[item.specId] = item.specValues.map(child => child.value);
return origin;
}, {});
let commonImageList = data.commonImageList || [];
let cardImageList = [];
if (data.productType === 4) {
const [firstImg, ...towAfterImgList] = oneItem.imageList || [];
cardImageList = towAfterImgList || [];
commonImageList = [firstImg];
}
const SourceData = {
state: row.state,
id: data.id,
productType: data.productType,
pageProductType: row.productType,
productAttributeApplyList: data.productAttributeApplyList,
infoMation: {
...specsParams,
brandId: data.brandId,
supplierId: data.supplierId,
character: data.character,
name: data.name,
categoryId: [data.firstCategoryId, data.secondCategoryId, data.thirdCategoryId],
description: serviceItem.description || null,
afterAddressId: data.afterAddressId || null,
},
infoSpecData: {
firstSpec: oneItem.firstSpec,
firstSpecId: oneItem.firstSpecId,
firstSpecValue: orginSpecItem.firstSpecValue,
secondSpec: oneItem.secondSpec,
secondSpecId: oneItem.secondSpecId,
secondSpecValue: orginSpecItem.secondSpecValue,
},
infoImageData: {
imageList,
cardImageList,
commonImageList,
detailImageList: data.detailImageList,
},
skuList: data.skuList,
...servesItemParams,
};
console.log('SourceData===============>', SourceData);
return SourceData;
};
/** ********************************************************************************* */
/** ********************************************************************************* */
/** ********************************************************************************* */
.button {
margin: 5px;
}
.selectWidth {
width: 200px;
}
.btngroup {
margin: 10px;
}
.filterModal {
margin: 20px 0;
}
.footerButton {
position: absolute;
bottom: 20px;
left: 0;
}
.ptop {
margin-top: 5px;
}
.tabletop {
margin-top: 20px;
}
.logBtn {
display: inherit;
margin: 20px auto;
}
.linkInput {
width: 310px !important;
}
.picBtn {
margin-top: 5px;
margin-left: 10px;
}
.pullBtn {
position: absolute;
top: 30px;
left: 800px;
}
.price {
// text-align: left;
cursor: pointer;
}
.searchForm {
:global {
.ant-form-item-label {
line-height: 32px;
}
.ant-form-item {
margin-bottom: 12px;
}
}
.button {
margin: 1px 5px;
}
}
.queryBtn {
margin-left: 45px;
}
.actionBtn {
button {
min-width: 75px;
}
}
.pagination {
margin-top: 10px;
}
.imgBorder {
margin: 5px 0;
padding-left: 20px;
background: #fff;
border: 1px solid #efefef;
border-radius: 10px;
}
.state {
font-size: 13px;
}
.card {
margin-top: 15px;
margin-bottom: 15px;
}
.modal {
background: #ddd;
}
.warning {
margin-top: -20px;
margin-bottom: 20px;
color: red;
}
.iptNumRight {
margin-right: 0 !important;
}
.sizeTitle {
font-size: 12px;
}
.stockTip {
color: #d9363e;
line-height: 1;
}
.stockErrMsg {
box-sizing: border-box;
padding-left: 30%;
color: #d9363e;
line-height: 1;
}
.cardTitle {
padding: 15px;
font-weight: bold;
font-size: 18px;
}
.stateAuditTxt {
color: #1890ff;
cursor: pointer;
}
.attrbox {
max-height: 384px;
overflow: hidden;
}
.attrboxMore {
max-height: max-content;
}
.draftName {
text-align: left;
word-break: break-all;
}
.takeawayBox {
margin-top: 20px;
padding-bottom: 20px;
background-color: #fff;
&--page {
padding-top: 10px;
padding-left: 30px;
text-align: left;
}
}
.groupBox {
padding: 0 24px 15px 24px;
&-title {
display: flex;
align-items: center;
padding: 10px 0;
font-size: 18px;
&--name {
margin-right: 15px;
}
}
&-body {
padding: 5px 0;
&--tag {
position: relative;
box-sizing: border-box;
height: 34px;
margin-right: 20px;
padding: 0 15px 0 10px;
font-size: 14px;
line-height: 32px;
&__move {
cursor: move;
}
&__edit {
margin-left: 5px;
color: #1890ff;
cursor: pointer;
}
&__close {
position: absolute;
top: 0;
right: 0;
color: #1890ff;
font-size: 16px;
transform: translate(50%, -50%);
cursor: pointer;
}
&__text {
user-select: none;
}
}
&--tag-normal {
position: relative;
height: 34px;
margin-right: 0;
padding: 0 20px;
font-size: 14px;
line-height: 32px;
cursor: pointer;
}
&--tag-input {
width: 80px;
height: 26px;
margin-right: 20px;
}
&--tag__cur {
color: #fff;
background-color: #1890ff;
border: 1px solid #1890ff;
}
&--new {
height: 34px;
margin-right: 0 !important;
margin-left: 10px;
padding: 0 15px;
line-height: 32px;
cursor: pointer;
}
&--dragbox {
padding: 0;
}
}
}
.row-dragging {
background: #fafafa;
border: 1px solid #ccc;
& td {
padding: 16px;
}
}
.drag-visible {
visibility: visible;
}
.td-center {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 0 10px;
word-break: break-all;
}
// .virtual-table {
// table-layout: auto !important;
// table {
// table-layout: auto !important;
// }
// }
.action-bar-box {
padding: 0 0 15px 24px;
&--down {
margin-left: 10px;
}
}
.week-time-box {
&--icon {
position: relative;
top: 4px;
margin: 0 8px;
color: #999;
font-size: 24px;
cursor: pointer;
transition: all 0.3s;
:hover {
box-shadow: 0 0 4px #ccc;
}
}
}
.primary {
color: #1890ff;
}
.error {
color: #ff4d4f;
}
.stock-box {
position: relative;
&--btns {
position: absolute;
top: 0;
right: 0;
height: 32px;
padding-right: 8%;
line-height: 32px;
}
&--red {
color: #ff1616;
}
&--inputnum {
width: 100%;
}
}
.inputWdith {
width: 100%;
}
......@@ -40,14 +40,14 @@ const TableList = props => {
hideInSearch: true,
align: 'center',
},
{
title: '操作人',
dataIndex: 'userName',
key: 'userName',
hideInSearch: true,
width: 120,
align: 'center',
},
// {
// title: '操作人',
// dataIndex: 'userName',
// key: 'userName',
// hideInSearch: true,
// width: 120,
// align: 'center',
// },
{
title: '发货单数',
dataIndex: 'totalNum',
......
......@@ -10,7 +10,7 @@ export async function queryToSend(params) {
try {
const {
data: { current, records, total, size },
} = await request.post('/api/merchants/orders/list', {
} = await request.post('/api/merchants/pops/orders/list', {
prefix: config.kdspApi,
data: stringify(_.omitBy(params, v => !v)),
headers: {
......@@ -31,7 +31,7 @@ export async function queryToSend(params) {
// 快递公司
export async function queryExpress() {
try {
const { data } = await request.get('/api/merchants/companies/list', {
const { data } = await request.get('/api/merchants/pops/companies/list', {
prefix: config.kdspApi,
});
return data;
......@@ -41,7 +41,7 @@ export async function queryExpress() {
}
export async function getGoods(orderId) {
const { data } = await request.get(`/api/merchants/orders/skus/list?orderId=${orderId}`, {
const { data } = await request.get(`/api/merchants/pops/orders/skus/list?orderId=${orderId}`, {
prefix: config.kdspApi,
});
return data;
......@@ -49,7 +49,7 @@ export async function getGoods(orderId) {
export async function uploadFile(file) {
const params = new FormData();
params.append('file', file);
const data = await request.post('/api/merchants/orders/deliveries/batches/import', {
const data = await request.post('/api/merchants/pops/orders/deliveries/batches/import', {
data: params,
prefix: config.kdspApi,
});
......@@ -59,7 +59,7 @@ 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', {
const data = await request.post('/api/merchants/pops/orders/export', {
data: stringify(_.omitBy(params, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
......@@ -82,7 +82,7 @@ export async function queryToBatchSend(params) {
};
const {
data: { current, records, total, size },
} = await request.get('/api/merchants/deliveries/batchlist', {
} = await request.get('/api/merchants/pops/deliveries/batchlist', {
prefix: config.kdspApi,
params: _.omitBy(transformedParam, v => !v),
headers: {
......@@ -97,7 +97,7 @@ export async function queryToBatchSend(params) {
};
}
export async function downUploadeOrder(params) {
const data = await request.get('/api/merchants/deliveries/batchexport', {
const data = await request.get('/api/merchants/pops/deliveries/batchexport', {
params,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
......@@ -115,7 +115,7 @@ export async function downUploadeOrder(params) {
}
// 延迟发货
export function apiDelayDeliverGoods(data) {
return request.post('/api/merchants/orders/logs/add', {
return request.post('/api/merchants/pops/orders/logs/add', {
data,
prefix: config.kdspApi,
});
......@@ -128,7 +128,7 @@ export function apiDelayDeliverGoods(data) {
* @see http://yapi.quantgroups.com/project/389/interface/api/45840
*/
export function apiQueryOrderInfo(params) {
return request.get('/api/merchants/orders/deliveries/packages/detail', {
return request.get('/api/merchants/pops/orders/deliveries/packages/detail', {
params,
prefix: config.kdspApi,
});
......@@ -140,14 +140,14 @@ export function apiQueryOrderInfo(params) {
* @see http://yapi.quantgroups.com/project/389/interface/api/45816
*/
export function apiDeliveriesAdd(data) {
return request.post('/api/merchants/orders/deliveries/add', {
return request.post('/api/merchants/pops/orders/deliveries/add', {
data,
prefix: config.kdspApi,
});
}
export function apiDeliveriesEdit(data) {
return request.post('/api/merchants/orders/deliveries/edit', {
return request.post('/api/merchants/pops/orders/deliveries/edit', {
data,
prefix: config.kdspApi,
});
......@@ -159,7 +159,7 @@ export function apiDeliveriesEdit(data) {
*/
export function apiDeliveriesTraceList(data) {
return request.post('/api/merchants/deliveries/traces/list', {
return request.post('/api/merchants/pops/deliveries/traces/list', {
data: stringify(data),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
......
/* eslint-disable no-confusing-arrow */
export const formItemLayout = {
labelCol: {
sm: { span: 3 },
......@@ -158,8 +159,11 @@ export const WeeksList = [
value: 7,
},
];
export const StaticColumns = customer => [
// isGoodsManage用来判断是否是popGoodsManage页面,这个页面是用来应付审计团队的,没有实际功能,这里删掉了供货价字段,正常业务逻辑不用考虑这个字段,这就用第一个数据就好了
const isGoodsManage = window.location.pathname.indexOf('popGoodsManage') < 0;
export const StaticColumns = customer =>
isGoodsManage
? [
{
title: '供货价',
dataIndex: 'supplyPrice',
......@@ -322,7 +326,156 @@ export const StaticColumns = customer => [
roleRules: { required: false },
disabeldRender: () => customer.isDisabled,
},
];
]
: [
// 这部分是用来应付审计团队的,没有实际功能,这里删掉了供货价字段,正常业务逻辑不用考虑这里
{
title: '佣金费率',
dataIndex: 'commissionRate',
editable: true,
role: [4],
roleRules: { required: false },
roleProps: {
min: 0,
},
// batchProps: {
// min: 0,
// },
},
{
title: '市场价',
dataIndex: 'marketPrice',
editable: true,
batchRole: [1, 2, 3, 4],
roleProps: {
precision: 2,
min: 0,
},
batchProps: {
precision: 2,
min: 0,
},
roleRules: { required: true },
disabeldRender: () => customer.isDisabled,
},
{
title: '销售价',
dataIndex: 'salePrice',
editable: true,
batchRole: [4],
role: [4],
roleRules: { required: true },
roleProps: {
precision: 2,
min: 0,
},
batchProps: {
precision: 2,
min: 0,
},
disabeldRender: () => customer.isDisabled,
},
{
title: `重量(${customer.isGold ? 'g' : 'kg'})`,
dataIndex: 'weight',
editable: true,
batchRole: [1],
batchProps: {
min: 0,
precision: customer.isGold ? 2 : 3,
max: customer.isGold ? 100 : 999999.999,
},
role: [1],
roleRules: { required: true },
roleProps: {
min: 0,
precision: customer.isGold ? 2 : 3,
max: customer.isGold ? 100 : 999999.999,
},
disabeldRender: () => customer.isDisabled,
},
{
title: '库存',
dataIndex: 'productStock',
editable: true,
role: [1, 2, 4],
batchRole: [1, 2, 4],
batchProps: {
precision: 0,
step: 1,
min: 0,
},
roleProps: {
min: 0,
step: 1,
precision: 0,
},
roleRules: { required: true },
disabeldRender: record => {
if (record.stock === null) return false;
return customer.isEdit && customer.isNormal;
},
},
{
title: '库存预警',
dataIndex: 'productStockWarning',
editable: true,
batchRole: [1],
role: [1, 4],
roleProps: {
min: 0,
precision: 0,
maxLength: 5,
},
batchProps: {
min: 0,
precision: 0,
maxLength: 5,
},
disabeldRender: () => customer.isDisabled,
},
{
title: '商品自编码',
dataIndex: 'thirdSkuNo',
editable: true,
role: [1, 2],
inputType: 'input',
roleRules: { required: true },
disabeldRender: () => customer.isDisabled,
},
{
title: '京东链接',
dataIndex: 'skuLink',
editable: true,
role: [1, 2],
inputType: 'input',
roleRules: { required: false },
disabeldRender: () => customer.isDisabled,
},
{
title: 'sku名称',
dataIndex: 'name',
editable: true,
role: customer.isEdit && customer.isJDGoods ? [1, 2] : [],
inputType: 'btnText',
roleRules: { required: false },
disabeldRender: () => customer.isDisabled,
},
{
title: '操作',
editable: true,
dataIndex: 'option',
role: [1, 2],
inputType: 'option',
roleProps: {
isJDGoods: customer.isJDGoods,
disabled: customer.isDisabled,
min: 0,
},
roleRules: { required: false },
disabeldRender: () => customer.isDisabled,
},
];
export const ENUM_REPERTORY = [{ label: '单规格', value: '1' }, { label: '多规格', value: '2' }];
export const ENUM_SET_REPERTORY = [{ label: '清零', value: '0' }, { label: '最大', value: '1' }];
export const ENUM_WEEK = [
......
......@@ -25,6 +25,7 @@ import {
apiEditDraft,
apiGetShopDetail,
} from './service';
import { useLocation } from 'react-router-dom';
import { isUrl, filterSendData, clearCurrent, onAutoSaveValue, localAutoSaveKey } from './utils';
import { ServiceContext } from './context';
import { GOOD_MANAGE } from '@/../config/permission.config';
......@@ -85,6 +86,7 @@ const ServiceGoods = options => {
picturesRef,
takeawayRef,
]);
const location = useLocation();
const [specKeyList, setSpecKeyList] = useState([]); // 记录一级规格key字段
......@@ -505,6 +507,7 @@ const ServiceGoods = options => {
},
});
};
const isPopGoods = location.pathname.indexOf('popGoodsManage') > 0; // pop商品管理-商品库(应付审计用的, 驳回和修改状态下不能编辑)
const providerValue = {
pageId,
isEdit,
......@@ -516,9 +519,10 @@ const ServiceGoods = options => {
isTakeawayService: productType === 5,
isGold: categoryIds.includes(GoldCategory), // 投资金 重量显示克
// 0, "商品删除" 1, "新建" 2, "提交审核" 3, "待审核" 4, "驳回" 5, "未上架" 6, "已上架" 7, "已下架"
isNormal: SourceData.state && SourceData.state !== 4, // 商品不是驳回状态
isNormal: (SourceData.state && SourceData.state !== 4) || isPopGoods, // 商品不是驳回状态
// 当商品进行编辑 & 类型不为电子卡券 & 商品状态不为驳回 禁用当前功能
isDisabled: isEdit && productType !== 4 && SourceData.state && SourceData.state !== 4,
isDisabled:
(isEdit && productType !== 4 && SourceData.state && SourceData.state !== 4) || isPopGoods,
isJDGoods: isEdit && SourceData.pageProductType && +SourceData.pageProductType !== 1,
isUseCache, // 是否使用缓存数据
onEventBus,
......
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