Commit 27cfda56 authored by 杨鑫's avatar 杨鑫

Merge branch 'feature/20230327_public_takeaway' into 'master'

Feature/20230327 public takeaway

See merge request !90
parents 2c2c28c5 d6a3e203
......@@ -15,5 +15,6 @@ module.exports = {
'import/no-unresolved': 0,
'import/extensions': 0,
'no-unused-expressions': ['error', { allowShortCircuit: true }],
'template-curly-spacing': 'off',
},
};
......@@ -62,7 +62,8 @@ export default {
targets: {
ie: 11,
},
devtool: process.env.SENTRY_ENV === 'test' ? false : 'hidden-source-map',
// devtool: process.env.SENTRY_ENV === 'test' ? false : 'hidden-source-map',
devtool: false,
// umi routes: https://umijs.org/zh/guide/router.html
routes: [
{
......@@ -273,6 +274,18 @@ export default {
name: 'systemManageLog',
component: './systemManage/Log',
},
{
title: '商户管理后台-合同查看',
path: '/contractView',
name: 'contractView',
component: './contractView',
},
{
title: '商户管理后台-商家资料',
path: '/businessInfo',
name: 'businessInfo',
component: './businessManage/info',
},
{
component: './404',
},
......
const isProduction = process.env.NODE_ENV === 'production';
const isPre = process.env.PRE_ENV === 'pre';
const environment = 'xyqb';
const environment = 'yxm2';
const envAPi = {
api: `https://security-${environment}.liangkebang.net`, //'https://security-xyqb.liangkebang.net',
kdspOpApi: `https://sc-merchant-api-${environment}.liangkebang.net`,
......
......@@ -5,6 +5,7 @@ export const GOOD_MANAGE = {
EDITABLE: '020102', // 新增/修改
ADD_SERVICE_GOODS: '020103', // 新增服务商品
ADD_NORMAL_GOODS: '020104', // 新增实物商品
ADD_TAKEAWAY_GOODS: '020105', // 新增外卖商品
};
// 配送区域
......
......@@ -6960,6 +6960,23 @@
}
}
},
"antd-img-crop": {
"version": "4.12.2",
"resolved": "http://npmprivate.quantgroups.com/antd-img-crop/-/antd-img-crop-4.12.2.tgz",
"integrity": "sha512-xgxR3x2sg+tCBHMfx1gejEfhhvnIL2mpZ4OIPfQDJZTTfm9YpMqaqLF9qWbF9Nf83bXCdaQywYihcVGyw3niDg==",
"requires": {
"compare-versions": "6.0.0-rc.1",
"react-easy-crop": "^4.7.4",
"tslib": "^2.5.0"
},
"dependencies": {
"tslib": {
"version": "2.5.0",
"resolved": "http://npmprivate.quantgroups.com/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
}
}
},
"antd-mobile": {
"version": "2.3.4",
"resolved": "http://npmprivate.quantgroups.com/antd-mobile/-/antd-mobile-2.3.4.tgz",
......@@ -7170,6 +7187,11 @@
"integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
"dev": true
},
"array-move": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/array-move/-/array-move-4.0.0.tgz",
"integrity": "sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ=="
},
"array-reduce": {
"version": "0.0.0",
"resolved": "http://npmprivate.quantgroups.com/array-reduce/-/array-reduce-0.0.0.tgz",
......@@ -8730,6 +8752,11 @@
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
"dev": true
},
"compare-versions": {
"version": "6.0.0-rc.1",
"resolved": "http://npmprivate.quantgroups.com/compare-versions/-/compare-versions-6.0.0-rc.1.tgz",
"integrity": "sha512-cFhkjbGY1jLFWIV7KegECbfuyYPxSGvgGkdkfM+ibboQDoPwg2FRHm5BSNTOApiauRBzJIQH7qvOJs2sW5ueKQ=="
},
"component-classes": {
"version": "1.2.6",
"resolved": "http://npmprivate.quantgroups.com/component-classes/-/component-classes-1.2.6.tgz",
......@@ -18381,6 +18408,11 @@
"object-visit": "^1.0.0"
}
},
"mapvgl": {
"version": "1.0.0-beta.175",
"resolved": "https://registry.npmjs.org/mapvgl/-/mapvgl-1.0.0-beta.175.tgz",
"integrity": "sha512-BaUPH4EAhgIjcTwqoOEBB6Vk93K7itLdaES7Qz8RnrwJeGBzoQKypRSZtszeueuFjRW/M5GntjxCC8vm5PD+OA=="
},
"markdown-escapes": {
"version": "1.0.4",
"resolved": "http://npmprivate.quantgroups.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz",
......@@ -19334,6 +19366,11 @@
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
"dev": true
},
"normalize-wheel": {
"version": "1.0.1",
"resolved": "http://npmprivate.quantgroups.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz",
"integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU="
},
"normalize.css": {
"version": "7.0.0",
"resolved": "http://npmprivate.quantgroups.com/normalize.css/-/normalize.css-7.0.0.tgz",
......@@ -20916,6 +20953,11 @@
}
}
},
"pubsub-js": {
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.4.tgz",
"integrity": "sha512-hJYpaDvPH4w8ZX/0Fdf9ma1AwRgU353GfbaVfPjfJQf1KxZ2iHaHl3fAUw1qlJIR5dr4F3RzjGaWohYUEyoh7A=="
},
"pump": {
"version": "3.0.0",
"resolved": "http://npmprivate.quantgroups.com/pump/-/pump-3.0.0.tgz",
......@@ -22115,6 +22157,15 @@
"resolved": "http://npmprivate.quantgroups.com/react-amap/-/react-amap-1.2.8.tgz",
"integrity": "sha512-uHPEUXti+CcwFyCeqGGqR0ACnXJA9D8S/lQYal9AG3XEOrwkaOFbWUavrvXxjcfAclROIWg8uKxzlRpMQnkHFg=="
},
"react-bmapgl": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/react-bmapgl/-/react-bmapgl-0.2.17.tgz",
"integrity": "sha512-pcpPOaUeHY3eMmP3Wfz3qdkcrkmtBcbLy8Ih14exYwCe0XJ3IZpmkkarnMvayLymzhH2iyXAyZv42tGnkmv2LA==",
"requires": {
"mapvgl": "^1.0.0-beta.174",
"shallowequal": "^1.1.0"
}
},
"react-copy-to-clipboard": {
"version": "5.0.2",
"resolved": "http://npmprivate.quantgroups.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.2.tgz",
......@@ -22304,6 +22355,22 @@
"scheduler": "^0.19.1"
}
},
"react-easy-crop": {
"version": "4.7.4",
"resolved": "http://npmprivate.quantgroups.com/react-easy-crop/-/react-easy-crop-4.7.4.tgz",
"integrity": "sha512-oDi1375Jo/zuPUvo3oauxnNbfy8L4wsbmHD1KB2vT55fdgu+q8/K0w/rDWzy9jz4jfQ94Q9+3Yu366sDDFVmiA==",
"requires": {
"normalize-wheel": "^1.0.1",
"tslib": "2.0.1"
},
"dependencies": {
"tslib": {
"version": "2.0.1",
"resolved": "http://npmprivate.quantgroups.com/tslib/-/tslib-2.0.1.tgz",
"integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ=="
}
}
},
"react-error-overlay": {
"version": "5.1.6",
"resolved": "http://npmprivate.quantgroups.com/react-error-overlay/-/react-error-overlay-5.1.6.tgz",
......@@ -22479,6 +22546,16 @@
"shallowequal": "^1.0.1"
}
},
"react-sortable-hoc": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz",
"integrity": "sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==",
"requires": {
"@babel/runtime": "^7.2.0",
"invariant": "^2.2.4",
"prop-types": "^15.5.7"
}
},
"react-sortablejs": {
"version": "6.0.0",
"resolved": "http://npmprivate.quantgroups.com/react-sortablejs/-/react-sortablejs-6.0.0.tgz",
......@@ -22498,6 +22575,15 @@
"tween-functions": "^1.0.1"
}
},
"react-window": {
"version": "1.8.8",
"resolved": "http://npmprivate.quantgroups.com/react-window/-/react-window-1.8.8.tgz",
"integrity": "sha512-D4IiBeRtGXziZ1n0XklnFGu7h9gU684zepqyKzgPNzrsrk7xOCxni+TCckjg2Nr/DiaEEGVVmnhYSlT2rB47dQ==",
"requires": {
"@babel/runtime": "^7.0.0",
"memoize-one": ">=3.1.1 <6"
}
},
"read-pkg": {
"version": "2.0.0",
"resolved": "http://npmprivate.quantgroups.com/read-pkg/-/read-pkg-2.0.0.tgz",
/* eslint-disable no-console */
import * as Sentry from '@sentry/react';
import localStorage from '@/utils/localStorage';
// process.env.SENTRY_ENV !== 'test' 加上测试环境不会报错
if (process.env.NODE_ENV === 'production' && process.env.SENTRY_ENV !== 'test') {
if (process.env.NODE_ENV === 'production') {
try {
Sentry.init({
dsn: 'https://b3f60c62e1234e26a5b851b9f26fba07@sentry.q-gp.com/34',
......
import React, { useState } from 'react';
import { Map, Marker, ZoomControl, CityListControl } from 'react-bmapgl';
import { Modal, Input } from 'antd';
export default props => {
const { visible, onSetPoint, onCancel, lngLat } = props;
const defaultLnglat = { lng: 116.404449, lat: 39.914889 };
if (lngLat) {
defaultLnglat.lng = lngLat.lng;
defaultLnglat.lat = lngLat.lat;
}
const [lnglatPoint, setLnglatPoint] = useState(defaultLnglat);
const [lnglatText, setLnglatText] = useState(`${defaultLnglat.lng},${defaultLnglat.lat}`);
const handleOk = () => {
onSetPoint(lnglatPoint);
onCancel(true);
};
const handleCancle = () => onCancel(true);
const onGetPoint = e => {
setLnglatPoint({
lng: e.latlng.lng,
lat: e.latlng.lat,
});
setLnglatText(`${e.latlng.lng},${e.latlng.lat}`);
};
return (
<Modal
title="门店信息"
visible={visible}
width="800px"
onOk={() => handleOk()}
onCancel={() => handleCancle()}
>
<div style={{ marginBottom: '20px' }}>
<Input value={lnglatText} placeholder="点击地图选择经纬度" />
</div>
<div style={{ width: '100%', height: '360px' }}>
<Map
center={lnglatPoint}
enableScrollWheelZoom
enableDoubleClickZoom
coordType="gcj02"
onClick={e => onGetPoint(e)}
zoom={15}
>
<Marker
position={lnglatPoint}
Icon
coordType="gcj02"
autoViewport
viewportOptions={{
zoomFactor: -12,
}}
/>
<CityListControl />
<ZoomControl />
</Map>
</div>
</Modal>
);
};
......@@ -7,6 +7,7 @@ const productType = {
2: '虚拟商品',
3: '电子卡卷',
4: '服务商品',
5: '外卖商品',
};
export const columns = ({ onDel, onEdit }) => [
......
......@@ -12,6 +12,7 @@ import {
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';
......@@ -31,6 +32,7 @@ class goodsManage extends Component {
state = {
loading: false,
productType: null,
};
componentDidMount() {
......@@ -59,6 +61,10 @@ class goodsManage extends Component {
const form = this.formRef.current;
form.resetFields();
this.props.onReset();
this.props.changeProductType(1);
this.setState({
productType: 1,
});
};
addSpu = () => {
......@@ -78,6 +84,24 @@ class goodsManage extends Component {
}
};
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({
......@@ -105,7 +129,6 @@ class goodsManage extends Component {
const { treeData, permissions } = this.props;
const selectW = { width: 250 };
const iptNumWidth = { width: 118 };
const that = this;
const canEditable = permissions[GOOD_MANAGE.EDITABLE];
const content = (
<div>
......@@ -130,46 +153,29 @@ class goodsManage extends Component {
</Button>
</div>
);
// const uploadProps = {
// name: 'file',
// async customRequest(info) {
// const result = await uploadFile(info.file);
// if (result && result.businessCode === '0000') {
// that.handleSearch();
// notification.success({
// message: '操作成功',
// });
// } else {
// notification.warning({
// message: result.msg,
// description: (
// <div>
// {result.data?.length &&
// result.data.map(item => <p>{item.skuNo + item.errSkuMessage}</p>)}
// </div>
// ),
// duration: 6,
// });
// }
// },
// accept: '.xlsx',
// showUploadList: false,
// };
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">
<Input placeholder="请输入SKU编码" allowClear style={selectW} />
<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="请选择类目"
......@@ -180,82 +186,79 @@ class goodsManage extends Component {
options={treeData}
/>
</FormItem>
<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="请输入" style={iptNumWidth} />
</FormItem>
<span>--</span>
<FormItem name="supplyPriceMax" className={styles.iptNumRight} noStyle>
<InputNumber style={iptNumWidth} placeholder="请输入" onChange={this.valueMin} />
</FormItem>
</FormItem>
<FormItem label="商品类型" name="productType">
<Select style={selectW} placeholder="请选择商品类型">
<Option value={1}>实体商品</Option>
<Option value={2}>虚拟商品</Option>
<Option value={4}>服务类商品</Option>
</Select>
</FormItem>
<FormItem name="thirdSkuNo" label="第三方SKU编码">
<Input placeholder="请输入第三方SKU编码" allowClear style={selectW} />
</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()} type="primary" className={styles.button}>
<Button onClick={() => this.onReset()} className={styles.button}>
重置
</Button>
<Button
loading={this.state.loading}
onClick={() => this.onExportGoodsInfo()}
className={styles.button}
>
导出
</Button>
</FormItem>
{canEditable ? (
<FormItem style={{ float: 'right' }}>
<Popover content={content} onVisibleChange={this.handleVisibleChange}>
<Button type="primary" className={styles.button}>
批量设置
{this.state.productType !== 5 && (
<>
<Button
loading={this.state.loading}
onClick={() => this.onExportGoodsInfo()}
type="primary"
ghost
className={styles.button}
>
导出
</Button>
</Popover>
{this.props.selectNum > 0 && <Tag color="green">已选商品 {this.props.selectNum}</Tag>}
{/* <Button
className={styles.button}
type="primary"
icon="download"
ghost
onClick={() => {
window.location.href = 'https://kdspstatic.q-gp.com/批量修改库存模板.xlsx';
}}
>
模版
</Button>
<Upload {...uploadProps}>
<Button type="primary" className={styles.button}>
批量库存修改
</Button>
</Upload> */}
</FormItem>
) : (
''
)}
{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>
);
}
......
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;
This diff is collapsed.
......@@ -89,7 +89,7 @@ export async function categoryList() {
}
/**
* 商品分类
* type 商品类型:1-实物类,2-虚拟类,4-服务类
* type 商品类型:1-实物类,2-虚拟类,4-服务类 5 外卖
* */
export async function apiCategoryListType(type) {
return request.get(`/product/category/getByProductType/${type}`, {
......@@ -289,3 +289,101 @@ export async function apiDraftList(data) {
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('/product/api/merchant/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,
});
}
import React from 'react';
import { Button, Badge, Switch, Modal } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
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, apiQueryLastAuditRecord } from './service';
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,
......@@ -244,6 +254,190 @@ export function column() {
},
];
}
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: '待审核' },
......
......@@ -54,8 +54,14 @@
.searchForm {
:global {
.ant-form-item-label {
line-height: 40px;
line-height: 32px;
}
.ant-form-item {
margin-bottom: 12px;
}
}
.button {
margin: 1px 5px;
}
}
.queryBtn {
......@@ -63,7 +69,7 @@
}
.actionBtn {
button {
width: 75px;
min-width: 75px;
}
}
.pagination {
......@@ -130,3 +136,158 @@
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%;
}
......@@ -28,7 +28,17 @@
content: '';
}
}
.week {
background: #f0f0f0;
}
.weekCon {
display: flex;
flex-flow: row nowrap;
color: #319bfe;
}
.weekText {
margin-right: 10px;
}
.prodcutContent {
display: flex;
padding-bottom: 10px;
......@@ -186,3 +196,155 @@
color: #0e75fd;
cursor: pointer;
}
.required {
:global {
.ant-form-item-label > {
::before {
display: inline-block;
margin-right: 4px;
color: #ff4d4f;
font-size: 14px;
font-family: SimSun, sans-serif;
line-height: 1;
content: '*';
}
}
.ant-form-item {
margin-bottom: 0;
}
.ant-form-item-control-input-content {
display: flex;
}
.ant-form-text {
display: flex;
align-items: center;
justify-content: center;
}
}
}
.itemInline {
:global {
.ant-form-item-control-input-content {
display: flex;
}
.ant-form-item {
margin-bottom: 0;
margin-left: 40px;
}
}
}
.itemInlineModal {
:global {
.ant-form-item-control-input-content {
display: flex;
}
.ant-form-item {
margin-bottom: 0;
}
}
}
.textStyle {
color: red;
}
.multiSpecification {
display: flex;
flex-flow: row wrap;
:global {
.ant-form-item-control {
flex-direction: none;
}
.ant-form-item-control-input-content {
flex: none;
}
}
}
.specsSingularBetween {
display: flex;
align-items: center;
justify-content: flex-start;
width: max-content;
:global {
.ant-form-item-label {
overflow: inherit;
}
.ant-col-sm-3 {
max-width: fit-content;
}
}
}
.specsBetween {
display: flex;
justify-content: flex-end;
:global {
.ant-form-item-label {
overflow: inherit;
}
.ant-col-sm-3 {
max-width: fit-content;
}
}
}
.specRepertory {
height: 20px;
margin: 0 10px;
padding: 0 8px;
color: #fff;
line-height: 20px;
text-align: center;
background-color: #319bfe;
border-radius: 8px;
}
.repertoryLimit {
height: 20px;
color: #fff;
line-height: 20px;
text-align: center;
background-color: #88c0f5;
border-radius: 3px;
}
.deal {
:global {
.ant-form-item-control-input-content {
display: flex;
align-items: center;
}
.ant-form-item {
margin-bottom: 0;
}
}
}
.conBg {
width: fit-content;
min-width: 100%;
background: #f8f8f8;
:global {
.ant-input {
margin: 10px 0;
}
.ant-form-item {
margin-bottom: 8px;
}
}
}
.nameWidth {
width: 300px;
}
.colRow {
display: flex;
flex: none;
max-width: none;
}
.rowWarp {
display: flex;
flex-direction: column;
background: #f8f8f8;
}
:global {
// .reactEasyCrop_Container {
// height: 550px !important;
// }
// .reactEasyCrop_CropArea {
// height: 100% !important;
// }
}
import React, { useState, useEffect, forwardRef, useRef, useImperativeHandle } from 'react';
import { Button, Modal, Radio, Form, InputNumber, Switch, Space, message } from 'antd';
import styles from '../common.less';
import { ENUM_SET_REPERTORY } from '../config';
import { debounce } from '@/utils/utils';
import { isCheckPriceTwoDecimal, isIntegerNotZero } from '@/utils/validator';
const AddRepertoryModal = (props, ref) => {
const [confirmLoading, setConfirmLoading] = useState(false);
const [openRepertory, setOpenRepertory] = useState(false);
const [form] = Form.useForm();
const [repertoryState, setRepertoryState] = useState('');
const [initialValues, setInitialValues] = useState({
productStock: 0,
maxStock: 0,
autoStock: false,
});
const [isRequired, setIsRequired] = useState(true);
const { modifiedInventory, intactData, repertoryModel } = props;
const { type, idx, item } = repertoryModel;
useImperativeHandle(ref, () => ({
// changeVal 就是暴露给父组件的方法
setOpenRepertory: newVal => {
setOpenRepertory(newVal);
},
}));
// 自动补全
const onChangeAutoStock = e => {
form.setFieldsValue({
autoStock: e ? 1 : 0,
});
setIsRequired(!e);
};
// 勾选库存设置
const onChangeSetRepertory = e => {
setRepertoryState(`${e.target.value}`);
if (+e.target.value === 0) {
form.setFieldsValue({
productStock: 0,
});
} else {
const { maxStock } = form.getFieldsValue(['maxStock']);
form.setFieldsValue({
productStock: maxStock,
});
}
};
// 最大库存设置
const onChangeMaxStock = e => {
// 已经勾选最大库存 自动更新剩余库存
if (+repertoryState === 1) {
form.setFieldsValue({
productStock: e,
});
}
};
const getFormValues = debounce(() => {
const values = form.getFieldsValue();
}, 400);
const handleOk = async () => {
const values = await form.validateFields();
values.autoStock = values.autoStock ? 1 : 0;
if (+values.autoStock === 1 && +values.maxStock === 0) {
message.error('最大库存不能为0');
return;
}
// 回调库存
modifiedInventory(type, idx, values);
setConfirmLoading(true);
setTimeout(() => {
setOpenRepertory(false);
setConfirmLoading(false);
}, 1000);
};
const handleCancel = () => {
setOpenRepertory(false);
};
useEffect(() => {
if (item?.serviceItem) {
const { productStock = 0, autoStock = 0, maxStock = 0 } = item?.serviceItem;
const params = {
productStock,
autoStock: +autoStock === 1,
maxStock,
};
form.setFieldsValue(params);
}
}, [openRepertory, item]);
return (
<>
{openRepertory && (
<Modal
title="修改库存"
visible={openRepertory}
onOk={handleOk}
confirmLoading={confirmLoading}
onCancel={handleCancel}
>
<Form form={form} initialValues={initialValues} onValuesChange={getFormValues}>
<Space>
<Form.Item className={styles.itemInlineModal}>
<Form.Item
name="productStock"
label="剩余库存"
style={{
display: 'flex',
marginRight: '8px',
}}
rules={[
{ required: true, message: '请填写剩余库存' },
{
validator: isIntegerNotZero,
type: 'number',
message: '请输入大于0的整数',
},
]}
>
<InputNumber
min={0}
max={999999999}
style={{ width: 200, display: 'inline-block' }}
placeholder="请输入"
/>
</Form.Item>
<Form.Item style={{ width: 200, display: 'inline-block' }}>
<Radio.Group value={repertoryState} onChange={onChangeSetRepertory}>
<Radio.Button value="0">清空</Radio.Button>
<Radio.Button value="1">最大</Radio.Button>
</Radio.Group>
</Form.Item>
</Form.Item>
</Space>
<Form.Item
name="maxStock"
label="最大库存"
rules={[
{ required: !isRequired, message: '请填写最大库存' },
{
validator: isIntegerNotZero,
type: 'number',
message: '请输入大于0的整数',
},
]}
>
<InputNumber
min={0}
max={999999999}
style={{ width: 200 }}
placeholder="请输入"
onChange={onChangeMaxStock}
/>
</Form.Item>
<Form.Item name="autoStock" label="自动补足" valuePropName="checked">
<Switch
checkedChildren="开启"
unCheckedChildren="关闭"
// defaultChecked
onChange={onChangeAutoStock}
/>
</Form.Item>
</Form>
<div className={styles.textStyle}>修改成功后,原库存将被替换,请谨慎操作!</div>
</Modal>
)}
</>
);
};
export default forwardRef(AddRepertoryModal);
import React, { useState, useEffect, forwardRef, useRef, useImperativeHandle } from 'react';
import { Button, Modal, Form, Checkbox, Row, Col, time, DatePicker } from 'antd';
import { map } from 'lodash';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import moment from 'moment';
import { ENUM_WEEK } from '../config';
import styles from '../common.less';
const format = 'HH:mm';
const { RangePicker } = DatePicker;
const AddSellTimeModal = (props, ref) => {
const [confirmLoading, setConfirmLoading] = useState(false);
const [modalText, setModalText] = useState('Content of the modal');
const [formProject] = Form.useForm();
const [open, setOpen] = useState(false);
const { saleDates, setSaleDates } = props;
useImperativeHandle(ref, () => ({
// changeVal 就是暴露给父组件的方法
setOpen: newVal => {
setOpen(newVal);
},
}));
const handleOk = () => {
formProject.validateFields().then(async values => {
console.log('valuse', values);
setConfirmLoading(true);
setSaleDates(values);
setTimeout(() => {
setOpen(false);
setConfirmLoading(false);
}, 1000);
});
};
const handleCancel = () => {
console.log('Clicked cancel button');
setOpen(false);
};
useEffect(() => {
console.log('open', open);
// formProject.setFieldsValue({ saleTimes: [['12:00', '13:00']] })
}, [open]);
const onChange = () => {};
return (
<>
{open && (
<Modal
title="售卖时段"
visible={open}
onOk={handleOk}
confirmLoading={confirmLoading}
onCancel={handleCancel}
width={1050}
>
<Form
name="sellTime"
form={formProject}
// {...formItemLayout}
// onFinish={onFinish}
initialValues={saleDates}
>
<Form.Item name="unavailableDate" label="售卖时期(可多选)">
<Checkbox.Group>
<Row>
<Col className={styles.colRow} span={8}>
{ENUM_WEEK.map((item, index) => (
<Checkbox value={item.value}>{item.label}</Checkbox>
))}
</Col>
</Row>
</Checkbox.Group>
</Form.Item>
<Form.Item label="售卖时段">
<Form.List
name="saleTimes"
initialValue={[[]]}
// rules={[
// {
// validator: async (_, times) => {
// if (!times || times.length < 2) {
// return Promise.reject(new Error('At least 2 passengers'));
// }
// },
// },
// ]}
>
{(fields, { add, remove }) => (
<>
{fields.map((field, index) => (
<Form.Item
// {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
required={false}
key={field.key}
className={styles.deal}
>
<Form.Item
className={styles.deal}
{...field}
// validateTrigger={['onChange', 'onBlur']}
rules={[
{
required: true,
message: '请输入售卖时间',
},
]}
>
<RangePicker picker="time" format={format} onChange={onChange} />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => remove(field.name)}
/>
) : null}
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
style={{ width: '60%' }}
icon={<PlusOutlined />}
>
新增时段
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
</Form>
</Modal>
)}
</>
);
};
export default forwardRef(AddSellTimeModal);
......@@ -77,7 +77,7 @@ const FormCai = forwardRef((props, ref) => {
</Col>
<Col span={2}>
<Form.Item
mame={[name, 'unit']}
name={[name, 'unit']}
rules={[{ required: true, message: '请选择单位' }]}
>
<Select defaultValue="份">
......
......@@ -215,8 +215,9 @@ const FormRuleSetting = forwardRef((props, ref) => {
>
<Input.TextArea
showCount
rows={5}
maxLength={200}
placeholder="请输入温馨提示,200字以内 例如:全场通用例如:\n不兑零"
placeholder="请输入温馨提示,200字以内&#13;&#10;例如:&#13;&#10;*不兑零,不找零&#13;&#10;*无需预约,消费高峰期可能需要等位&#13;&#10;*商家提供免费WIFI"
/>
</Form.Item>
</Form>
......
This diff is collapsed.
......@@ -5,9 +5,13 @@ import commonStyle from '../common.less';
export const TaskTypeSelect = props => {
const customer = useContext(ServiceContext);
const typeConfig = TaskList(customer.canAddService, customer.canAddNormal);
const typeConfig = TaskList(
customer.canAddService,
customer.canAddNormal,
customer.canTakeawayService,
);
const selectTabs = task => {
if (!customer.isEdit) {
if (!customer.isEdit && Object.keys(props.takeAway).length === 0) {
props.onChange(task);
}
};
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -87,11 +87,22 @@ export const supplierListQuery = () =>
// supplierId: 供应商id(优先使用token中的supplierId,这里可以传任意值,建议传0)
// state: 状态:-1-全部,1-启用,0-禁用
export const shopGetBySupplierId = (state = 1, supplierId = 0) =>
request.get(`/shop/getBySupplierId/${supplierId}/${state}`, {
prefix: goodsApi,
headers,
});
// export const shopGetBySupplierId = (state = 1, supplierId = 0) =>
// request.get(`/shop/getBySupplierId/${supplierId}/${state}`, {
// prefix: goodsApi,
// headers,
// });
export const shopGetBySupplierId = (productBusiness, state = 1) =>
request.get(
`/api/merchants/shops/getBySupplierId?${stringify({
state,
productBusiness,
})}`,
{
prefix: goodsApi,
headers,
},
);
export const getByProductType = type =>
request.get(`/product/category/getByProductType/${type}`, {
prefix: goodsApi,
......@@ -117,3 +128,47 @@ export const apiEditDraft = data =>
prefix: goodsApi,
data,
});
// 菜单分组 http://yapi.quantgroups.com/project/389/interface/api/64044
export const apiQueryShopList = data =>
request.post('/api/merchants/products/storageRack/listByShopIdAndStorageRackIds', {
prefix: goodsApi,
data: stringify(_.omitBy(data, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
// 新建分组
export const apiCreateShop = data =>
request.post('/api/merchants/products/storageRack/create', {
prefix: goodsApi,
data,
});
// 商品标签列表 http://yapi.quantgroups.com/project/389/interface/api/57979
export const apiTagList = data =>
request.post('/api/merchants/products/tag/getAll', {
prefix: goodsApi,
data,
});
// 单位列表 http://yapi.quantgroups.com/project/389/interface/api/57179
export const apiUnits = data =>
request.get('/api/merchants/products/units', {
prefix: goodsApi,
data,
});
// 获取shopids http://yapi.quantgroups.com/project/389/interface/api/38056
export const apiShopIds = data =>
request.get('/api/merchants/shops/getBySupplierId?state=1&productBusiness=1', {
prefix: goodsApi,
data,
});
// 获取店铺详情 http://yapi.quantgroups.com/project/389/interface/api/57589
export const apiGetShopDetail = data =>
request.post('/product/api/merchant/detail', {
prefix: goodsApi,
data: stringify(_.omitBy(data, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
const Model = {
namespace: 'BusinessManage',
state: {},
effects: {},
reducers: {},
};
export default Model;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
.contract {
text-align: center;
}
This diff is collapsed.
This diff is collapsed.
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