Commit d98ff0ad authored by beisir's avatar beisir

feat: 初始化服务商品改造

parent a02c476b
...@@ -206,11 +206,12 @@ export default { ...@@ -206,11 +206,12 @@ export default {
icon: 'smile', icon: 'smile',
component: './password', component: './password',
}, },
// { {
// path: '/GoodsManage-new', title: '服务商品改造-商品模块',
// name: 'GoodsManageNew', path: '/ServiceGoods/:id',
// component: './GoodsManage-new', name: 'ServiceGoods',
// }, component: './ServiceGoods/index',
},
{ {
component: './404', component: './404',
}, },
......
This diff is collapsed.
.header {
:global {
.ant-pro-page-header-wrap-children-content {
margin: 0;
}
}
}
.container {
padding: 0 10px;
background-color: #fff;
}
.title {
position: relative;
height: 44px;
margin-bottom: 10px;
padding-left: 30px;
line-height: 44px;
background-color: #f8f8f8;
&::before {
position: absolute;
top: 12px;
left: 15px;
width: 4px;
height: 20px;
background-color: #319bfe;
content: '';
}
}
.prodcutContent {
display: flex;
padding-bottom: 10px;
}
.productCard {
margin-bottom: 0;
margin-left: 20px;
padding: 10px 20px 5px 20px;
text-align: center;
border: solid #ccc 1px;
border-radius: 5px;
cursor: pointer;
:global {
.prodcut-name {
font-weight: bold;
}
.prodcut-desc {
color: #bbb;
}
}
}
.activeCard {
position: relative;
overflow: hidden;
border-color: #165cd3;
&::after {
position: absolute;
right: -15px;
bottom: -15px;
width: 30px;
height: 30px;
background-color: #165cd3;
transform: rotate(45deg);
content: '';
}
:global {
.prodcut-name {
color: #165cd3;
font-weight: bold;
}
}
}
import React from 'react';
import commonStyle from '../common.less';
export const WrapperContainer = props => (
<div className={commonStyle.container}>{props.children}</div>
);
/**
* title 组件
* value 可以传入多种类型的值
* onChange只会回调 number|undefined 类型
* 当isNaN(Number(value)) 为true的时候,代表选择默认类型
* 当选择默认类型的时候,onChange会回调undefined
* @param props
*/
export const Title = props => (
<div className={commonStyle.title}>
<h3>{props.title}</h3>
</div>
);
// export const IdSelect = (props: IdSelectProps) => {
// const {value, onChange, defaultOptionName, options, ...resetProps} = props;
// return <Select {...resetProps} value={options?.length ? toNumber(value): 0} onChange={value => onChange?.(toNumber(value) || undefined)}>
// {
// defaultOptionName ? <Select.Option value={0}>{defaultOptionName}</Select.Option> : null
// }
// {options?.map(option => <Select.Option key={option.id} value={option.id}>{option.name}</Select.Option>)}
// </Select>
// }
// const toNumber = (value: unknown) => isNaN(Number(value)) ? 0: Number(value);
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
import { Cascader, Form, Input, Select } from 'antd';
import { useParams } from 'react-router-dom';
import { DefaultOptionType } from 'antd/lib/select';
interface CategoryItem {
id: number;
level: number;
name: string;
parentId: number;
children?: CategoryItem[];
}
interface BrandItem {
id: number;
name: string;
chineseName?: string;
chinesePinyin?: string;
englishName?: string;
firstLetter?: string;
horizontalLogo?: string;
logo?: string;
priority?: string;
}
interface PropsType {
editData: any;
categoryList: Array<CategoryItem>;
brandList: BrandItem[];
}
const categoryIdArrayList = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
];
const formItemLayout = {
labelCol: {
sm: { span: 3 },
},
wrapperCol: {
sm: { span: 16 },
},
};
const CreateSelectOption = (optionList: BrandItem[]) =>
optionList.map((brandItem: BrandItem) => (
<Select.Option key={brandItem.id} value={brandItem.id}>
{brandItem.name}
</Select.Option>
));
const fileterBrandOptions = (
input: string,
options: { key: string; value: number; children: string },
) => options.children.includes(input);
const filterCategoryOptions = (inputValue: string, path: DefaultOptionType[]) =>
path.some(option => option.name.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
const FormInformationBasic = forwardRef((props: PropsType, ref) => {
const { editData, categoryList, brandList } = props;
const [form] = Form.useForm();
const onCheck = async () => {
try {
const values = await form.validateFields();
return values;
} catch (errorInfo) {
return null;
}
};
useImperativeHandle(ref, () => ({
onCheck,
}));
const { id } = useParams<any>();
useEffect(() => {
if (+id !== 0) {
if (!Object.keys(editData).length) return;
const { serviceItem } = editData.skuList[0];
form.setFieldsValue({
brandId: editData.brandId,
supplierId: editData.supplierId,
name: editData.name,
categoryId: [editData.firstCategoryId, editData.secondCategoryId, editData.thirdCategoryId],
description: serviceItem.description,
});
}
}, [id, editData]);
const onFinish = (values: any) => {
console.log('Received values of form: ', values);
};
return (
<Form
{...formItemLayout}
form={form}
name="register"
onFinish={onFinish}
initialValues={{
brandId: null,
supplierId: '',
name: '',
categoryId: [],
description: '',
}}
scrollToFirstError
>
<Form.Item
name="brandId"
label="供应商名称"
rules={[{ required: true, message: '请选择供应商名称!' }]}
>
<Select showSearch placeholder="请选择供应商名称" filterOption={fileterBrandOptions}>
{CreateSelectOption(brandList)}
</Select>
</Form.Item>
<Form.Item
name="name"
label="商品名称"
rules={[{ required: true, message: '请输入商品名称!', whitespace: true }]}
>
<Input placeholder="请输入商品名称" />
</Form.Item>
<Form.Item
name="categoryId"
label="商品类目"
rules={[{ type: 'array', required: true, message: '请输入商品类目!' }]}
>
<Cascader
placeholder="请选择商品类目!"
showSearch={{ filter: filterCategoryOptions }}
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
options={categoryList}
/>
</Form.Item>
<Form.Item
name="description"
label="描述"
rules={[{ required: true, message: '请输入描述!' }]}
>
<Input.TextArea showCount maxLength={100} placeholder="请输入描述!" />
</Form.Item>
</Form>
);
});
export default FormInformationBasic;
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Form, Input, Select, Space } from 'antd';
import React, { useState, forwardRef, useImperativeHandle } from 'react';
const { Option } = Select;
const areas = [{ label: 'Beijing', value: 'Beijing' }, { label: 'Shanghai', value: 'Shanghai' }];
const sights = {
Beijing: ['Tiananmen', 'Great Wall'],
Shanghai: ['Oriental Pearl', 'The Bund'],
};
// type SightsKeys = keyof typeof sights;
const FormPriceOrStock = forwardRef((props, ref) => {
const [form] = Form.useForm();
const onFinish = (values: any) => {
console.log('Received values of form:', values);
};
const handleChange = () => {
form.setFieldsValue({ sights: [] });
};
const onCheck = async () => {
try {
const values = await form.validateFields();
return values;
} catch (errorInfo) {
return null;
}
};
useImperativeHandle(ref, () => ({
onCheck,
}));
return (
<Form
form={form}
name="dynamic_form_nest_item"
onFinish={onFinish}
autoComplete="off"
initialValues={{
area: 'Beijing',
}}
>
{/* <Form.Item name="area" label="Area" rules={[{ required: true, message: 'Missing area' }]}>
<Select options={areas} onChange={handleChange} />
</Form.Item> */}
<Form.List name="sights">
{(fields, { add, remove }) => (
<>
{fields.map(field => (
<Form.Item key={field.key} noStyle shouldUpdate={(prevValues, curValues) => false}>
{() => (
<Space key={field.key} align="baseline">
<Form.Item
label="Sight"
name={[field.name, 'sight']}
rules={[{ required: true, message: 'Missing sight' }]}
>
<Select disabled={false} style={{ width: 130 }}>
{(sights[form.getFieldValue('area')] || []).map(item => (
<Option key={item} value={item}>
{item}
</Option>
))}
</Select>
</Form.Item>
<Form.Item
label="Price"
name={[field.name, 'price']}
rules={[{ required: true, message: 'Missing price' }]}
>
<Input />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
)}
</Form.Item>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Add sights
</Button>
</Form.Item>
</>
)}
{/* {(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' }]}
>
<Select disabled={!form.getFieldValue('area')} style={{ width: 130 }}>
{(sights[form.getFieldValue('area') as SightsKeys] || []).map(item => (
<Option key={item} value={item}>
{item}
</Option>
))}
</Select>
</Form.Item>
)}
</Form.Item>
<Form.Item
{...field}
label="Price"
name={[field.name, 'price']}
rules={[{ required: true, message: 'Missing price' }]}
>
<Input />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Add sights
</Button>
</Form.Item>
</>
)} */}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
});
export default FormPriceOrStock;
import { Form, Input, Select, Checkbox, DatePicker, Radio, InputNumber } from 'antd';
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
import moment from 'moment';
import { useParams } from 'react-router-dom';
import UploadImage from './UploadImage';
import { WeeksList } from '../config';
const { Option } = Select;
const { RangePicker } = DatePicker;
const formItemLayout = {
labelCol: {
sm: { span: 3 },
},
wrapperCol: {
sm: { span: 16 },
},
};
const WeekCheckBox = () =>
WeeksList.map(item => (
<Checkbox key={item.value} value={item.value} style={{ lineHeight: '32px' }}>
{item.name}
</Checkbox>
));
const formatTime = (time, crm = 'YYYY-MM-DD HH') => time.format(crm);
const resetTime = (time, crm = 'YYYY-MM-DD HH') => moment(time, crm);
const rangeConfig = {
rules: [{ type: 'array', required: true, message: 'Please select time!' }],
};
const FormRuleSetting = forwardRef((props: { editData: any }, ref) => {
const [form] = Form.useForm();
const { editData } = props;
const url = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png';
const { id } = useParams<any>();
const [imageList, setImageList] = useState([]);
const [commonImageList, setCommonImageList] = useState([]);
const [detailImageList, setDetailImageList] = useState([]);
useEffect(() => {
console.log(id);
if (+id !== 0) {
console.log();
if (!Object.keys(editData).length) return;
setImageList(editData.commonImageList);
setCommonImageList(editData.commonImageList);
setDetailImageList(editData.detailImageList);
const { serviceItem } = editData.skuList[0];
form.setFieldsValue({
useTime: [resetTime(serviceItem.useStartTime), resetTime(serviceItem.useEndTime)],
purchaseTime: [
resetTime(serviceItem.purchaseStartTime),
resetTime(serviceItem.purchaseEndTime),
], // 购买开始时间
shopIds: serviceItem.shopIds || [], // 适用门店列表
unavailableDate: serviceItem.unavailableDate, // 不可用日期
useTimeDescription: serviceItem.useTimeDescription, // 使用时间
useMethod: serviceItem.useMethod, // 使用方法
ruleDescription: serviceItem.ruleDescription, // 规则说明
applyScope: serviceItem.applyScope, // 适用范围
tips: serviceItem.tips, // 温馨提示
imageList: editData.commonImageList,
commonImageList: editData.commonImageList,
detailImageList: editData.detailImageList,
appointment: '0', // 预约
});
}
}, [id, editData]);
const onCheck = async () => {
try {
const { useTime, purchaseTime, ...values } = await form.validateFields();
return {
useStartTime: formatTime(useTime[0]),
useEndTime: formatTime(useTime[1]),
purchaseStartTime: formatTime(purchaseTime[0]),
purchaseEndTime: formatTime(purchaseTime[1]),
...values,
};
} catch (errorInfo) {
return null;
}
};
useImperativeHandle(ref, () => ({
onCheck,
}));
const onFinish = (values: any) => {
console.log('Received values of form: ', values);
};
return (
<>
<Form
{...formItemLayout}
form={form}
name="register"
onFinish={onFinish}
initialValues={{
useTime: [], // 使用开始时间
// useEndTime: '', // 使用结束时间
purchaseTime: [], // 购买开始时间
// purchaseEndTime: '2022-07-27 06', // 购买结束时间
shopIds: [], // 适用门店列表
unavailableDate: [], // 不可用日期
useTimeDescription: '', // 使用时间
useMethod: '', // 使用方法
ruleDescription: '', // 规则说明
applyScope: '', // 适用范围
tips: '', // 温馨提示
imageList,
commonImageList,
detailImageList,
appointment: '', // 预约
}}
scrollToFirstError
>
<Form.Item name="useTime" label="购买时间" {...rangeConfig}>
<RangePicker showTime format="YYYY-MM-DD HH" />
</Form.Item>
<Form.Item name="purchaseTime" label="有效期" {...rangeConfig}>
<RangePicker showTime format="YYYY-MM-DD HH" />
</Form.Item>
<Form.Item
name="shopIds"
label="适用门店"
rules={[{ required: true, message: '请选择适用门店!', type: 'array' }]}
>
<Select mode="multiple" placeholder="请选择适用门店">
<Option value={1}>Male</Option>
<Option value={2}>Female</Option>
<Option value={3}>Other</Option>
</Select>
</Form.Item>
<Form.Item
name="unavailableDate"
label="不可用日期"
rules={[{ required: true, message: '请选择不可用日期!', type: 'array' }]}
>
<Checkbox.Group>{WeekCheckBox()}</Checkbox.Group>
</Form.Item>
<Form.Item
name="useTimeDescription"
label="使用时间"
rules={[{ required: false, message: '请输入描述!' }]}
>
<Input.TextArea
showCount
maxLength={100}
placeholder="例如:11:00-12:00;14:00-17:00可用,其他时间不可用"
/>
</Form.Item>
<Form.Item
name="useMethod"
label="使用方法"
rules={[{ required: false, message: '请输使用方法,200字以内!' }]}
>
<Input.TextArea showCount maxLength={200} placeholder="请输使用方法,200字以内!" />
</Form.Item>
<Form.Item
name="ruleDescription"
label="规则说明"
rules={[{ required: false, message: '请输规则说明,200字以内!' }]}
>
<Input.TextArea showCount maxLength={200} placeholder="请输规则说明,200字以内!" />
</Form.Item>
<Form.Item
name="applyScope"
label="适用范围"
rules={[{ required: false, message: '请输入适用范围' }]}
>
<Input.TextArea
showCount
maxLength={50}
placeholder="请输入适用范围,50字以内 例如:全场通用"
/>
</Form.Item>
<Form.Item
name="tips"
label="温馨提示"
rules={[{ required: false, message: '请输入温馨提示' }]}
>
<Input.TextArea
showCount
maxLength={200}
placeholder="请输入温馨提示,200字以内 例如:全场通用例如:\n不兑零"
/>
</Form.Item>
<Form.Item
label="封面图片"
name="commonImageList"
extra={`建议尺寸: ##宽##高 (${commonImageList.length} / 1) `}
rules={[
{ required: true, type: 'array', message: '请输入温馨提示', validateTrigger: 'submit' },
]}
>
<UploadImage
name="commonImageList"
limit={1}
pictures={commonImageList}
setPictureList={setCommonImageList}
/>
</Form.Item>
<Form.Item
label="商品图片"
name="imageList"
extra={`建议尺寸: ##宽##高 (${imageList.length} / 11) `}
rules={[
{ required: true, type: 'array', message: '请输入温馨提示', validateTrigger: 'submit' },
]}
>
<UploadImage
name="imageList"
limit={11}
pictures={imageList}
setPictureList={setImageList}
/>
</Form.Item>
<Form.Item
label="商品详情图"
name="detailImageList"
extra={`最多上传30张,${detailImageList.length} / 30`}
rules={[
{ required: true, type: 'array', message: '请输入温馨提示', validateTrigger: 'submit' },
]}
>
<UploadImage
name="detailImageList"
limit={4}
pictures={detailImageList}
setPictureList={setDetailImageList}
/>
</Form.Item>
<Form.Item name="appointment" label="预约">
<Radio.Group>
<Radio value="1"></Radio>
<Radio value="0"></Radio>
</Radio.Group>
</Form.Item>
<Form.Item
name="meiri"
label="每日最低接待量"
rules={[{ required: true, message: '每日最低接待量' }]}
>
<InputNumber min={0} style={{ width: 200 }} placeholder="请输入每日最低接待量" />
</Form.Item>
</Form>
</>
);
});
export default FormRuleSetting;
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
import { Form, Input, Select, Checkbox, Radio, Space, InputNumber, Switch } from 'antd';
import { useParams } from 'react-router-dom';
import { Title } from './CommonTemplate';
import { formItemLayout } from '../config';
interface PropsType {
editData: any;
}
const FormSettlementOthers = forwardRef((props: PropsType, ref) => {
const { editData } = props;
const [form] = Form.useForm();
const onCheck = async () => {
try {
const values = await form.validateFields();
return {
...values,
limitPurchase: values.limitPurchase ? 1 : 0,
};
} catch (errorInfo) {
return null;
}
};
useImperativeHandle(ref, () => ({
onCheck,
}));
const { id } = useParams<any>();
const [initValue, setInitValue] = useState({
settlementMethod: 1,
limitPurchase: null, // 是否限购1:限购/0:不限购
limitPurchaseType: null, // 限购类型,如果限购必填1:长期限购/2:周期限购
limitPurchaseCycle: null, // 限购周期1:每天/7:7天/30:30天
limitPurchaseQuantity: null, // 限购数量
packageContent: '',
});
useEffect(() => {
if (+id !== 0) {
if (!Object.keys(editData).length) return;
const { serviceItem } = editData.skuList[0];
const data = {
settlementMethod: 1,
limitPurchase: Boolean(serviceItem.limitPurchase), // 是否限购1:限购/0:不限购
limitPurchaseType: serviceItem.limitPurchaseType, // 限购类型,如果限购必填1:长期限购/2:周期限购
limitPurchaseCycle: serviceItem.limitPurchaseCycle, // 限购周期1:每天/7:7天/30:30天
limitPurchaseQuantity: serviceItem.limitPurchaseQuantity, // 限购数量
packageContent: serviceItem.packageContent,
};
form.setFieldsValue(data);
setInitValue({ ...data });
}
}, [id, editData]);
useEffect(() => {}, [form]);
const onFinish = (values: any) => {
console.log('Received values of form: ', values);
};
const radioChangeEvent = (key: string) => {
const value = form.getFieldValue(key);
setInitValue({
...initValue,
[key]: value,
});
};
const AuditLimitPurchaseType = () => {
const limitPurchaseType: number = form.getFieldValue('limitPurchaseType');
if (limitPurchaseType === 1) {
return null;
}
return (
<Form.Item name="limitPurchaseCycle" rules={[{ required: true, message: '请选择限购周期' }]}>
<Select placeholder="请选择限购周期" style={{ width: 150 }}>
<Select.Option value={1}>每天</Select.Option>
<Select.Option value={7}>7天</Select.Option>
<Select.Option value={30}>30天</Select.Option>
</Select>
</Form.Item>
);
};
const AuditLimitPurchaseTemplate = () => {
if (!initValue.limitPurchase) {
return null;
}
const PurchaseTemplate =
initValue.limitPurchaseType !== null ? (
<Form.Item wrapperCol={{ offset: 3, span: 16 }}>
<Space>
{AuditLimitPurchaseType()}
<Form.Item
name="limitPurchaseQuantity"
rules={[{ required: initValue.limitPurchase, message: '请输入限购数量' }]}
>
<InputNumber placeholder="请输入限购数量" style={{ width: 150 }} />
</Form.Item>
</Space>
</Form.Item>
) : null;
return (
<>
<Form.Item
name="limitPurchaseType"
wrapperCol={{ offset: 3, span: 16 }}
rules={[{ required: true, message: '请选择限购类型' }]}
>
<Radio.Group onChange={() => radioChangeEvent('limitPurchaseType')}>
<Space direction="vertical">
<Radio value={1}>每ID限购</Radio>
<Radio value={2}>按周期限购</Radio>
</Space>
</Radio.Group>
</Form.Item>
{PurchaseTemplate}
</>
);
};
return (
<Form
{...formItemLayout}
form={form}
name="register"
onFinish={onFinish}
initialValues={initValue}
scrollToFirstError
>
<Title title="结算信息" />
<Form.Item
name="packageContent"
label="套餐内容"
rules={[{ required: true, message: '请输入套餐内容!' }]}
>
<Input.TextArea showCount maxLength={100} placeholder="请输入套餐内容!" />
</Form.Item>
<Form.Item
name="settlementMethod"
label="结算方式"
rules={[{ required: true, message: '请输入套餐内容!' }]}
extra="自动分账: 合同期内订单结算款实时分账到甲方指定账号。"
>
<span style={{ color: 'rgba(0, 0, 0, 0.45)' }}>(默认)</span>
</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>
{AuditLimitPurchaseTemplate()}
</Form>
);
});
export default FormSettlementOthers;
import React from 'react';
import { TaskList } from '../config';
import { Task } from '../type';
import commonStyle from '../common.less';
export const TaskTypeSelect = (props: { productType: number; onChange: (task: Task) => void }) => (
<div className={commonStyle.prodcutContent}>
{TaskList.map((task: Task) => {
const activeClassName: string = props.productType === task.type ? commonStyle.activeCard : '';
return (
<dl
key={task.type}
onClick={() => props.onChange(task)}
className={`${commonStyle.productCard} ${activeClassName}`}
>
<dd className="prodcut-name">{task.name}</dd>
<dd className="prodcut-desc">({task.desc})</dd>
</dl>
);
})}
</div>
);
import { PlusOutlined } from '@ant-design/icons';
import { Modal, Upload, notification, Spin } from 'antd';
import type { UploadProps } from 'antd/es/upload';
import type { UploadFile } from 'antd/es/upload/interface';
import React, { useState, useEffect, useRef } from 'react';
import lodash from 'lodash';
import { merchantUpload } from '../service';
type PicturesType = {
name?: string;
limit?: number;
uploadParams?: UploadProps;
pictures?: Array<string>;
onChange?: (path: string[]) => void;
setPictureList?: (item: string[]) => void
};
const MAX_FILE_SIZE = 5;
const UNIT = 1024 * 1024;
// const getBase64 = (file: RcFile): Promise<string> =>
// new Promise((resolve, reject) => {
// const reader = new FileReader();
// reader.readAsDataURL(file);
// reader.onload = () => resolve(reader.result.toString());
// reader.onerror = error => reject(error);
// });
const uploadButton = (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
const UploadImage: React.FC<PicturesType> = props => {
const {
name = `${Date.now()}`,
limit = 1,
uploadParams,
pictures = [],
onChange = () => {},
setPictureList = () => {},
} = props;
const [uploadLoading, setUploadLoading] = useState(false);
const [previewVisible, setPreviewVisible] = useState(false);
const [previewImage, setPreviewImage] = useState('');
const [previewTitle, setPreviewTitle] = useState('');
const [fileList, setFileList] = useState<UploadFile[]>([]);
const fileListRef = useRef([]);
useEffect(() => {
const newPictures = pictures.map((url: string, ind: number) => ({
url,
name: url,
uid: `${ind}`,
}));
fileListRef.current = [...newPictures];
setFileList([...newPictures]);
}, [pictures]);
const handleCancel = () => setPreviewVisible(false);
const handlePreview = async (file: UploadFile) => {
setPreviewImage(file.url);
setPreviewVisible(true);
setPreviewTitle(file.name || (file.url).substring(file.url.lastIndexOf('/') + 1));
};
const bundleChange = imgFile => {
const imgList = imgFile.map(item => item.url);
setPictureList(imgList);
onChange(imgList);
};
const handleRemove = file => {
const freshFiles = fileList?.filter(ele => ele.uid !== file.uid);
bundleChange(freshFiles);
}
// const saveFiles = (file, ret) => {
// return new Promise((resolve) => {
// const reader = new FileReader();
// // 监听图片转换完成
// reader.addEventListener('load',
// () => {
// // ...接入antd upload 的 filelist 中
// // if (typeof setPictureList === 'function') {
// const temFile = { uid: file.uid, status: 'done', name: file.name, url: ret };
// let newFiles = [...fileListRef.current, temFile];
// if (!uploadParams.multiple) {
// // 只保留最后一个文件
// newFiles = newFiles.slice(-1);
// }
// bundleChange(newFiles);
// // }
// },
// false,
// );
// reader.readAsDataURL(file);
// })
// // 图片转换成base64编码作为缩略图
// };
// const customRequestObject = async params => {
// console.log(fileList.length);
// const { file, onSuccess, onError } = params;
// try {
// const res = await merchantUpload([file]);
// const [url] = res.data;
// onSuccess();
// saveFiles(file, url);
// setFileList(preFileList => preFileList.map(item => id === item.uid ? { ...item, status: 'done', url, percent: 100,} : item));
// } catch(error) {
// onError();
// console.log(error);
// }
// };
const checkFile = file => {
console.log(file);
return new Promise(resolve => {
const curType = file.name.substr(file.name.lastIndexOf('.') + 1).toLowerCase();
const fileType = ['jpg', 'jpeg', 'png'];
if (!fileType.includes(curType)) {
notification.open({
message: file.name,
description: '图片格式须为jpg、jpeg、png!',
});
return resolve(null);
}
if (file.size > MAX_FILE_SIZE * UNIT) {
notification.open({
message: file.name,
description: `单个图片大小不能超过${MAX_FILE_SIZE}M!`,
});
return resolve(null);
}
return resolve(file);
});
}
const imageLoading = (file, ret) => new Promise(resolve => {
const reader = new FileReader();
// 监听图片转换完成
reader.addEventListener('load',
() => {
const temFile = { uid: file.uid, status: 'done', name: file.name, url: ret };
resolve(temFile);
},
false,
);
reader.readAsDataURL(file);
})
const defaultBeforeUpload = lodash.debounce((file, fileArray) =>
// 文件显示
new Promise(async () => {
if ((fileListRef.current.length + fileArray.length) > limit) {
Modal.warning({
maskClosable: true,
title: '超出上传个数',
});
return Upload.LIST_IGNORE;
}
const fileAll = fileArray.map(item => checkFile(item));
const checkFiles = (await Promise.all(fileAll)).filter(item => item !== null);
try {
if (checkFiles.length) {
setUploadLoading(true);
const res = await merchantUpload(checkFiles);
const proFiles = (res.data || []).map((urlItem, urlIndex) => imageLoading(checkFiles[urlIndex], urlItem));
const imagList = await Promise.all(proFiles);
const newFiles = [...fileListRef.current, ...imagList];
bundleChange(newFiles);
setUploadLoading(false);
}
} catch (error) {
setUploadLoading(false);
Modal.warning({
maskClosable: true,
title: '上传失败,请重新尝试!',
});
}
return null;
}),
);
return (
<Spin tip="正在上传..." spinning={uploadLoading} delay={100}>
<Upload
{...uploadParams}
multiple={limit > 1}
name={name}
customRequest={() => {}}
listType="picture-card"
beforeUpload={defaultBeforeUpload}
fileList={fileList}
onPreview={handlePreview}
onRemove={handleRemove}
>
{fileList.length >= props.limit ? null : uploadButton}
</Upload>
<Modal visible={previewVisible} title={previewTitle} footer={null} onCancel={handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</Spin>
);
};
export default UploadImage;
export const formItemLayout = {
labelCol: {
sm: { span: 3 },
},
wrapperCol: {
sm: { span: 16 },
},
};
export const TaskList = [
{
name: '实体商品',
type: 1,
desc: '物流发货',
},
{
name: '虚拟商品',
type: 2,
desc: '无需物流',
},
{
name: '电子卡卷',
type: 3,
desc: '无需物流',
},
{
name: '服务类商品',
type: 4,
desc: '无需物流',
},
];
export const WeeksList = [
{
name: '法定节假日',
value: 8,
},
{
name: '周一',
value: 1,
},
{
name: '周二',
value: 2,
},
{
name: '周三',
value: 3,
},
{
name: '周四',
value: 4,
},
{
name: '周五',
value: 5,
},
{
name: '周六',
value: 6,
},
{
name: '周日',
value: 7,
},
];
import React, { useState, useRef, useEffect } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { Spin, Button } from 'antd';
import { ConsoleSqlOutlined } from '@ant-design/icons';
// import { getAppChannelAllList } from '@/services/common';
import { Title, WrapperContainer } from './components/CommonTemplate';
import { TaskTypeSelect } from './components/TaskTypeSelect';
import { Task } from './type';
import FormInformationBasic from './components/FormInformationBasic';
import FormPriceOrStock from './components/FormPriceOrStock';
import FormRuleSetting from './components/FormRuleSetting';
import FormSettlementOthers from './components/FormSettlementOthers';
import commonStyle from './common.less';
import { getProductDetail, merchantCategoryGetAll, merchantBrandList } from './service';
/**
* 服务商品改造-商品模块
* @param {*} router options
* @returns ReactDOM
*/
const ServiceGoods = options => {
const pageId = +options.match.params.id; // pageId: 0-新增,1-修改,2-查看
const ref = useRef();
const basicRef = useRef(null);
const stockRef = useRef(null);
const settingRef = useRef(null);
const settleOtrRef = useRef(null);
const [productType, setProductType] = useState(1);
const [pageLoading, setPageLoading] = useState(false);
const [categoryList, setCategoryList] = useState([]); // 获取三级类目
const [brandList, setBrandList] = useState([]); // 获取商品牌
const [examLoading, setExamLoading] = useState(false);
const [channelAllList, setChannelAllList] = useState({});
const [editData, setEditData] = useState({});
// const refreshTable = () => {
// ref.current.reload();
// };
// useEffect(() => {
// getChannelAllList();
// }, []);
// const onReset = () => {};
const productChange = (task: Task): void => {
setProductType(task.type);
};
const submitEvent = async () => {
const resuslt = await Promise.all([
basicRef.current.onCheck(),
stockRef.current.onCheck(),
settingRef.current.onCheck(),
settleOtrRef.current.onCheck(),
]);
console.log(resuslt);
};
const getProductDetailResponse = async () => {
try {
const res = await getProductDetail(pageId);
if (res && res.data) {
setProductType(res.data.type);
setEditData(res.data);
}
} catch (err) {
console.log('接口调用失败!');
}
};
// 获取三级类目分类数据
const getMerchantCategory = async () => {
const result = await merchantCategoryGetAll();
setCategoryList(result.data || []);
};
// 获取商品牌数据
const getMerchantBrandList = async () => {
const result = await merchantBrandList();
setBrandList(result.data || []);
};
useEffect(() => {
(async () => {
setPageLoading(true);
await getMerchantCategory();
await getMerchantBrandList();
if (pageId !== 0) {
await getProductDetailResponse();
}
setPageLoading(false);
})();
}, [pageId]);
return (
<Spin tip="正在加载..." spinning={pageLoading} delay={100}>
<PageHeaderWrapper className={commonStyle.header}>
<WrapperContainer>
<Title title="商品类型" />
<TaskTypeSelect productType={productType} onChange={productChange} />
<Title title="商品基本信息编辑" />
<FormInformationBasic
ref={basicRef}
editData={editData}
categoryList={categoryList}
brandList={brandList}
/>
<Title title="价格与库存" />
<FormPriceOrStock ref={stockRef} initValue={editData} />
<Title title="规则设置" />
<FormRuleSetting ref={settingRef} editData={editData} />
<FormSettlementOthers ref={settleOtrRef} editData={editData} />
<Button type="primary" onClick={submitEvent}>
Register
</Button>
</WrapperContainer>
</PageHeaderWrapper>
</Spin>
);
};
export default ServiceGoods;
import request from '@/utils/request';
import config from '../../../config/env.config';
import { stringify } from 'qs';
import _ from 'lodash';
const { goodsApi, kdspApi } = config;
export const merchantUpload = async files => {
const params = new FormData();
files.forEach(file => params.append('file', file));
const data = await request.post('/image/api/merchant/upload', {
prefix: goodsApi,
data: params,
});
return data;
};
export const getProductDetail = id =>
request.post('/mock/389/product/detail', {
prefix: 'http://yapi.quantgroups.com',
data: { id },
});
export const merchantCategoryGetAll = () =>
request.post('/product/category/api/merchant/getAll', {
prefix: goodsApi,
});
// 获取商品品牌
export const merchantBrandList = () =>
request.post('/product/brand/api/merchant/list', {
prefix: goodsApi,
});
export interface Task {
name: string;
type: number;
desc: string;
}
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