Commit 87d0fb79 authored by 陈万宝's avatar 陈万宝

Merge branch 'feature/20230315_take_out_goods' into feature/20230327_public_takeaway

parents 3f7fa4ce 46cd83d3
const isProduction = process.env.NODE_ENV === 'production';
const isPre = process.env.PRE_ENV === 'pre';
const environment = 'xyqb';
const environment = 'sc1';
const envAPi = {
api: `https://security-${environment}.liangkebang.net`, //'https://security-xyqb.liangkebang.net',
kdspOpApi: `https://sc-merchant-api-${environment}.liangkebang.net`,
......
......@@ -369,6 +369,13 @@ class goodsManage extends Component {
const canAddService = permissions[GOOD_MANAGE.ADD_SERVICE_GOODS];
const canAddNormal = permissions[GOOD_MANAGE.ADD_NORMAL_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
......
......@@ -289,3 +289,11 @@ export async function apiDraftList(data) {
data,
});
}
// 新增外卖商品
export async function addTakeOutGoods(params) {
return request.post('/v1/channels/products/add', {
prefix: goodsApi,
data: params,
});
}
......@@ -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,108 @@
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;
}
}
}
.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;
}
.specsBetween {
display: flex;
flex-flow: row nowrap;
}
.specRepertory {
background-color: #319bfe;
color: #fff;
height: 20px;
line-height: 20px;
text-align: center;
margin: 0 10px;
padding: 0 8px;
border-radius: 8px;
}
.repertoryLimit {
background-color: #88c0f5;
height: 20px;
line-height: 20px;
text-align: center;
color: #fff;
border-radius: 3px;
}
.deal {
:global {
.ant-form-item-control-input-content {
display: flex;
align-items: center;
}
.ant-form-item {
margin-bottom: 0;
}
}
}
.conBg {
background: #f8f8f8;
min-width: 100%;
width: fit-content;
: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-flow: row wrap;
background: #f8f8f8;
}
import React, { useState, useEffect, forwardRef, useRef, useImperativeHandle } from 'react';
import { Button, Modal, Form, Switch, Input, Select } from 'antd';
import styles from '../common.less';
import { apiCreateShop } from '../service';
const AddMenusModal = (props, ref) => {
const [confirmLoading, setConfirmLoading] = useState(false);
const [open, setOpen] = useState(false);
const [formProject] = Form.useForm();
const [menusSwitch, setMenusSwitch] = useState(0); // 分组开启状态
// const {
// queryShopList,
// } = props;
useImperativeHandle(ref, () => ({
// changeVal 就是暴露给父组件的方法
setOpen: newVal => {
setOpen(newVal);
},
}));
const handleOk = () => {
formProject
.validateFields()
.then(async values => {
console.log('valuse', values, menusSwitch);
const params = {
name: values?.name,
necessary: menusSwitch,
};
const data = await apiCreateShop(params);
if (data.code === '0000') {
formProject.resetFields(); // 表单清除历史
setConfirmLoading(true);
setTimeout(() => {
setOpen(false);
setConfirmLoading(false);
}, 2000);
}
})
.catch(info => {
// queryShopList()
console.log('保存异常', info);
});
};
const handleCancel = () => {
console.log('Clicked cancel button');
setOpen(false);
};
const onChange = () => {
if (menusSwitch === 0) {
setMenusSwitch(1);
return false;
}
setMenusSwitch(0);
return false;
};
useEffect(() => {
console.log('open', open);
}, [open]);
return (
<>
{open && (
<Modal
title="添加分组"
visible={open}
onOk={handleOk}
confirmLoading={confirmLoading}
initialValues={{
menusSwitch: 0,
}}
onCancel={handleCancel}
>
<Form form={formProject}>
<Form.Item label="Select">
<Select>
<Select.Option value="shopId">Demo</Select.Option>
</Select>
</Form.Item>
<Form.Item label="分组名称" name="name" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item label="下单必选分组" name="necessary" valuePropName={menusSwitch}>
<Switch
defaultChecked={menusSwitch}
checkedChildren="开启"
unCheckedChildren="关闭"
onChange={onChange}
/>
<div className={styles.textStyle}>
选中后,顾客下单需至少选择1个 “下单必须分组” 商品
</div>
<div className={styles.textStyle}>每店仅可设置1个必点分组</div>
</Form.Item>
</Form>
</Modal>
)}
</>
);
};
export default forwardRef(AddMenusModal);
import React, { useState, useEffect, forwardRef, useRef, useImperativeHandle } from 'react';
import { Button, Modal, Radio, Space, Form, InputNumber, Switch, Input } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import styles from '../common.less';
import { ENUM_SET_REPERTORY } from '../config';
const AddMultiSpecModal = (props, ref) => {
const [confirmLoading, setConfirmLoading] = useState(false);
const [modalText, setModalText] = useState('Content of the modal');
const [multiRepertory, setMultiRepertory] = useState(false);
// const {
// open,
// setOpen,
// } = props;
useImperativeHandle(ref, () => ({
// changeVal 就是暴露给父组件的方法
setMultiRepertory: newVal => {
setMultiRepertory(newVal);
},
}));
const onChange3 = () => {};
const handleOk = () => {
setModalText('The modal will be closed after two seconds');
setConfirmLoading(true);
setTimeout(() => {
setMultiRepertory(false);
setConfirmLoading(false);
}, 2000);
};
const handleCancel = () => {
console.log('Clicked cancel button');
setMultiRepertory(false);
};
useEffect(() => {
console.log('open', multiRepertory);
}, [multiRepertory]);
return (
<>
{multiRepertory && (
<Modal
title="修改库存"
visible={multiRepertory}
onOk={handleOk}
width={1050}
confirmLoading={confirmLoading}
onCancel={handleCancel}
>
<Form>
<Form.Item>
<div>份量(如大小/小份、微辣/特辣等)</div>
<Form.List name="users">
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }) => (
<Space
key={key}
style={{
display: 'flex',
flexDirection: 'column',
marginBottom: 8,
}}
align="baseline"
>
<Form.Item className={styles.deal}>
<Form.Item
{...restField}
name={[name, 'first']}
rules={[
{
required: true,
message: 'Missing first name',
},
]}
>
<Input placeholder="名称" />
</Form.Item>
<Form.Item
{...restField}
name={[name, 'last']}
rules={[
{
required: true,
message: 'Missing last name',
},
]}
>
<Input placeholder="约 份量(数字)" />
</Form.Item>
<Form.Item
{...restField}
name={[name, 'last']}
rules={[
{
required: true,
message: 'Missing last name',
},
]}
>
<Input placeholder="约 份量(数字)" />
</Form.Item>
<Form.Item
{...restField}
name={[name, 'last']}
rules={[
{
required: true,
message: 'Missing last name',
},
]}
>
<Input placeholder="销售价(元)" />
</Form.Item>
<Form.Item
{...restField}
name={[name, 'last']}
rules={[
{
required: true,
message: 'Missing last name',
},
]}
>
<Input placeholder="活动价(元)" />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(name)} />
</Form.Item>
<Form.Item>
<Form.Item>
<div>添加规格(如加料、甜度、辣度等)</div>
<Form.Item>
<Form.List name="names">
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field, index) => (
<Form.Item
// {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
// label={index === 0 ? 'Passengers' : ''}
required={false}
key={field.key}
>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
className={styles.deal}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field.",
},
]}
>
<Input
placeholder="规格名称"
className={styles.nameWidth}
/>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => remove(field.name)}
/>
) : null}
</Form.Item>
<Form.Item>
<Form.List name="sights">
{(fields, { add, remove }) => (
<>
{fields.map(field => (
<Space key={field.key} align="baseline">
<Form.Item
noStyle
shouldUpdate={(prevValues, curValues) =>
prevValues.area !== curValues.area ||
prevValues.sights !== curValues.sights
}
>
{() => (
<Form.Item
{...field}
// label="Sight"
name={[field.name, 'sight']}
rules={[
{
required: true,
message: 'Missing sight',
},
]}
>
<Input style={{ width: '200px' }} placeholder="加价名称" />
</Form.Item>
)}
</Form.Item>
<Form.Item
{...field}
// label="Price"
// name={[field.name, 'price']}
rules={[
{
required: true,
message: 'Missing price',
},
]}
>
<InputNumber style={{ width: '200px' }} placeholder="加价名称金额(元)" />
</Form.Item>
<MinusCircleOutlined
onClick={() => remove(field.name)}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
block
icon={<PlusOutlined />}
>
新增加价
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
style={{ width: '60%' }}
icon={<PlusOutlined />}
>
新增规格
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
</Form.Item>
</Form.Item>
</Space>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
新增份量
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
</Form>
</Modal>
)}
</>
);
};
export default forwardRef(AddMultiSpecModal);
import React, { useState, useEffect, forwardRef, useRef, useImperativeHandle } from 'react';
import { Button, Modal, Radio, Form, InputNumber, Switch } from 'antd';
import styles from '../common.less';
import { ENUM_SET_REPERTORY } from '../config';
import { debounce } from '@/utils/utils';
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 { 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,
});
};
// 勾选库存设置
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;
// 回调库存
modifiedInventory(type, idx, values);
setConfirmLoading(true);
setTimeout(() => {
setOpenRepertory(false);
setConfirmLoading(false);
}, 1000);
};
const handleCancel = () => {
setOpenRepertory(false);
};
useEffect(() => {
if (item?.serviceItem) {
const { productStock = 1, autoStock = 0, maxStock = 2 } = item?.serviceItem;
const params = {
productStock,
autoStock: +autoStock === 1,
maxStock,
};
switch (type) {
case 'all': // 统一设置
form.setFieldsValue(params);
break;
case 'multi': // 多规格设置
form.setFieldsValue(params);
break;
case 'singular': // 单规格设置
// setInitialValues(params);
form.setFieldsValue(params);
break;
default:
break;
}
}
}, [openRepertory, item]);
return (
<>
{openRepertory && (
<Modal
title="修改库存"
visible={openRepertory}
onOk={handleOk}
confirmLoading={confirmLoading}
onCancel={handleCancel}
>
<Form form={form} initialValues={initialValues} onValuesChange={getFormValues}>
<Form.Item className={styles.itemInlineModal}>
<Form.Item
name="productStock"
label="剩余库存"
style={{
display: 'flex',
marginRight: '8px',
}}
rules={[{ required: true, message: '请填写剩余库存' }]}
>
<InputNumber
min={0}
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>
<Form.Item
name="maxStock"
label="最大库存"
rules={[{ required: true, message: '请填写最大库存' }]}
>
<InputNumber
min={0}
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);
console.log('sss/', formProject);
// 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);
import React, { useState, useContext, useEffect, forwardRef, useImperativeHandle } from 'react';
import { Cascader, Form, Input, Select, Popover, Button, Checkbox } from 'antd';
import React, {
useState,
useContext,
useEffect,
forwardRef,
useImperativeHandle,
useRef,
} from 'react';
import { Cascader, Form, Input, Select, Popover, Button, Checkbox, Divider, Modal } from 'antd';
import { formItemLayout } from '../config';
import { ServiceContext } from '../context';
import { debounce } from '@/utils/utils';
import AddMenusModal from './AddMenusModal';
import UploadImage from './UploadImage';
import { apiShopIds, apiQueryShopList } from '../service';
const CreateSelectOption = optionList =>
optionList.map(brandItem => (
......@@ -23,13 +33,16 @@ const FormInformationBasic = forwardRef((props, ref) => {
categoryList,
virtualCategoryList,
brandList,
shopList,
afterAddressList,
specListData,
} = props;
const [form] = Form.useForm();
const [noreBrandList, setNoreBrandList] = useState([]);
const customer = useContext(ServiceContext);
const childAddMenusModalRef = useRef(null);
const [takeawayImageList, setTakeawayImageList] = useState([]);
const [shopIds, setShopIds] = useState([]);
const [shopList, setShopList] = useState([]);
const onCheck = async () => {
try {
......@@ -53,12 +66,61 @@ const FormInformationBasic = forwardRef((props, ref) => {
return null;
}
};
// 自定义加入菜单
const showModal = () => {
childAddMenusModalRef.current.setOpen(true);
};
// 自定义菜单下拉
const dropdownRender = menus => (
<div>
{menus}
<Divider
style={{
margin: 0,
}}
/>
<div
style={{
padding: 8,
background: '#1890ff',
color: '#fff',
textAlign: 'center',
}}
onClick={showModal}
>
添加自定义分组
</div>
</div>
);
const onTakeawayImageList = imgList => {
setTakeawayImageList(imgList);
form.setFieldsValue({
commonImageList: imgList,
});
};
const getFormValues = debounce(() => {
const values = form.getFieldsValue();
props.onValuesChange({ infoMation: values });
}, 400);
// 查询shopIds
const queryShopIds = async () => {
if (!shopIds.length) {
const result = await apiShopIds();
setShopIds(result.data || []);
}
};
// 查询分组列表
const queryShopList = async params => {
if (!shopList.length) {
const result = await apiQueryShopList(params);
setShopList(result.data || []);
}
};
const onChangeShopId = async e => {
if (e) {
queryShopList({ shopId: e }); // 分组列表
}
};
useImperativeHandle(ref, () => ({
onCheck,
reset: form.resetFields,
......@@ -74,6 +136,12 @@ const FormInformationBasic = forwardRef((props, ref) => {
form.setFieldsValue(editData);
}, [customer.isEdit, editData]);
useEffect(() => {
console.log('!customer.isTakeawayService', customer);
console.log('newCategoryList[customer.productType]', newCategoryList[customer.productType]);
console.log('newCategoryList[customer.productType]', shopList);
queryShopIds()
}, [customer.productType]);
return (
<Form
{...formItemLayout}
......@@ -88,13 +156,61 @@ const FormInformationBasic = forwardRef((props, ref) => {
scrollToFirstError
onValuesChange={getFormValues}
>
<Popover content={form.getFieldValue('name')} trigger="hover">
<Form.Item
key="name"
name="name"
label="商品名称"
rules={[
{ required: true, min: 2, message: '请输入最少两个字符的商品名称', whitespace: true },
]}
>
<Input placeholder="请输入商品名称" disabled={customer.isDisabled} />
</Form.Item>
</Popover>
{customer.isTakeawayService && (
<Form.Item
name="productRefShopId"
key="productRefShopId"
label="所属门店"
rules={[{ required: true, message: '请选择所属门店' }]}
>
<Select
fieldNames={{ label: 'name', value: 'id' }}
placeholder="请选择所属门店"
options={shopIds}
onChange={onChangeShopId}
></Select>
</Form.Item>
)}
{/* 菜单分组 */}
{customer.isTakeawayService && (
<Form.Item
name="storageRackIds"
label="菜单分组"
rules={[{ type: 'array', required: true, message: '请输入菜单分组' }]}
>
<Cascader
placeholder="请选择菜单分组"
disabled={customer.isEdit && customer.isNormal}
showSearch={{ filter: filterCategoryOptions }}
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
onChange={props.onCategoryChange}
options={shopList}
dropdownRender={dropdownRender}
/>
</Form.Item>
)}
{/* 新增菜单分组弹框 */}
<AddMenusModal ref={childAddMenusModalRef} queryShopList={queryShopList} />
<Form.Item
name="categoryId"
key="categoryId"
label="商品类目"
rules={[{ type: 'array', required: true, message: '请输入商品类目!' }]}
rules={[{ type: 'array', required: true, message: '请输入商品类目' }]}
>
<Cascader
placeholder="请选择商品类目"
placeholder="请选择商品类目"
disabled={customer.isEdit && customer.isNormal}
showSearch={{ filter: filterCategoryOptions }}
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
......@@ -102,11 +218,35 @@ const FormInformationBasic = forwardRef((props, ref) => {
options={newCategoryList[customer.productType]}
/>
</Form.Item>
{!customer.isCard && (
{/* 外卖-商品图片 */}
{customer.isTakeawayService && (
<Form.Item
name="commonImageList"
label="商品图片"
extra="支持.jpg/png格式图片,建议单张比例1:1,大小200kb左右,最多可以上传5张"
rules={[
{
required: true,
type: 'array',
message: '请上传商品图片',
},
]}
>
<UploadImage
name="takeawayImageList"
limit={5}
disabled={customer.isDisabled}
pictures={takeawayImageList}
setPictureList={list => onTakeawayImageList(list)}
/>
</Form.Item>
)}
{!customer.isTakeawayService && !customer.isCard && (
<Form.Item
name="brandId"
label="商品品牌"
rules={[{ required: true, message: '请选择商品品牌!' }]}
rules={[{ required: true, message: '请选择商品品牌' }]}
extra="若需新增品牌请联系业务员"
>
<Select
......@@ -121,19 +261,8 @@ const FormInformationBasic = forwardRef((props, ref) => {
</Select>
</Form.Item>
)}
<Popover content={form.getFieldValue('name')} trigger="hover">
<Form.Item
key="name"
name="name"
label="商品名称"
rules={[
{ required: true, min: 2, message: '请输入最少两个字符的商品名称!', whitespace: true },
]}
>
<Input placeholder="请输入商品名称" disabled={customer.isDisabled} />
</Form.Item>
</Popover>
{!customer.isCard && (
{!customer.isTakeawayService && !customer.isCard && (
<Form.Item
name="character"
label="商品卖点"
......@@ -146,7 +275,7 @@ const FormInformationBasic = forwardRef((props, ref) => {
</Form.Item>
)}
{!customer.isCard && (
{!customer.isTakeawayService && !customer.isCard && (
<Form.Item name="afterAddressId" label="售后地址">
<Select showSearch placeholder="请选择售后地址" filterOption={fileterBrandOptions}>
{(afterAddressList || []).map(item => (
......@@ -158,7 +287,8 @@ const FormInformationBasic = forwardRef((props, ref) => {
</Form.Item>
)}
{!customer.isCard &&
{!customer.isTakeawayService &&
!customer.isCard &&
specListData.map((item, index) => (
<Form.Item name={item.specId} key={item.specId} label={item.specName}>
<Checkbox.Group options={item.specValues} />
......@@ -180,9 +310,9 @@ const FormInformationBasic = forwardRef((props, ref) => {
key="description"
name="description"
label="描述"
rules={[{ required: true, message: '请输入描述,100字以内' }]}
rules={[{ required: true, message: '请输入描述,100字以内' }]}
>
<Input.TextArea showCount maxLength={100} placeholder="请输入描述, 100字以内" />
<Input.TextArea showCount maxLength={100} placeholder="请输入描述, 100字以内" />
</Form.Item>
) : null}
</Form>
......
/* eslint-disable react/no-array-index-key */
/* eslint-disable no-shadow */
import React, {
useContext,
useState,
useEffect,
forwardRef,
useImperativeHandle,
useRef,
} from 'react';
import {
Form,
Input,
Select,
Button,
Checkbox,
Radio,
Space,
Modal,
Switch,
Row,
Col,
InputNumber,
Cascader,
Divider,
DatePicker,
} from 'antd';
import moment from 'moment';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Title } from './CommonTemplate';
import { formItemLayout, ENUM_REPERTORY, ENUM_SET_REPERTORY, ENUM_WEEK } from '../config';
import { ServiceContext } from '../context';
import { debounce } from '@/utils/utils';
import UploadImage from './UploadImage';
import AddSellTimeModal from './AddSellTimeModal';
import styles from '../common.less';
import AddRepertoryModal from './AddRepertoryModal';
import AddMultiSpecModal from './AddMultiSpecModal';
import { apiTagList, apiUnits, apiGetShopDetail } from '../service';
import { localAutoSaveKey } from '../utils';
import localStorage from '@/utils/localStorage';
const createInitValues = () => ({
description: '', // 商品描述
detailImageList: [], // 商品图片
stock: '1', // 库存类型
saleTimeType: 0, // 售卖时间
singleDelivery: 0, // 单点不送
list: 1, // 列出商品
});
const format = 'HH:mm';
const { RangePicker } = DatePicker;
const TakeawayGoodsInfo = forwardRef((props, ref) => {
const { editData, infoMation } = props;
const [form] = Form.useForm();
const [initValue, setInitValue] = useState(createInitValues());
const customer = useContext(ServiceContext);
const [detailImageList, setDetailImageList] = useState([]);
const [newCategoryList, setNewCategoryList] = useState({});
const addSellTimeRef = useRef(null);
const AddRepertoryRef = useRef(null);
const AddMultiSpecRef = useRef(null);
const [saleDates, setSaleDates] = useState({
saleDates: [],
saleTimes: [],
// saleTimes: [[moment('22-02', 'HH:mm'), moment('23-02', 'HH:mm')]],
}); // 可售日期
const [timeType, setTimeType] = useState(0);
const [repertoryType, setRepertoryType] = useState('1');
const [tagList, setTagList] = useState([]);
const [unitsList, setUnitsList] = useState([]);
const [takeawayData, setTakeawayData] = useState({});
// const [multiSpecList, setMultiSpecList] = useState([]);
// const [singularSpecList, setSingularSpecList] = useState([]);
const [multiSpu, setMultiSpu] = useState([]);
const [singularSpu, setSingularSpu] = useState([]);
const [intactData, setIntactData] = useState({});
const [repertoryState, setRepertoryState] = useState('');
const [repertoryModel, setRepertoryModel] = useState({});
const [tempMultiSpu, setTempMultiSpu] = useState([]);
const initialDealValue = [
{
specGroupName: '',
},
];
// 自定义加入菜单
const showModal = () => {
addSellTimeRef.current.setOpen(true);
};
const onCheck = async () => {
try {
const values = await form.validateFields();
return {
...values,
temp: 'takeawayItem',
intactData,
};
} catch (errorInfo) {
return null;
}
};
const takeawayCalc = takeawayData => {
// 商品基本信息编辑商品名称
const { infoMation: name, infoMation, takeawayItem } = takeawayData;
// weight 份量 specs规格 生成sku规则 weight * specs
const {
specs = [],
weight = [],
description,
detailImageList,
list,
minPurchaseNum = 0,
saleDates,
maxStock = 0,
saleTimes = [],
label,
unit = [],
quantity,
productStock = 0,
salePrice,
singleDelivery,
saleTimeType,
autoStock = 0,
} = takeawayItem;
const singularSpecList = [{ specGroupName: '份量', generateSku: 1, specs: [] }]; // 单规格
const multiSpecList = [{ specGroupName: '份量', generateSku: 1, specs: [] }]; // 多规格
const multiSpu = []; // 多库存spu、
let singularSpu = []; // 单库存spu
const saleTimesTemp = [];
let initIndex = 0;
if (saleTimes.length) {
saleTimes.forEach(item => {
const startTime = item ? moment(item[0]).format(format) : '';
const endTime = item ? moment(item[1]).format(format) : '';
saleTimesTemp.push({ startTime, endTime });
});
}
const temp = {
salePrice: salePrice || 0,
productStock,
list: 1,
serviceItem: {
description, // 商品描述
maxStock, // 最大库存
productStock,
minPurchaseNum, // 最少购买
saleTimeType, // 售卖时间
singleDelivery, // 单点不送
list, // 是否列出 1 是 0 否(外卖商品必填)
label, // 商品标签id
saleDates, // 可售日期 1-8
saleTimes: saleTimesTemp, // 可售日期 时间段
autoStock, // 弹框设置---自动补足
},
};
// 单规格
if (+repertoryType === 1) {
const specs = {
salePrice,
maxStock,
quantity,
unit: (unit && unit.splice(unit.length - 1)[0]) || '',
productStock,
specGroupName: '份量',
};
singularSpecList[0].specs = [specs];
singularSpu = [{ ...temp, specs: [specs] }];
setSingularSpu(singularSpu);
}
// 多规格
if (+repertoryType === 2) {
if (name) {
console.log('weight', weight);
if (weight.length) {
weight.forEach((item, weightIndex) => {
console.log('eee====', item);
if (item?.unit && item.unit.length) {
item.unit = item.unit.slice(item.unit.length - 1).toString();
}
if (item && !item.specGroupName) {
item.specGroupName = '份量';
}
if (item && !item.generateSku) {
item.generateSku = 1;
}
multiSpecList[0].specs = weight; // 处理specList
// 循环一次 插入规则列表
let isFirstLoops = true;
if (specs.length) {
specs.forEach((specsItem, specsIndex) => {
if (specsItem && !specsItem.generateSku) {
specsItem.generateSku = 1;
}
if (isFirstLoops && multiSpecList.length < specs.length + 1) {
multiSpecList.push(specsItem); // 处理specList
}
if (specsItem.specs.length > 1) {
specsItem.specs.forEach((itm, idx) => {
const params = {
initIndex,
unique: `${item.specName}-${specsItem.specGroupName}-${specsItem.specs[idx].specName}`,
...JSON.parse(JSON.stringify(temp)),
// ...intactData?.items[initIndex],
specs: [
{ ...item },
{ ...specsItem.specs[idx], specGroupName: specsItem.specGroupName },
],
};
multiSpu.push(params); // 处理spu
initIndex++;
});
} else {
const params = {
initIndex,
unique: `${item.specName}-${specsItem.specGroupName}-${specsItem.specs[0].specName}`,
...JSON.parse(JSON.stringify(temp)),
// ...intactData?.items[initIndex],
specs: [
{ ...item },
{ ...specsItem.specs[0], specGroupName: specsItem.specGroupName },
],
};
multiSpu.push(params); // 处理spu
initIndex++;
}
});
isFirstLoops = false;
} else {
const params = {
initIndex,
unique: `${item?.specName}`,
...JSON.parse(JSON.stringify(temp)),
// ...intactData?.items[initIndex],
specs: [{ ...item }],
};
multiSpu.push(params); // 处理spu
initIndex++;
}
});
}
}
// console.log('multiSpu', multiSpu);
console.log('multiSpu===1111', multiSpu);
if (tempMultiSpu.length) {
multiSpu.forEach((item, index) => {
tempMultiSpu.forEach((itm, idx) => {
if (item.unique === itm.unique) {
item.serviceItem = objectComparison(item.serviceItem, itm);
}
});
});
}
// debugger
console.log('multiSpu===2222', multiSpu);
setMultiSpu(multiSpu);
}
// debugger
// +repertoryType === 1 单规格 2多规格
const intactDataTemp = {
type: 5, // 外卖类型
...infoMation,
label: label && label.toString(),
list,
description,
detailImageList,
singleDelivery,
specList: +repertoryType === 1 ? singularSpecList : multiSpecList, // 单库存和多库存specList
items: +repertoryType === 1 ? singularSpu : JSON.parse(JSON.stringify(multiSpu)),
categoryId: (
infoMation?.categoryId && infoMation?.categoryId.slice(infoMation.categoryId.length - 1)
).toString(),
};
console.log('intactData======>', intactData);
setIntactData(intactDataTemp);
return intactData;
};
// 过滤对象
const objectComparison = (item, itm) => {
let {
serviceItem: { maxStock, autoStock, productStock },
} = itm;
let params = { maxStock, autoStock, productStock };
let temp = { ...item, ...params };
return temp;
};
const onChange = () => {};
const onDealFinish = values => {
console.log('Received values of form:', values);
};
const getFormValues = debounce(() => {
const values = form.getFieldsValue();
console.log('values========', values);
props.onValuesChange({ takeawayItem: values });
const takeawayData = localStorage.get(localAutoSaveKey);
console.log('takeawayData', takeawayData);
setTakeawayData(takeawayData);
takeawayCalc(takeawayData);
}, 400);
// 设置库存
const modifiedInventory = (type, idx, values) => {
const { productStock, maxStock, autoStock } = values;
if (type === 'multi') {
multiSpu.map(item => {
if (item.unique === idx) {
item.serviceItem.productStock = productStock;
item.serviceItem.autoStock = autoStock;
item.serviceItem.maxStock = maxStock;
}
return item;
});
let temp = JSON.parse(JSON.stringify(multiSpu));
tempMultiSpu.push(...temp);
setTempMultiSpu(tempMultiSpu);
setMultiSpu(temp);
console.log('33333========>', multiSpu, tempMultiSpu);
// if (intactData?.items.length) {
// intactData.items[+idx].serviceItem.productStock = productStock;
// intactData.items[+idx].serviceItem.autoStock = autoStock;
// intactData.items[+idx].serviceItem.maxStock = maxStock;
// console.log('index===2222', +idx, intactData);
// const temp = { ...intactData };
// setIntactData(temp);
// }
}
if (type === 'all') {
if (intactData?.items.length) {
intactData?.items.forEach(item => {
item.serviceItem.productStock = productStock;
item.serviceItem.autoStock = autoStock;
item.serviceItem.maxStock = maxStock;
return item;
});
const temp = { ...intactData };
setIntactData(temp);
}
}
return false;
};
const querGetShopDetail = async () => {
let params = {
id: '638899799727110',
shopId: 15,
};
const result = await apiGetShopDetail(params);
console.log('result', result);
};
const fileterBrandOptions = (input, options) => options.children.includes(input);
const filterCategoryOptions = (inputValue, path) =>
path.some(option => option.name.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
useImperativeHandle(ref, () => ({
onCheck,
reset: () => {
setInitValue(createInitValues());
form.resetFields();
},
}));
const startTime = moment(moment().format(format));
const endTime = moment(moment().format(format));
// 上传图片
const onCardSuccessImageList = imgList => {
setDetailImageList(imgList);
form.setFieldsValue({
detailImageList: imgList,
});
};
const radioChangeEvent = key => {
const value = form.getFieldValue(key);
setInitValue({
...initValue,
[key]: value,
});
};
// 自定义菜单下拉
const dropdownRender = menus => (
<div>
{menus}
<Divider
style={{
margin: 0,
}}
/>
<div
style={{
padding: 8,
background: '#1890ff',
color: '#fff',
textAlign: 'center',
}}
onClick={showModal}
>
添加自定售卖时间
</div>
</div>
);
// 切换库存
const onChangeRepertory = e => {
setRepertoryType(`${e.target.value}`);
};
// 勾选库存设置
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,
});
}
getFormValues();
};
// 最大库存设置
const onChangeMaxStock = e => {
// 已经勾选最大库存 自动更新剩余库存
if (+repertoryState === 1) {
form.setFieldsValue({
productStock: e,
});
}
};
// 切换时间
const onChangeTime = e => {
setTimeType(e.target.value);
};
// 自动补全
const onChangeAutoStock = e => {
console.log('e=======', e);
form.setFieldsValue({
autoStock: e ? 1 : 0,
});
};
// 显示加入库存弹框
const showAddRepertoryModal = (type, idx, item) => {
AddRepertoryRef.current.setOpenRepertory(true);
setRepertoryModel({ type, idx, item });
};
const onFinish = values => {
console.log('Received values of form:', values);
};
// 拼接sku 名称
const calcLabelName = (intactData, item) => {
const tempName = `${intactData?.name || ''}`;
const tempSpecName = `${item?.specs[0]?.specName || ''}`;
const tempQuantity = `(${item.specs[0]?.quantity || ''}`;
const tempUnit = `${item.specs[0]?.unit || ''})`;
const tempSecondSpecName = `${item.specs[1]?.specName || ''}`;
const isShow = tempQuantity && tempUnit && '+';
return `${tempName} ${tempSpecName} ${tempQuantity} ${isShow} ${tempUnit} ${tempSecondSpecName}`;
};
const init = async () => {
if (!tagList.length) {
const res = await apiTagList();
setTagList(res.data || []);
}
if (!unitsList.length) {
const res = await apiUnits();
setUnitsList(res.data || []);
}
querGetShopDetail();
setTempMultiSpu([])
return false;
};
useEffect(() => {
if (customer.isEdit || customer.isUseCache) {
if (!editData) return;
form.setFieldsValue(editData);
setInitValue({ ...editData });
}
}, [customer.isEdit, customer.isUseCache, editData]);
useEffect(() => {
setIntactData(intactData);
}, [intactData]);
useEffect(() => {
init();
}, []);
return (
<>
<Form
{...formItemLayout}
form={form}
name="takeaway"
initialValues={initValue}
scrollToFirstError
onValuesChange={getFormValues}
>
<Title title="商品详细信息" />
<Form.Item name="description" label="商品描述">
<Input.TextArea
showCount
maxLength={200}
style={{ width: 400 }}
placeholder="请输入商品描述"
/>
</Form.Item>
<Form.Item
name="detailImageList"
label="商品图片"
extra="支持.jpg/png格式图片,建议单张切片宽750像素,大小200kb左右,您可以拖拽图片调整顺序,最多上传5张。"
>
<UploadImage
name="detailImageList"
limit={5}
disabled={customer.isDisabled}
pictures={detailImageList}
setPictureList={list => onCardSuccessImageList(list)}
/>
</Form.Item>
<Title title="商品售卖信息" />
<Form.Item
name="minPurchaseNum"
label="最少购买"
// rules={[{ required: true, message: '每日最低接待量' }]}
>
<InputNumber min={1} style={{ width: 200 }} placeholder="请输入购买量" />
</Form.Item>
<Form.Item
name="saleTimeType"
label="售卖时间"
// rules={[{ type: 'array', required: true, message: '请输入售卖时间!' }]}
>
<Radio.Group onChange={onChangeTime}>
<Radio value={0}>全时段</Radio>
<Radio value={1}>自定义售卖时间</Radio>
</Radio.Group>
</Form.Item>
{timeType === 1 && (
<>
<Form.Item name="saleDates" 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"
defaultPickerValue={[startTime, endTime]}
format={format}
onChange={onChange}
showTime={{
format: 'HH:mm',
defaultValue: [moment('00:00', 'HH:mm'), moment('00:00', 'HH:mm')],
hideDisabledOptions: true,
}}
disabledTime={() => ({
disabledMinutes: () => {
const result = [];
for (let i = 1; i < 60; i++) {
if (i !== 30) {
result.push(i);
}
}
return result;
},
})}
/>
</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.Item
name="label"
label="商品标签"
// rules={[{ type: 'array', required: true, message: '请输入售卖时间!' }]}
>
<Select
mode="multiple"
allowClear
placeholder="请选择商品标签"
style={{ width: 400 }}
disabled={customer.isEdit && customer.isNormal}
// showSearch={{ filter: filterCategoryOptions }}
fieldNames={{ label: 'tagName', value: 'tagId' }}
// onChange={props.onCategoryChange}
options={tagList}
/>
</Form.Item>
<Form.Item name="singleDelivery" label="单点不送" extra="开启后顾客点单则此商品不可下单">
<Radio.Group>
<Radio value={1}>是</Radio>
<Radio value={0}>否</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name="list" label="列出商品" extra="开启后平台展示商品">
<Radio.Group>
<Radio value={1}>是</Radio>
<Radio value={0}>否</Radio>
</Radio.Group>
</Form.Item>
<Title title="规格信息" />
{/* <Form.Item label="限购" name="limitPurchase" valuePropName="checked">
<Checkbox onChange={() => radioChangeEvent('limitPurchase')}>
<b style={{ marginLeft: 10 }}>启用限购</b>
<span style={{ marginLeft: 10 }} className="ant-form-text">
限制每人可购买数量
</span>
</Checkbox>
</Form.Item> */}
<Form.Item label="库存" name="stock">
<Radio.Group
options={ENUM_REPERTORY}
onChange={onChangeRepertory}
value="1"
buttonStyle="solid"
optionType="button"
/>
</Form.Item>
{repertoryType === '1' && (
<>
<Form.Item label="份量" className={styles.required}>
<Form.Item
name="quantity"
style={{
display: 'inline-block',
}}
rules={[{ required: true, message: '请输入分量' }]}
>
<InputNumber min={1} style={{ width: 200 }} placeholder="请输入数字" />
</Form.Item>
<Form.Item
name="unit"
rules={[{ type: 'array', required: true, message: '请选择单位' }]}
style={{
display: 'inline-block',
margin: '0 8px',
}}
>
<Cascader
placeholder="请选择单位"
disabled={customer.isEdit && customer.isNormal}
showSearch={{ filter: filterCategoryOptions }}
fieldNames={{ label: 'name', value: 'name', children: 'children' }}
onChange={props.onCategoryChange}
options={unitsList}
/>
</Form.Item>
</Form.Item>
<Form.Item
name="salePrice"
label="销售价格"
rules={[{ required: true, message: '请输入销售价格' }]}
>
<InputNumber min={0} style={{ width: 200 }} placeholder="元" />
</Form.Item>
<Form.Item className={styles.itemInline}>
<Form.Item
name="productStock"
label="剩余库存"
style={{
display: 'flex',
marginRight: '8px',
}}
rules={[{ required: true, message: '请填写剩余库存' }]}
>
<InputNumber
min={0}
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>
<Form.Item
name="maxStock"
label="最大库存"
rules={[{ required: true, message: '请填写最大库存' }]}
>
<InputNumber
min={0}
style={{ width: 200 }}
placeholder="请输入"
onChange={onChangeMaxStock}
/>
</Form.Item>
<Form.Item name="autoStock" label="自动补足" valuePropName="checked">
<Switch
checkedChildren="开启"
unCheckedChildren="关闭"
// defaultUnChecked
onChange={onChangeAutoStock}
/>
</Form.Item>
<div className={styles.rowWarp}>
{singularSpu.length > 0 &&
takeawayData?.infoMation?.name &&
singularSpu.map((item, index) => (
<div className={styles.specsBetween}>
<Form.Item label={calcLabelName(intactData, item, 'singular')}>
<div className={styles.specsBetween}>
<span className={styles.repertoryLimit}>
{item?.specs[0]?.productStock}/{item?.specs[0]?.maxStock}
</span>
{/* <div
className={styles.specRepertory}
onClick={() => {
showAddRepertoryModal('singular', index); // 单规格库存
}}
>
设置库存
</div> */}
</div>
</Form.Item>
</div>
))}
</div>
</>
)}
{repertoryType === '2' && (
<>
<Form.Item>
<div>份量(如大小/小份、微辣/特辣等)</div>
{/* <Form.List name="deal">
{(dealFields, { add, remove }) => (
<>
{dealFields.map((field, index) => (
<Form.Item key={field.key} className={styles.conBg}>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
name={[field.name, 'specGroupName']}
rules={[
{
required: true,
whitespace: true,
message: '请输入名称例如(份量)',
},
]}
noStyle
>
<Input
placeholder="请输入名称"
style={{
width: '60%',
}}
/>
</Form.Item>
{dealFields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => remove(field.name)}
/>
) : null} */}
<Form.List name="weight" initialValue={[{}]}>
{(weightFields, { add: weightAdd, remove: weightRemove }) => (
<>
{weightFields.map(weightField => (
<Space key={weightField.key} align="baseline" className={styles.conBg}>
<Form.Item
{...weightField}
name={[weightField.name, 'specName']}
rules={[
{
required: true,
message: '请输入名称',
},
]}
>
<Input style={{ width: '200px' }} placeholder="名称" />
</Form.Item>
<Form.Item
{...weightField}
name={[weightField.name, 'quantity']}
rules={[
{
required: true,
message: '份量',
},
]}
>
<Input style={{ width: '200px' }} placeholder="约 份量(数字)" />
</Form.Item>
<span className="ant-form-text"> 约</span>
<Form.Item
{...weightField}
name={[weightField.name, 'unit']}
rules={[
{
type: 'array',
required: true,
message: '请选择单位',
},
]}
>
<Cascader
placeholder="请选择单位"
disabled={customer.isEdit && customer.isNormal}
showSearch={{ filter: filterCategoryOptions }}
fieldNames={{
label: 'name',
value: 'name',
children: 'children',
}}
onChange={props.onCategoryChange}
options={unitsList}
/>
</Form.Item>
<Form.Item
{...weightField}
name={[weightField.name, 'salePrice']}
rules={[
{
required: true,
message: '销售价',
},
]}
>
<Input style={{ width: '200px' }} placeholder="销售价(元)" />
</Form.Item>
<Form.Item
{...weightField}
name={[weightField.name, 'activityPrice']}
rules={[
{
required: true,
message: '活动价',
},
]}
>
<Input style={{ width: '200px' }} placeholder="活动价(元)" />
</Form.Item>
<MinusCircleOutlined onClick={() => weightRemove(weightField.name)} />
</Space>
))}
<Form.Item>
<Button
type="primary"
onClick={() => weightAdd()}
block
style={{ width: '400px' }}
icon={<PlusOutlined />}
>
新增份量
</Button>
</Form.Item>
</>
)}
</Form.List>
{/* </Form.Item>
))} */}
{/* <Form.Item>
<Button
type="primary"
onClick={() => add()}
style={{
width: '400px',
}}
icon={<PlusOutlined />}
>
添加份量
</Button>
</Form.Item> */}
{/* </>
)}
</Form.List> */}
</Form.Item>
{takeawayData?.takeawayItem?.weight?.length > 0 && (
<>
<Form.Item>
<div>添加规格(如加料、甜度、辣度等)</div>
<Form.List name="specs">
{(specsFields, { add, remove }) => (
<>
{specsFields.map((field, index) => (
<Form.Item key={field.key} className={styles.conBg}>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
name={[field.name, 'specGroupName']}
rules={[
{
required: true,
whitespace: true,
message: '请输入规格名称',
},
]}
noStyle
>
<Input placeholder="规格名称" className={styles.nameWidth} />
</Form.Item>
{specsFields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => remove(field.name)}
/>
) : null}
<Form.List
{...field}
name={[field.name, 'specs']}
initialValue={[
{
specName: '',
salePrice: '',
},
]}
>
{(specsInfoFields, { add: specsAdd, remove: specsRemove }) => (
<>
{specsInfoFields.map(specsInfofield => (
<Space key={specsInfofield.key} align="baseline">
<Form.Item
{...specsInfofield}
name={[specsInfofield.name, 'specName']}
rules={[
{
required: true,
message: '请输入加价名称',
},
]}
>
<Input style={{ width: '200px' }} placeholder="加价名称" />
</Form.Item>
<Form.Item
{...specsInfofield}
name={[specsInfofield.name, 'salePrice']}
rules={[
{
required: true,
message: '请输入加价金额(元)',
},
]}
>
<InputNumber
style={{ width: '200px' }}
placeholder="加价名称金额(元)"
/>
</Form.Item>
<MinusCircleOutlined
onClick={() => specsRemove(specsInfofield.name)}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => specsAdd()}
block
style={{ width: '400px' }}
icon={<PlusOutlined />}
>
新增加价
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
))}
<Form.Item>
<Button
type="primary"
onClick={() => add()}
style={{ width: '400px' }}
icon={<PlusOutlined />}
>
新增规格
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
<div className={styles.textStyle}>修改成功后,原库存将被替换,请谨慎操作!</div>
<Form.Item
name="receptionVolume"
label="多规格库存"
className={styles.multiSpecification}
>
<Button
size="small"
danger
style={{ marginBottom: '10px' }}
onClick={() => {
showAddRepertoryModal('all'); // 统一库存
}}
shape="round"
>
统一设置置库存
</Button>
<div className={styles.rowWarp}>
{multiSpu.length > 0 &&
multiSpu?.map((item, idx) => (
<>
{item.unique && (
<>
<div key={idx} className={styles.specsBetween}>
<Form.Item label={calcLabelName(intactData, item)}>
<div className={styles.specsBetween}>
<span className={styles.repertoryLimit}>
{item?.serviceItem?.productStock}/
{item?.serviceItem?.maxStock}
</span>
<div
className={styles.specRepertory}
onClick={() => {
showAddRepertoryModal('multi', `${item.unique}`, item); // 多个库存
}}
>
设置库存{item.unique}
</div>
</div>
</Form.Item>
</div>
</>
)}
</>
))}
</div>
</Form.Item>
</>
)}
</>
)}
</Form>
{/* 加入库存 */}
<AddRepertoryModal
ref={AddRepertoryRef}
modifiedInventory={modifiedInventory}
intactData={intactData}
repertoryModel={repertoryModel}
/>
{/* 加入多规格 */}
<AddMultiSpecModal ref={AddMultiSpecRef} />
</>
);
});
export default TakeawayGoodsInfo;
......@@ -5,7 +5,11 @@ 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) {
props.onChange(task);
......@@ -25,6 +29,7 @@ export const TaskTypeSelect = props => {
>
<dd className="prodcut-name">{task.name}</dd>
<dd className="prodcut-desc">({task.desc})</dd>
<dd className="prodcut-desc">({task.type})</dd>
</dl>
);
})}
......
......@@ -90,6 +90,38 @@ export const TaskList = (canAddService, canAddNormal) => [
},
},
},
{
name: '外卖商品',
type: 5,
desc: '无需物流',
// hide: !canAddService,
imgConfig: {
commonImageList: {
title: '封面图片',
rule: true,
limit: 1,
renderExtra(leng) {
return `建议尺寸: ##宽##高 (${leng} / 1) 封面图第一张 `;
},
},
cardImageList: {
title: '商品图片',
rule: true,
limit: 11,
renderExtra(leng) {
return `建议尺寸: ##宽##高,sku商品轮播图(${leng} / 11)`;
},
},
detailImageList: {
title: '商品详情图',
// rule: true,
limit: 30,
renderExtra() {
return '最多上传30张';
},
},
},
},
];
export const WeeksList = [
......@@ -291,3 +323,18 @@ export const StaticColumns = customer => [
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 = [
{ value: 1, label: '周一' },
{ value: 2, label: '周二' },
{ value: 3, label: '周三' },
{ value: 4, label: '周四' },
{ value: 5, label: '周五' },
{ value: 6, label: '周六' },
{ value: 7, label: '周日' },
{ value: 8, label: '法定假日' },
];
......@@ -9,6 +9,7 @@ import FormRuleSetting from './components/FormRuleSetting';
import FormRuleVPictures from './components/FormRuleVPictures';
import FormSettlementOthers from './components/FormSettlementOthers';
import FormAttr from './components/FormAttr';
import FormTakeaway from './components/FormTakeaway';
import localStorage from '@/utils/localStorage';
import {
merchantBrandList,
......@@ -21,6 +22,7 @@ import {
getByProductType,
apiCreateDraft,
apiEditDraft,
apiAddTakeawayProducts,
} from './service';
import { isUrl, filterSendData, clearCurrent, onAutoSaveValue, localAutoSaveKey } from './utils';
import { ServiceContext } from './context';
......@@ -36,6 +38,8 @@ const ServiceGoods = options => {
const { SourceData, categoryList, virtualCategoryList, specListData, permissions } = options;
const canAddService = permissions[GOOD_MANAGE.ADD_SERVICE_GOODS];
const canAddNormal = permissions[GOOD_MANAGE.ADD_NORMAL_GOODS];
// const canTakeawayService = permissions[GOOD_MANAGE.ADD_TAKEAWAY_GOODS];
// const canTakeawayService = true
const basicRef = useRef(null);
const stockRef = useRef(null);
......@@ -43,6 +47,7 @@ const ServiceGoods = options => {
const picturesRef = useRef(null);
const settleOtrRef = useRef(null);
const attrRef = useRef(null);
const takeawayRef = useRef(null);
const [pageId, setPageId] = useState(null);
const [categoryIds, setCategoryIds] = useState([]); // 商品品类ID
......@@ -57,6 +62,7 @@ const ServiceGoods = options => {
const [specList, setSpecList] = useState([]); // 规格列表
const [editData, setEditData] = useState({}); // 编辑保存数据
const [newCategoryList, setNewCategoryList] = useState({});
const [visibleCacheEdit, setVisibleCacheEdit] = useState(false); // 显示有缓存未保存提示
const [checkFormList] = useState([
basicRef,
......@@ -65,6 +71,7 @@ const ServiceGoods = options => {
settingRef,
settleOtrRef,
picturesRef,
takeawayRef,
]);
const [specKeyList, setSpecKeyList] = useState([]); // 记录一级规格key字段
......@@ -72,6 +79,7 @@ const ServiceGoods = options => {
const resetForm = () => clearCurrent(checkFormList).forEach(({ current }) => current.reset());
const onValuesChange = e => {
console.log('e======', e, !isEdit, visibleCacheEdit);
if (!isEdit) {
if (visibleCacheEdit) {
setVisibleCacheEdit(false);
......@@ -146,7 +154,6 @@ const ServiceGoods = options => {
setSpecList(result.data || []);
}
};
const getAfterSalesAddrsPage = async () => {
if (!afterAddressList.length) {
const result = await afterSalesAddrsPage();
......@@ -196,10 +203,18 @@ const ServiceGoods = options => {
console.log('resuslt :>> ', resuslt);
if (!resuslt.includes(null)) {
const params = resuslt.reduce((origin, item) => {
console.log('origin', origin, item);
const { temp, ...other } = item;
origin[temp] = other;
return origin;
}, {});
// 外卖商品创建
if (+productType === 5) {
console.log('parmas', params);
const res = await sendMerchantProductHttpRequest(params?.takeawayItem?.intactData);
return false;
}
const sendData = filterSendData(productType, params);
if (isEdit) {
sendData.id = pageId;
......@@ -396,14 +411,16 @@ const ServiceGoods = options => {
},
});
};
console.log('productType', productType);
const providerValue = {
pageId,
isEdit,
productType,
canAddService, // 是否可以添加服务商品(电子卡券)
canAddNormal, // 是否可以添加实物商品
// canTakeawayService, // 是否可以添加外卖商品
isCard: productType === 4,
isTakeawayService: productType === 5,
// 0, "商品删除" 1, "新建" 2, "提交审核" 3, "待审核" 4, "驳回" 5, "未上架" 6, "已上架" 7, "已下架"
isNormal: SourceData.state && SourceData.state !== 4, // 商品不是驳回状态
// 当商品进行编辑 & 类型不为电子卡券 & 商品状态不为驳回 禁用当前功能
......@@ -460,6 +477,7 @@ const ServiceGoods = options => {
specListData={specListData}
onCategoryChange={onCategoryChange}
onValuesChange={onValuesChange}
// queryShopList={queryShopList}
/>
{[1, 2].includes(productType) && [
......@@ -472,32 +490,43 @@ const ServiceGoods = options => {
onValuesChange={onValuesChange}
/>,
]}
<Title title="价格与库存" />
<FormPriceOrStock
ref={stockRef}
specList={specList}
onSpecChange={onSpecCommonImgEvent}
editData={editData.infoSpecData}
skuList={editData.skuList}
onValuesChange={onValuesChange}
/>
<Title title="规则设置" />
{productType === 4 && (
<FormRuleSetting
ref={settingRef}
editData={editData.serviceItem}
supplierIdList={supplierIdList}
onValuesChange={onValuesChange}
/>
{productType !== 5 && (
<>
<Title title="价格与库存" />
<FormPriceOrStock
ref={stockRef}
specList={specList}
onSpecChange={onSpecCommonImgEvent}
editData={editData.infoSpecData}
skuList={editData.skuList}
onValuesChange={onValuesChange}
/>
</>
)}
{productType !== 5 && (
<>
<Title title="规则设置" />
{productType === 4 && (
<FormRuleSetting
ref={settingRef}
editData={editData.serviceItem}
supplierIdList={supplierIdList}
onValuesChange={onValuesChange}
/>
)}
</>
)}
{productType !== 5 && (
<>
<FormRuleVPictures
ref={picturesRef}
specKeyItem={specKeyList}
editData={editData.infoImageData}
onValuesChange={onValuesChange}
/>
</>
)}
<FormRuleVPictures
ref={picturesRef}
specKeyItem={specKeyList}
editData={editData.infoImageData}
onValuesChange={onValuesChange}
/>
{productType === 4 && (
<FormSettlementOthers
ref={settleOtrRef}
......@@ -505,6 +534,17 @@ const ServiceGoods = options => {
onValuesChange={onValuesChange}
/>
)}
{productType === 5 && (
<>
<FormTakeaway
ref={takeawayRef}
editData={editData.takeawayItem}
infoMation={editData.infoMation}
supplierIdList={supplierIdList}
onValuesChange={onValuesChange}
/>
</>
)}
</ServiceContext.Provider>
</WrapperContainer>
</Spin>
......
......@@ -117,3 +117,53 @@ 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/57324
export const apiAddTakeawayProducts = data =>
request.post('/v1/channels/products/add', {
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', {
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',
},
});
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