提交 7cece82e 作者: 龚洪江

功能:商城商品sku修改

上级 59067fe9
......@@ -86,7 +86,7 @@ export type editGoodsType = InterFunction<
export type detailGoodsType = InterFunction<
{ goodsInfoId: number; type: number; leaseTerm?: number },
BaseInfoType & {
goodsSpec: specEntity[];
goodsSpec?: specEntity[];
goodsDetail: { goodsDesc: string; productDesc: string; content: string };
otherService: { id: number; saleServiceName: string; saleServiceId: number }[];
goodsVideoId: number;
......@@ -143,6 +143,15 @@ export type addMallGoodsType = InterFunction<
}[];
shelfStatus: number;
tradeName: string;
priceStock: {
channelPrice: number;
id?: number;
productSpec: string;
salePrice: number;
skuImage: string;
skuNo: string;
stock: number;
}[];
},
any
>;
......
import { Button, Popconfirm, Table, Tag } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { ColumnsType } from 'antd/es/table';
import { FC, useState } from 'react';
import { InterDataType, InterReqType, PaginationProps } from '~/api/interface';
import { addMallGoodsType, skuUnitType } from '~/api/interface/goodsType';
import { Button, Col, Form, Input, Row, Select, Table, Tag } from 'antd';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { useState } from 'react';
import { isEmptyBol, regPriceNumber } from '~/utils/validateUtils';
import EditableCell from '~/components/EditableCell';
import { InterReqType } from '~/api/interface';
import { addMallGoodsType } from '~/api/interface/goodsType';
//商品sku规格类型
type goodsSpecType = Exclude<InterReqType<addMallGoodsType>, undefined>['goodsSpecList'][0];
//单位返回类型
type unitType = InterDataType<skuUnitType>;
interface selfProps {
addOrEditSkuClick: () => void;
skuTableData: goodsSpecType[];
skuUnitList: unitType;
deleteSkuClick: (record: goodsSpecType) => void;
editSkuClick: (record: goodsSpecType) => void;
}
type EditableTableProps = Parameters<typeof Table>[0];
type ColumnTypes = Exclude<EditableTableProps['columns'], undefined>;
const SkuInfo: FC<selfProps> = ({
addOrEditSkuClick,
skuTableData,
skuUnitList,
deleteSkuClick,
editSkuClick,
}) => {
const tableColumns: ColumnsType<goodsSpecType> = [
//规格表单数据类型
type specificationFormListType = {
optionList: { label: string; value: string }[];
id: number;
name: string;
addSpecificationValueShow: boolean;
specificationValueList: { name: string; id: number; specificationName: string }[];
};
//规格表格类型
type skuTableType = Exclude<InterReqType<addMallGoodsType>, undefined>['priceStock'];
const SkuInfo = () => {
const [form] = Form.useForm<{ [x: string]: string }>();
//表格数据
const [tableData, setTableData] = useState<skuTableType>([]);
//表格-列
const salePriceValidator = (_rules: any, value: number) => {
if (!isEmptyBol(value)) {
if (regPriceNumber(value.toString())) {
if (value > 99999999 || value < 0) {
return Promise.reject(new Error('价格最大为99999999且大于0'));
}
return Promise.resolve();
} else {
return Promise.reject(new Error('为整数且最多保留两位小数'));
}
} else {
return Promise.reject(new Error('请输入销售价'));
}
};
//渠道正则价格校验
const channelPriceValidator = (_rules: any, value: number) => {
if (!isEmptyBol(value)) {
if (regPriceNumber(value.toString())) {
if (value > 99999999 || value < 0) {
return Promise.reject(new Error('价格最大为99999999且大于0'));
}
return Promise.resolve();
} else {
return Promise.reject(new Error('为整数且最多保留两位小数'));
}
} else {
return Promise.resolve();
}
};
//库存正则校验
const stockPriceValidator = (_rules: any, value: number) => {
if (!isEmptyBol(value)) {
if (/^[+]{0,1}(\d+)$/.test(value.toString())) {
if (value > 99999999 || value < 0) {
return Promise.reject(new Error('库存最大为99999999且大于0'));
}
return Promise.resolve();
} else {
return Promise.reject(new Error('请输入正整数'));
}
} else {
return Promise.resolve();
}
};
const defaultColumns: (ColumnTypes[number] & {
editable?: boolean;
dataIndex?: string;
inputType?: string;
radioOption?: { name: string; id: number }[];
rules?: any;
maxLength?: number;
})[] = [
{
title: '序号',
title: '商品规格',
align: 'center',
render: (_text: string, _record, index: number) => index + 1,
},
{
title: '规格名称',
title: '图片',
align: 'center',
dataIndex: 'specName',
editable: true,
dataIndex: 'skuImage',
inputType: 'uploader',
},
{
title: '选择方式',
title: '料号',
align: 'center',
dataIndex: 'chooseType',
render: (text: number) => (text ? '多选' : '单选'),
editable: true,
dataIndex: 'skuNo',
maxLength: 30,
},
{
title: '是否必选',
title: '销售价',
align: 'center',
dataIndex: 'must',
render: (text: number) => (text ? '必选' : '非必选'),
editable: true,
dataIndex: 'salePrice',
rules: [{ required: true, validator: salePriceValidator }],
inputType: 'number',
},
{
title: '规格单位',
title: '渠道价',
editable: true,
align: 'center',
dataIndex: 'skuUnitId',
render: (text: number) => skuUnitList.find((v) => v.id === text)?.unitName || '',
dataIndex: 'channelPrice',
rules: [{ required: false, validator: channelPriceValidator }],
inputType: 'number',
},
{
title: '规格值',
title: '库存',
editable: true,
align: 'center',
dataIndex: 'goodsSpecValuesList',
render: (text: goodsSpecType['goodsSpecValuesList']) =>
text.map((v) => (
<Tag key={v.id}>
{v.specValueName}
{v.partNo ? `(${v.partNo})` : ''}
</Tag>
)),
dataIndex: 'stock',
rules: [{ required: false, validator: stockPriceValidator }],
inputType: 'number',
},
{
title: '操作',
align: 'center',
render: (_text: string, record) => (
<>
<Button type='link' onClick={() => editSkuClick(record)}>
编辑
</Button>
<Popconfirm
placement='topLeft'
title={'删除规格'}
description={'确认删除该规格吗?'}
onConfirm={() => deleteSkuClick(record)}
okText='确定'
cancelText='取消'
>
<Button type='link' danger>
删除
</Button>
</Popconfirm>
</>
),
width: '10%',
render: (_text: string, _record: any, index: number) => <></>,
},
];
const [pagination, setPagination] = useState<PaginationProps>({
pageNo: 1,
pageSize: 10,
const columns = defaultColumns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: any) => ({
record,
dataIndex: col.dataIndex,
title: col.title,
editing: col.editable,
radioOption: col.radioOption,
inputType: col.inputType,
uploadSuccess: col.inputType === 'uploader' ? uploadSuccess : undefined,
rules: col.rules,
}),
};
});
//分页
const paginationChange = (pageNo: number, pageSize: number) => {
pagination.pageNo = pageNo;
pagination.pageSize = pageSize;
setPagination({ ...pagination });
//规格表单数组
const [specificationFormList, setSpecificationFormList] = useState<specificationFormListType[]>([
{
id: Math.random(),
name: `specification1`,
optionList: [],
specificationValueList: [],
addSpecificationValueShow: false,
},
]);
//新增规格项目
const addSpecificationClick = () => {
setSpecificationFormList([
...specificationFormList,
{
id: Math.random(),
name: `specification${specificationFormList.length + 1}`,
optionList: [],
specificationValueList: [],
addSpecificationValueShow: false,
},
]);
};
// 删除规格项目
const deleteSpecificationClick = (index: number) => {
specificationFormList.splice(index, 1);
combineSpecificationValue();
setSpecificationFormList([...specificationFormList]);
};
//规格项名称输入完成
const specificationPressEnter = (e: any, index: number) => {
specificationFormList[index].optionList = [{ label: e.target.value, value: e.target.value }];
const obj = Object.create(null);
obj[specificationFormList[index].name] = e.target.value;
form.setFieldsValue(obj);
setSpecificationFormList([...specificationFormList]);
};
//规格值添加
const addSpecificationValueClick = (index: number) => {
specificationFormList[index].addSpecificationValueShow = true;
setSpecificationFormList([...specificationFormList]);
};
const specificationValuePressEnter = (e: any, index: number) => {
specificationFormList[index].specificationValueList.push({
id: Math.random(),
name: e.target.value,
specificationName: specificationFormList[index].optionList[0].value,
});
combineSpecificationValue();
setSpecificationFormList(specificationFormList);
specificationValueCancel(index);
};
//规格值添加-取消
const specificationValueCancel = (index: number) => {
specificationFormList[index].addSpecificationValueShow = false;
setSpecificationFormList([...specificationFormList]);
};
//规格值-删除
const specificationValueDelete = (i: number, j: number) => {
specificationFormList[i].specificationValueList.splice(j, 1);
combineSpecificationValue();
setSpecificationFormList([...specificationFormList]);
};
//组合数据
const combineSpecificationValue = () => {
let combineSpecificationList: any = [];
const combineList = specificationFormList.reduce((pre: any, cur, currentIndex) => {
// 首次组合两个数据
if (currentIndex === 0 && specificationFormList.length > 1) {
combineSpecificationList = combineEvent(
cur.specificationValueList,
specificationFormList[currentIndex + 1].specificationValueList,
);
//二维数组拆分为对象
combineSpecificationList = getCombineObj(combineSpecificationList);
// 两个数据以上的组合
} else if (currentIndex < specificationFormList.length - 1) {
// 上一次的组合作为下一次组合的参数
combineSpecificationList = combineEvent(
combineSpecificationList,
specificationFormList[currentIndex + 1].specificationValueList,
);
//二维数组拆分为对象
combineSpecificationList = getCombineObj(combineSpecificationList);
}
pre = combineSpecificationList;
return pre;
}, []);
console.log('组合数据-->', combineList);
const tableDataList = combineList.reduce((pre: any, cur: any) => {
pre.push(
cur.reduce((a: any, b: any, currentIndex: number) => {
a['name' + (currentIndex + 1)] = b.name;
a['specificationName' + (currentIndex + 1)] = b.specificationName;
a['id'] = Math.random();
return a;
}, {}),
);
return pre;
}, []);
console.log('表格数据-->', tableDataList);
setTableData(tableDataList);
};
//组合数据拆分为对象
const getCombineObj = (combineSpecificationList: any) => {
return combineSpecificationList.reduce((pre: any, cur: any) => {
pre.push(
cur.reduce((a: any, b: any) => {
if (Array.isArray(b)) {
a.push(...b);
} else {
a.push({ ...b });
}
return a;
}, []),
);
return pre;
}, []);
};
const combineEvent = (list1: any, list2: any) => {
return list1.reduce((pre: any, cur: any) => pre.concat(list2.map((v: any) => [cur, v])), []);
};
//规格值上传图片返回
const uploadSuccess = () => {};
return (
<div className='sku-info'>
<div className='sku-info-operate' style={{ margin: ' 20px 0 ' }}>
<Button
type='primary'
icon={<PlusOutlined></PlusOutlined>}
onClick={() => addOrEditSkuClick()}
>
添加规格
</Button>
</div>
<Table
bordered
columns={tableColumns}
dataSource={skuTableData.slice(
(pagination.pageNo - 1) * pagination.pageSize,
pagination.pageNo * pagination.pageSize,
)}
rowKey='id'
pagination={{
total: skuTableData.length,
pageSize: pagination.pageSize,
current: pagination.pageNo,
showSizeChanger: true,
showQuickJumper: true,
onChange: (page: number, pageSize: number) => paginationChange(page, pageSize),
showTotal: (total, range) => `当前 ${range[0]}-${range[1]} 条记录 / 共 ${total} 条数据`,
}}
/>
<Form wrapperCol={{ span: 5 }} labelCol={{ span: 1 }} form={form}>
{specificationFormList.map((v, index) => (
<>
{/* 规格项*/}
<Row key={v.id}>
<Col span={7}>
<Form.Item
label={'规格项' + (index + 1)}
wrapperCol={{ span: 18 }}
labelCol={{ span: 5 }}
name={v.name}
>
<Select
placeholder='请输入规格项,按回车键完成'
dropdownRender={(menu) => (
<>
{v.optionList.length ? menu : ''}
<Input onPressEnter={(e) => specificationPressEnter(e, index)} />
</>
)}
options={v.optionList}
/>
</Form.Item>
</Col>
{index ? (
<Col span={2}>
<Button
danger
icon={<DeleteOutlined />}
onClick={() => deleteSpecificationClick(index)}
>
删除
</Button>
</Col>
) : (
''
)}
</Row>
{/*规格值显示*/}
{v.specificationValueList.length ? (
<Row style={{ marginBottom: '10px' }}>
<Col span={2}></Col>
<Col span={4}>
<div>
{v.specificationValueList.map((v, i) => (
<Tag key={v.id} closable onClose={() => specificationValueDelete(index, i)}>
{v.name}
</Tag>
))}
</div>
</Col>
</Row>
) : (
''
)}
{/*规格值操作*/}
{v.optionList.length ? (
<Row>
<Col span={2}></Col>
<Col span={4} style={{ marginBottom: '10px' }}>
{v.addSpecificationValueShow ? (
<Input
placeholder='请输入规格值,按回车键完成'
onPressEnter={(e) => specificationValuePressEnter(e, index)}
/>
) : (
<Button
type='link'
danger
icon={<PlusOutlined />}
onClick={() => addSpecificationValueClick(index)}
>
添加规格值
</Button>
)}
</Col>
{v.addSpecificationValueShow ? (
<Col>
<Button
type='primary'
style={{ marginLeft: '10px' }}
onClick={() => specificationValueCancel(index)}
>
取消
</Button>
</Col>
) : (
''
)}
</Row>
) : (
''
)}
</>
))}
<Row>
<Col span={2}></Col>
<Col>
<Button type='primary' icon={<PlusOutlined />} onClick={addSpecificationClick}>
添加规格
</Button>
</Col>
</Row>
</Form>
<Form>
<Table
style={{ marginTop: '10px' }}
rowKey='id'
columns={columns as ColumnTypes}
components={{
body: {
cell: EditableCell,
},
}}
bordered
dataSource={tableData}
pagination={false}
/>
</Form>
</div>
);
};
......
import { Button, Popconfirm, Table, Tag } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { ColumnsType } from 'antd/es/table';
import { FC, useState } from 'react';
import { InterDataType, InterReqType, PaginationProps } from '~/api/interface';
import { addMallGoodsType, skuUnitType } from '~/api/interface/goodsType';
//商品sku规格类型
type goodsSpecType = Exclude<InterReqType<addMallGoodsType>, undefined>['goodsSpecList'][0];
//单位返回类型
type unitType = InterDataType<skuUnitType>;
interface selfProps {
addOrEditSkuClick: () => void;
skuTableData: goodsSpecType[];
skuUnitList: unitType;
deleteSkuClick: (record: goodsSpecType) => void;
editSkuClick: (record: goodsSpecType) => void;
}
const SkuInfo: FC<selfProps> = ({
addOrEditSkuClick,
skuTableData,
skuUnitList,
deleteSkuClick,
editSkuClick,
}) => {
const tableColumns: ColumnsType<goodsSpecType> = [
{
title: '序号',
align: 'center',
render: (_text: string, _record, index: number) => index + 1,
},
{
title: '规格名称',
align: 'center',
dataIndex: 'specName',
},
{
title: '选择方式',
align: 'center',
dataIndex: 'chooseType',
render: (text: number) => (text ? '多选' : '单选'),
},
{
title: '是否必选',
align: 'center',
dataIndex: 'must',
render: (text: number) => (text ? '必选' : '非必选'),
},
{
title: '规格单位',
align: 'center',
dataIndex: 'skuUnitId',
render: (text: number) => skuUnitList.find((v) => v.id === text)?.unitName || '',
},
{
title: '规格值',
align: 'center',
dataIndex: 'goodsSpecValuesList',
render: (text: goodsSpecType['goodsSpecValuesList']) =>
text.map((v) => (
<Tag key={v.id}>
{v.specValueName}
{v.partNo ? `(${v.partNo})` : ''}
</Tag>
)),
},
{
title: '操作',
align: 'center',
render: (_text: string, record) => (
<>
<Button type='link' onClick={() => editSkuClick(record)}>
编辑
</Button>
<Popconfirm
placement='topLeft'
title={'删除规格'}
description={'确认删除该规格吗?'}
onConfirm={() => deleteSkuClick(record)}
okText='确定'
cancelText='取消'
>
<Button type='link' danger>
删除
</Button>
</Popconfirm>
</>
),
},
];
const [pagination, setPagination] = useState<PaginationProps>({
pageNo: 1,
pageSize: 10,
});
//分页
const paginationChange = (pageNo: number, pageSize: number) => {
pagination.pageNo = pageNo;
pagination.pageSize = pageSize;
setPagination({ ...pagination });
};
return (
<div className='sku-info'>
<div className='sku-info-operate' style={{ margin: ' 20px 0 ' }}>
<Button
type='primary'
icon={<PlusOutlined></PlusOutlined>}
onClick={() => addOrEditSkuClick()}
>
添加规格
</Button>
</div>
<Table
bordered
columns={tableColumns}
dataSource={skuTableData.slice(
(pagination.pageNo - 1) * pagination.pageSize,
pagination.pageNo * pagination.pageSize,
)}
rowKey='id'
pagination={{
total: skuTableData.length,
pageSize: pagination.pageSize,
current: pagination.pageNo,
showSizeChanger: true,
showQuickJumper: true,
onChange: (page: number, pageSize: number) => paginationChange(page, pageSize),
showTotal: (total, range) => `当前 ${range[0]}-${range[1]} 条记录 / 共 ${total} 条数据`,
}}
/>
</div>
);
};
export default SkuInfo;
......@@ -41,10 +41,10 @@ const GoodsAddOrEditOrDetail = () => {
const [goodsDetailsInfo, setGoodsDetailsInfo] = useState<goodsDetailType>();
//新增、编辑sku弹窗显示
const addOrEditSkuClick = (record?: goodsSpecType) => {
setCurrentSku(record ? { ...record } : undefined);
setAddOrEditSkuModalShow(true);
};
// const addOrEditSkuClick = (record?: goodsSpecType) => {
// setCurrentSku(record ? { ...record } : undefined);
// setAddOrEditSkuModalShow(true);
// };
const addOrEditSkuModalCancel = () => {
setAddOrEditSkuModalShow(false);
};
......@@ -59,11 +59,11 @@ const GoodsAddOrEditOrDetail = () => {
}
};
//sku删除
const deleteSkuClick = (record: goodsSpecType) => {
const skuIndex: number = skuTable.findIndex((v) => v.id === record.id);
skuTable.splice(skuIndex, 1);
setSkuTable([...skuTable]);
};
// const deleteSkuClick = (record: goodsSpecType) => {
// const skuIndex: number = skuTable.findIndex((v) => v.id === record.id);
// skuTable.splice(skuIndex, 1);
// setSkuTable([...skuTable]);
// };
//商品详情获取
const getIntroduceInfo = (richText: string) => {
setGoodsDetails(richText);
......@@ -79,13 +79,16 @@ const GoodsAddOrEditOrDetail = () => {
key: '2',
label: `商品规格`,
children: (
<SkuInfo
addOrEditSkuClick={addOrEditSkuClick}
skuTableData={skuTable}
skuUnitList={skuUnitList}
deleteSkuClick={deleteSkuClick}
editSkuClick={addOrEditSkuClick}
/>
// (旧)
// <SkuInfo
// addOrEditSkuClick={addOrEditSkuClick}
// skuTableData={skuTable}
// skuUnitList={skuUnitList}
// deleteSkuClick={deleteSkuClick}
// editSkuClick={addOrEditSkuClick}
// />
//(新)
<SkuInfo />
),
},
{
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论