Commit 0adbd8ef authored by beisir's avatar beisir

feat: 商品库,商户录品二期

parent 648a86d8
...@@ -3,3 +3,4 @@ ...@@ -3,3 +3,4 @@
/config /config
.history .history
/src/utils/qiniu.min.js /src/utils/qiniu.min.js
/src/pages/GoodsManage-new
\ No newline at end of file
...@@ -7,6 +7,9 @@ module.exports = { ...@@ -7,6 +7,9 @@ module.exports = {
}, },
rules: { rules: {
'max-len': ['error', { code: 200 }], 'max-len': ['error', { code: 200 }],
'no-param-reassign': 0,
'no-console': 0,
'@typescript-eslint/camelcase': ['off'], '@typescript-eslint/camelcase': ['off'],
'@typescript-eslint/no-unused-vars': ['off'],
}, },
}; };
...@@ -150,6 +150,12 @@ export default { ...@@ -150,6 +150,12 @@ export default {
name: 'cancelBillManage', name: 'cancelBillManage',
component: './cancelBillManage', component: './cancelBillManage',
}, },
{
path: '/goodsManage',
name: 'goodsManage',
icon: 'smile',
component: './GoodsManage',
},
{ {
component: './404', component: './404',
}, },
......
const isProduction = process.env.NODE_ENV === 'production'; const isProduction = process.env.NODE_ENV === 'production';
let envAPi = { let envAPi = {
api: '//backstms-gyl.liangkebang.net', api: '//backstms-gyl2.liangkebang.net',
kdspOpApi: 'https://kdsp-operation-gyl.liangkebang.net', kdspOpApi: 'https://kdsp-operation-gyl2.liangkebang.net',
kdspApi: 'https://sc-op-api-gyl.liangkebang.net', kdspApi: 'https://sc-op-api-gyl2.liangkebang.net',
prologueDomain: 'https://prologue-gyl2.liangkebang.net',
qiniuHost: 'https://appsync.lkbang.net', qiniuHost: 'https://appsync.lkbang.net',
opapiHost: 'https://opapi-gyl.liangkebang.net', opapiHost: 'https://opapi-gyl2.liangkebang.net',
// opapiHost: 'http://192.168.29.45:7000', // opapiHost: 'http://192.168.29.45:7000',
}; };
let prodApi = { let prodApi = {
api: '//backstms.q-gp.com', api: '//backstms.q-gp.com',
kdspOpApi: '//kdsp-operation.q-gp.com', kdspOpApi: '//kdsp-operation.q-gp.com',
prologueDomain: '//prologue.q-gp.com',
kdspApi: '//sc-op-api.q-gp.com', kdspApi: '//sc-op-api.q-gp.com',
qiniuHost: 'https://appsync.lkbang.net', qiniuHost: 'https://appsync.lkbang.net',
opapiHost: 'https://opapi.xyqb.com', opapiHost: 'https://opapi.xyqb.com',
......
...@@ -4809,6 +4809,14 @@ ...@@ -4809,6 +4809,14 @@
} }
} }
}, },
"antd-virtual-select": {
"version": "1.1.2",
"resolved": "http://npmprivate.quantgroups.com/antd-virtual-select/-/antd-virtual-select-1.1.2.tgz",
"integrity": "sha512-GejZ/ihog9ZwSn16GsfABPBjeM/X9XMktndu570q9kCe1D/LieO07xNME/dLx4x8IaqGxh6yW4eTUzR83+gkKw==",
"requires": {
"moment": "^2.22.2"
}
},
"any-observable": { "any-observable": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "http://npmprivate.quantgroups.com/any-observable/-/any-observable-0.3.0.tgz", "resolved": "http://npmprivate.quantgroups.com/any-observable/-/any-observable-0.3.0.tgz",
...@@ -20109,6 +20117,15 @@ ...@@ -20109,6 +20117,15 @@
"resize-observer-polyfill": "^1.5.0" "resize-observer-polyfill": "^1.5.0"
} }
}, },
"react-sortablejs": {
"version": "6.0.0",
"resolved": "http://npmprivate.quantgroups.com/react-sortablejs/-/react-sortablejs-6.0.0.tgz",
"integrity": "sha512-vzi+TWOnofcYg+dYnC/Iz/ZZkBGG76uM6KaLwuAqBk0349JQxIy3PZizbK0TJdLlK6NnLt4CiEyyQXSSnVYvEw==",
"requires": {
"classnames": "^2.2.6",
"tiny-invariant": "^1.1.0"
}
},
"react-test-renderer": { "react-test-renderer": {
"version": "16.14.0", "version": "16.14.0",
"resolved": "http://npmprivate.quantgroups.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz", "resolved": "http://npmprivate.quantgroups.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz",
...@@ -21811,6 +21828,11 @@ ...@@ -21811,6 +21828,11 @@
} }
} }
}, },
"sortablejs": {
"version": "1.13.0",
"resolved": "http://npmprivate.quantgroups.com/sortablejs/-/sortablejs-1.13.0.tgz",
"integrity": "sha512-RBJirPY0spWCrU5yCmWM1eFs/XgX2J5c6b275/YyxFRgnzPhKl/TDeU2hNR8Dt7ITq66NRPM4UlOt+e5O4CFHg=="
},
"source-list-map": { "source-list-map": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "http://npmprivate.quantgroups.com/source-list-map/-/source-list-map-2.0.1.tgz", "resolved": "http://npmprivate.quantgroups.com/source-list-map/-/source-list-map-2.0.1.tgz",
This diff is collapsed.
/* eslint-disable no-param-reassign */
import React from 'react';
import { Cascader, Select, InputNumber, Button } from 'antd';
import { stateList } from './staticdata';
const filterOption = (input, op) => op.props.children.includes(input);
export const toolBarList = () => [
<Button key="getOffGoodsShelf" type="primary">
新增商品
</Button>,
<Button key="putGoodsShelf">模版</Button>,
<Button key="tags" type="primary">
批量修改库存
</Button>,
];
export const Goodscolumns = ({ categoryData, supplyPrice, setSupplyPrice, supplyPriceRef }) => [
{
title: 'SKU编码',
dataIndex: 'skuId',
key: 'skuId',
},
{
title: 'SKU商品名称',
dataIndex: 'skuName',
key: 'skuName',
},
{
title: '供应商名称',
dataIndex: 'shopId',
key: 'shopId',
hideInTable: true,
valueType: 'cascader',
renderFormItem: () => (
<Cascader
changeOnSelect
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
options={categoryData}
/>
),
},
{
title: '审核状态',
dataIndex: 'state',
key: 'state',
hideInTable: true,
valueType: 'select',
renderFormItem: () => (
<Select allowClear showSearch filterOption={filterOption}>
{stateList?.map(item => (
<Select.Option key={item.value} value={item.value}>
{item.label}
</Select.Option>
))}
</Select>
),
},
{
title: '供货价区间',
hideInTable: true,
dataIndex: 'supplyPrice',
modalHide: true,
renderFormItem: () => [
<div key="supplyPrice" className="search-profit-margin">
<InputNumber
key="min"
placeholder="请输入"
onChange={value => {
supplyPrice[0] = value;
setSupplyPrice(supplyPrice);
}}
/>
<span className="line">-</span>
<InputNumber
ref={supplyPriceRef}
key="max"
placeholder="请输入"
onChange={value => {
supplyPrice[1] = value;
setSupplyPrice(supplyPrice);
}}
/>
</div>,
],
},
{
title: '第三方SKU编码',
dataIndex: 'thirdSkuNo',
key: 'thirdSkuNo',
hideInTable: true,
},
{
title: '价格',
dataIndex: 'supplyPrice',
key: 'supplyPrice',
hideInSearch: true,
render: (value, row) => (
<>
<p>
供货价:<a>{value}</a>
</p>
<p>
市场价:<a>{row.marketPrice}</a>
</p>
</>
),
},
{
title: '库存',
dataIndex: 'stock',
key: 'stock',
hideInSearch: true,
render: (value, row) => (
<>
<p>
当前库存:<a>{row.productStock}</a>
</p>
<p>可售库存:{value}</p>
{row.type === 1 && row.productStockWarning > -1 && <p>预警值:{row.productStockWarning}</p>}
</>
),
},
{
title: '商品状态',
dataIndex: 'stateDesc',
key: 'stateDesc',
hideInSearch: true,
},
{
title: '操作',
key: 'option',
dataIndex: 'option',
valueType: 'option',
fixed: 'right',
modalHide: true,
render: () => (
<>
<Button key="update" type="link" onClick={() => {}}>
修改
</Button>
<Button key="preview" type="link" onClick={() => {}}>
预览
</Button>
<Button key="seelog" type="link" onClick={() => {}}>
查看日志
</Button>
</>
),
},
];
import { Modal, Table, Button, Pagination } from 'antd';
import React, { useState, useEffect } from 'react';
import styles from '../style.less';
import { changeLog } from '../service';
const LogModal = props => {
const [tableData, setTableData] = useState([]);
const [pageNo, setPageNo] = useState(1);
const [pageSize] = useState(20);
const columns = [
{
title: '时间',
dataIndex: 'createdAt',
align: 'center',
},
{
title: '变更字段',
align: 'center',
dataIndex: 'changeType',
},
{
title: '变更后内容',
dataIndex: 'afterChangeValue',
align: 'center',
},
{
title: '变更前内容',
dataIndex: 'beforeChangeValue',
align: 'center',
},
{
title: '变更原因',
dataIndex: 'reason',
align: 'center',
},
{
title: '操作账号',
dataIndex: 'operator',
align: 'center',
},
];
const handleSearch = async (page = 1) => {
setPageNo(page);
const [data] = await changeLog({ id: props.id, pageNo: page, pageSize });
setTableData(data);
};
const onPageChange = page => {
handleSearch(page);
};
useEffect(() => {
if (!props.id) return;
handleSearch();
}, [props.id]);
const { visible } = props;
return (
<Modal title="日志详情" visible={visible} footer={null} onCancel={props.onCancel} width="800px">
<Table
dataSource={tableData.records}
bordered
columns={columns}
rowKey={record => record.id}
pagination={false}
scroll={{ y: 300 }}
/>
{tableData.records && (
<Pagination
onChange={onPageChange}
total={tableData.total}
showTotal={total => `共${total}条`}
current={pageNo}
pageSize={pageSize}
className={styles.pagination}
/>
)}
<Button type="primary" onClick={props.onCancel} className={styles.logBtn}>
关闭
</Button>
</Modal>
);
};
export default LogModal;
This diff is collapsed.
import { Modal, Table, Button } from 'antd';
import React, { Component } from 'react';
import styles from '../style.less';
class DetailModal extends Component {
getColumns = (firstSpe, secondSpe, type) => {
const arr = [
{
title: '供应商',
dataIndex: 'supplierName',
key: 'supplierName',
align: 'center',
width: 50,
render: (val, row) => {
const obj = {
children: val,
props: {},
};
if (row.listLength) {
obj.props.rowSpan = row.listLength;
} else {
obj.props.rowSpan = 0;
}
return obj;
},
},
];
if (firstSpe) {
arr.push({
title: firstSpe,
dataIndex: 'firstSpecValue',
key: 'firstSpecValue',
align: 'center',
width: 50,
render: (val, row) => {
const obj = {
children: val,
props: {},
};
if (row.length) {
obj.props.rowSpan = row.length;
} else {
obj.props.rowSpan = 0;
}
return obj;
},
});
}
if (secondSpe) {
arr.push({
title: secondSpe,
align: 'center',
key: 'secondSpecValue',
dataIndex: 'secondSpecValue',
width: 50,
});
}
const newTable = arr.concat([
{
title: '供应商编码',
align: 'center',
key: 'supplierCode',
dataIndex: 'supplierCode',
width: 50,
},
{
title: '库存',
align: 'center',
key: 'stock',
dataIndex: 'stock',
width: 50,
},
{
title: '供货价',
align: 'center',
key: 'supplyPrice',
dataIndex: 'supplyPrice',
width: 50,
},
{
title: '市场价',
align: 'center',
key: 'marketPrice',
dataIndex: 'marketPrice',
width: 50,
},
{
title: '国际编码',
align: 'center',
key: 'thirdSkuNo',
dataIndex: 'thirdSkuNo',
width: 50,
},
]);
if (type === 1) {
newTable.splice(newTable.length - 1, 0, {
title: '重量(kg)',
align: 'center',
key: 'weight',
dataIndex: 'weight',
width: 90,
});
}
return newTable;
};
updateStatus = (row, productState) => {
this.props.updateStatus(row, productState);
};
dataInit = list => {
const obj = {};
let finialList = [];
list.map(item => {
obj[item.firstSpecValue] = [];
return obj;
});
list.map(item => obj[item.firstSpecValue].push(item));
const keys = Object.keys(obj);
// eslint-disable-next-line no-return-assign
keys.map(key => {
obj[key].forEach((i, index) => {
if (index === 0) {
i.length = obj[key].length;
}
});
finialList = finialList.concat(obj[key]);
return finialList;
});
if (finialList.length) {
finialList[0].listLength = finialList.length;
}
return finialList;
};
render() {
const { visible, data = [], type } = this.props;
const initdata = this.dataInit(data);
const firstName = data.length ? data[0].firstSpec : '';
const secondName = data.length ? data[0].secondSpec : '';
return (
<Modal
title="供货详情"
visible={visible}
footer={null}
onCancel={this.props.onCancel}
width="900px"
>
<Table
dataSource={initdata}
bordered
columns={this.getColumns(firstName, secondName, type)}
rowKey="id"
pagination={false}
scroll={{ x: '100%' }}
/>
<Button type="primary" onClick={this.props.onCancel} className={styles.logBtn}>
关闭
</Button>
</Modal>
);
}
}
export default DetailModal;
import { Modal, InputNumber, notification } from 'antd';
import React, { useState, useEffect } from 'react';
import { updatePrice } from '../service';
import styles from './style.less';
export default function UpdatePrice(props) {
const [visible, setVisible] = useState(props.visible);
const [supplyPrice, setSupplyPrice] = useState(props.info.supplyPrice);
const [marketPrice, setMarketPrice] = useState(props.info.marketPrice);
// const [salePrice, setSalePrice] = useState(props.info.salePrice);
useEffect(() => {
setVisible(props.visible);
setSupplyPrice(props.info.supplyPrice);
setMarketPrice(props.info.marketPrice);
// setSalePrice(props.info.salePrice);
}, [props]);
const submit = async () => {
if (!supplyPrice || !marketPrice) {
notification.error({ message: '价格不可为空!' });
return;
}
const error = await updatePrice({
// salePrice,
supplyPrice,
marketPrice,
id: props.info.id,
supplierId: props.info.supplierId,
});
if (!error) {
notification.success({ message: '修改成功' });
props.onCancel('success');
}
};
return (
<Modal
title="修改价格"
visible={visible}
onCancel={() => props.onCancel()}
onOk={submit}
width={400}
>
<div className={styles.center}>
供货价:
<InputNumber
min={0}
precision={2}
value={supplyPrice}
onChange={value => setSupplyPrice(value)}
className={styles.inputNW}
/>
<br />
<br />
市场价:
<InputNumber
min={0}
precision={2}
value={marketPrice}
onChange={value => setMarketPrice(value)}
className={styles.inputNW}
/>
<br />
<br />
{/* 销售价:
<InputNumber
min={0}
precision={2}
value={salePrice}
onChange={value => setSalePrice(value)}
className={styles.inputNW}
/> */}
</div>
</Modal>
);
}
.inputNW {
width: 200px;
}
.center {
text-align: center;
}
import { Modal, InputNumber, notification, Form, Input, Radio } from 'antd';
import React from 'react';
import { updateStock } from '../service';
import styles from '../UpdatePrice/style.less';
const UpdateStock = props => {
const { getFieldDecorator, validateFields, resetFields, getFieldValue } = props.form;
const valueInfo = props.info;
getFieldDecorator('stockChangeType', { initialValue: 1 });
const submit = async () => {
validateFields(async (err, { stock, changeReason, stockChangeType }) => {
if (err) return;
const error = await updateStock({
stock,
id: valueInfo.id,
supplierId: valueInfo.supplierId,
stockChangeType,
changeReason,
});
if (!error) {
notification.success({ message: '操作成功!' });
props.onCancel('success');
resetFields();
}
});
};
const onCancel = () => {
props.onCancel();
resetFields();
};
const formItemLayout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
const validatorCallback = (rule, value, callback) => {
// 减库存存时,校验可售库存-输入值>=0,即可售库存不可为负;
const stockChangeType = getFieldValue('stockChangeType');
const increment = valueInfo.marketableStock - value;
return !stockChangeType && increment < 0 ? callback(new Error(rule.message)) : callback();
};
return (
<Modal title="修改库存" visible={props.visible} onCancel={onCancel} onOk={submit} width={400}>
<Form {...formItemLayout}>
<Form.Item label="变更类型:">
{getFieldDecorator('stockChangeType', {
rules: [{ required: true, message: '请选择类型!' }],
})(
<Radio.Group>
<Radio value={1}>增库存</Radio>
<Radio value={0}>减库存</Radio>
</Radio.Group>,
)}
</Form.Item>
<Form.Item label="库存数:">
{getFieldDecorator('stock', {
rules: [
{ required: true, message: '请输入库存!' },
{ validator: validatorCallback, message: '减库存,输入库存数不可大于可售库存!' },
],
validateTrigger: ['onSubmit'],
})(
<InputNumber
min={0}
precision={0}
placeholder="请输入库存"
className={styles.inputNW}
/>,
)}
</Form.Item>
<Form.Item label="变更原因:">
{getFieldDecorator('changeReason', {
rules: [{ required: true, message: '请输入变更原因!' }],
initialValue: valueInfo.changeReason,
})(<Input.TextArea />)}
</Form.Item>
</Form>
</Modal>
);
};
export default Form.create()(UpdateStock);
import { Modal, Form, Select, Input, InputNumber } from 'antd';
import React, { Component } from 'react';
const FormItem = Form.Item;
class formModal extends Component {
componentDidMount() {
this.props.onRef(this);
}
handleOk = () => {
this.props.form.validateFields((err, fieldsValue) => {
if (err) return;
const value = {
...fieldsValue,
id: this.props.initForm.id ?? '',
};
this.props.submit(value);
});
};
resetFields = () => {
this.props.form.resetFields();
};
render() {
const {
visible,
form: { getFieldDecorator },
statusList = [],
initForm = {},
} = this.props;
const InputNumStyle = { width: '100%' };
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 17 },
},
};
const options =
statusList.length &&
statusList.map(d => (
<Select.Option value={d.status} key={d.status}>
{d.statusDesc}
</Select.Option>
));
return (
<Modal
title="商品信息修改"
visible={visible}
onCancel={this.props.onCancel}
onOk={this.handleOk}
>
<Form name="detail" {...formItemLayout}>
<FormItem label="sku编码">
{getFieldDecorator('skuNo', {
initialValue: initForm.skuNo,
})(<Input readOnly />)}
</FormItem>
<FormItem label="商品名称">
{getFieldDecorator('skuName', {
initialValue: initForm.skuName,
rules: [{ required: true, message: '请输入商品名称' }],
})(<Input allowClear />)}
</FormItem>
<FormItem label="商品规格">
{getFieldDecorator('attValue', {
initialValue: initForm.attValue,
rules: [{ message: '请输入商品规格' }],
})(<Input allowClear />)}
</FormItem>
<FormItem label="成本价">
{getFieldDecorator('costPrice', {
initialValue: initForm.costPrice,
rules: [{ required: true, message: '请输入成本价' }],
})(<InputNumber allowClear precision={2} style={InputNumStyle} />)}
</FormItem>
{/* <FormItem label="市场价">
{getFieldDecorator('marketPrice', {
initialValue: initForm.marketPrice,
rules: [{ required: true, message: '请输入市场价' }],
})(<InputNumber allowClear style={InputNumStyle} precision={2} />)}
</FormItem>
<FormItem label="销售价">
{getFieldDecorator('salePrice', {
initialValue: initForm.salePrice,
rules: [{ required: true, message: '请输入销售价' }],
})(<InputNumber allowClear style={InputNumStyle} precision={2} />)}
</FormItem> */}
<FormItem label="库存">
{getFieldDecorator('stock', {
initialValue: initForm.stock,
rules: [{ required: true, message: '请输入库存' }],
})(<InputNumber allowClear style={InputNumStyle} />)}
</FormItem>
<FormItem label="商品状态">
{getFieldDecorator('status', {
initialValue: initForm.status,
rules: [{ required: true, message: '请输入商品状态' }],
})(<Select allowClear>{options}</Select>)}
</FormItem>
</Form>
</Modal>
);
}
}
export default Form.create()(formModal);
import React from 'react';
import SuperSelect from 'antd-virtual-select';
import { Form, Modal, Card, Cascader, Select, Radio } from 'antd';
import { productTypeList } from '../staticdata';
const { Option } = Select;
const filterOption = (input, op) => op.props.children.includes(input);
const OperationForm = props => {
const {
operationVisible,
setOperationVisible,
categoryData,
// virtualTreeData,
shopList,
barndList,
// virtualBarndList,
form,
} = props;
const { getFieldDecorator } = form;
return (
<Modal
width="1050px"
visible={operationVisible}
onCancel={() => {
setOperationVisible(false);
}}
>
<Form layout="inline">
<Card bordered={false}>
<Form.Item label="商品类型:">
{getFieldDecorator('productType', {
initialValue: 1,
rules: [{ required: true, message: '请选择商品类型' }],
})(
<Radio.Group>
{productTypeList.map(item => (
<Radio key={item.value} value={item.value} disabled={item.disabled}>
{item.label}
</Radio>
))}
</Radio.Group>,
)}
</Form.Item>
<Form.Item label="类目:">
{getFieldDecorator('categoryId', {
rules: [{ required: true, message: '请选择类目' }],
})(
<Cascader
style={{ width: 690 }}
changeOnSelect
showSearch
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
options={categoryData}
/>,
)}
</Form.Item>
<Form.Item label="供货商">
{getFieldDecorator('supplierId', {
rules: [{ required: true, message: '请选择供货商' }],
})(
<Select allowClear showSearch style={{ width: 190 }} filterOption={filterOption}>
{shopList.map(item => (
<Option key={item.id} value={item.id}>
{item.name}
</Option>
))}
</Select>,
)}
</Form.Item>
<Form.Item label="商品品牌">
{getFieldDecorator('brandId', {
rules: [{ required: true, message: '请选择商品品牌' }],
})(
<SuperSelect allowClear showSearch style={{ width: 190 }} filterOption={filterOption}>
{barndList.map(item => (
<Option key={item.id} value={item.id}>
{item.name}
</Option>
))}
</SuperSelect>,
)}
</Form.Item>
</Card>
</Form>
</Modal>
);
};
export default Form.create()(OperationForm);
/* eslint-disable no-param-reassign */
import { Form, Select, Input, InputNumber, Button } from 'antd';
import React, { Component } from 'react';
// import styles from '../style.less';
const { Option } = Select;
const FormItem = Form.Item;
class goodsManage extends Component {
componentDidMount() {
this.props.onRef(this);
}
setFiled = flag => {
const { form } = this.props;
if (flag === 'firstKeys') {
form.setFieldsValue({ firstSpecValue: '' });
return;
}
form.setFieldsValue({ secondSpecValue: '' });
};
batchSetting = () => {
const { form, editData, isEdit } = this.props;
const data = form.getFieldsValue();
if (!data.firstSpecValue && !data.secondSpecValue) {
editData.forEach(item => {
item.marketPrice = data.marketPrice;
// item.salePrice = data.salePrice;
if (!isEdit) item.productStock = data.productStock; // 编辑状态不可修改库存
item.supplyPrice = data.supplyPrice;
item.weight = data.weight;
});
}
if (data.firstSpecValue && !data.secondSpecValue) {
editData.forEach(item => {
if (item.firstSpecValue === data.firstSpecValue) {
item.marketPrice = data.marketPrice;
// item.salePrice = data.salePrice;
if (!isEdit) item.productStock = data.productStock;
item.supplyPrice = data.supplyPrice;
item.weight = data.weight;
}
});
}
if (!data.firstSpecValue && data.secondSpecValue) {
editData.forEach(item => {
if (item.secondSpecValue === data.secondSpecValue) {
item.marketPrice = data.marketPrice;
// item.salePrice = data.salePrice;
if (!isEdit) item.productStock = data.productStock;
item.supplyPrice = data.supplyPrice;
item.weight = data.weight;
}
});
}
if (data.firstSpecValue && data.secondSpecValue) {
editData.forEach(item => {
if (
item.firstSpecValue === data.firstSpecValue &&
item.secondSpecValue === data.secondSpecValue
) {
item.marketPrice = data.marketPrice;
// item.salePrice = data.salePrice;
if (!isEdit) item.productStock = data.productStock;
item.supplyPrice = data.supplyPrice;
item.weight = data.weight;
}
});
}
this.props.batchSetting(editData);
};
render() {
const {
firstSpes = [],
secondSpecs = [],
firstSpesName = '',
secondSpesName = '',
productType,
isEdit,
} = this.props;
const { getFieldDecorator } = this.props.form;
return (
<Form layout="inline" onSubmit={this.handleSubmit}>
<FormItem>
{getFieldDecorator('firstSpecValue', {})(
<Select allowClear style={{ width: 120 }} placeholder={firstSpesName}>
{firstSpes.length > 0 &&
firstSpes.map(
item =>
item &&
typeof item === 'string' && (
<Option key={item} value={item}>
{item}
</Option>
),
)}
</Select>,
)}
</FormItem>
<FormItem>
{getFieldDecorator('secondSpecValue', {})(
<Select allowClear style={{ width: 120 }} placeholder={secondSpesName}>
{secondSpecs.length &&
secondSpecs.map(
item =>
item &&
typeof item === 'string' && (
<Option key={item} value={item}>
{item}
</Option>
),
)}
</Select>,
)}
</FormItem>
<FormItem>
{getFieldDecorator('supplyPrice', {})(
<Input placeholder="供货价" style={{ width: 100 }} />,
)}
</FormItem>
<FormItem>
{getFieldDecorator('marketPrice', {})(
<Input placeholder="市场价" style={{ width: 100 }} />,
)}
</FormItem>
{/* <FormItem>
{getFieldDecorator('salePrice', {})(
<Input placeholder="销售价" style={{ width: 100 }} />,
)}
</FormItem> */}
{productType === 1 && (
<FormItem>
{getFieldDecorator('weight', {})(
<InputNumber
precision={3}
max={999999.999}
// eslint-disable-next-line radix
placeholder="重量"
style={{ width: 150 }}
/>,
)}
</FormItem>
)}
{!isEdit && (
<FormItem>
{getFieldDecorator('productStock', {})(
<InputNumber
precision={0}
step={1}
// eslint-disable-next-line radix
formatter={val => parseInt(val, '10') || ''}
placeholder="库存"
style={{ width: 100 }}
/>,
)}
</FormItem>
)}
<FormItem>
<Button type="primary" htmlType="submit" onClick={this.batchSetting}>
批量设置
</Button>
</FormItem>
</Form>
);
}
}
export default Form.create()(goodsManage);
import { Row, Col, Button } from 'antd';
import React, { Component } from 'react';
import styles from '../style.less';
// eslint-disable-next-line react/prefer-stateless-function
class ButtonGroup extends Component {
render() {
const { initData, confirmLoading } = this.props;
return (
<Row type="flex" justify="center" align="middle" gutter={20}>
<Col>
<Button type="primary" onClick={() => this.props.onCancel()} className={styles.logBtn}>
取消
</Button>
</Col>
<Col key="submit">
<Button
type="primary"
onClick={() => this.props.confirm()}
className={styles.logBtn}
loading={confirmLoading}
disabled={confirmLoading}
>
提交
</Button>
</Col>
{initData && !Object.keys(initData).length && (
<Col key="submit-add">
<Button
type="primary"
onClick={() => this.props.confirm(true)}
className={styles.logBtn}
loading={confirmLoading}
disabled={confirmLoading}
>
提交并继续添加
</Button>
</Col>
)}
</Row>
);
}
}
export default ButtonGroup;
This diff is collapsed.
This diff is collapsed.
import { Form, Input, Modal } from 'antd';
import React, { Component } from 'react';
const { TextArea } = Input;
const FormItem = Form.Item;
// import styles from '../style.less';
class goodsManage extends Component {
// componentDidMount() {
// this.props.onRef(this);
// }
handleOk = () => {
const { form } = this.props;
form.validateFields((err, values) => {
if (!err) {
this.props.changeSkuName(values.name);
this.props.form.resetFields();
}
});
};
handleCancel = () => {
this.props.form.resetFields();
this.props.onCancle();
};
render() {
const { data, visible, form } = this.props;
const { getFieldDecorator } = form;
return (
<Modal title="" visible={visible} onOk={this.handleOk} onCancel={this.handleCancel}>
<Form>
<FormItem label="sku名称">
{getFieldDecorator('name', {
initialValue: data,
rules: [
{
required: true,
message: '请输入',
},
],
})(<TextArea autoSize={{ minRows: 2, maxRows: 6 }} allowClear />)}
</FormItem>
</Form>
</Modal>
);
}
}
export default Form.create()(goodsManage);
This diff is collapsed.
import React, { useEffect, useState, useRef } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import { Button } from 'antd';
import { categoryList, query, getVirtualCategory, getSupplierList, getBrandList } from './service';
import { Goodscolumns } from './Goodscolumns';
import styled from './style.less';
import OperationModal from './components/operationModal';
export default () => {
/**
* @name screenLoading
* @type boolean
* @desc 整个页面的加载状态
*/
const [categoryData, setCategoryData] = useState([]);
const [operationVisible, setOperationVisible] = useState(false);
const actionRef = useRef();
const supplyPriceRef = useRef();
const [supplyPrice, setSupplyPrice] = useState([]);
const [virtualTreeData, setVirtualTreeData] = useState([]);
const [shopList, setShopList] = useState([]);
const [barndList, setBarndList] = useState([]);
const [virtualBarndList, setVirtualBarndList] = useState([]);
useEffect(() => {
const featchData = async () => {
const { data = [] } = await categoryList();
const { data: virtualData = [] } = await getVirtualCategory();
const { data: shopData = [] } = await getSupplierList();
const { data: barndData = [] } = await getBrandList();
const virtualBarndData = barndData.filter(item => item.name === '虚拟商品');
setCategoryData(data);
setVirtualTreeData(virtualData);
setShopList(shopData);
setBarndList(barndData);
setVirtualBarndList(virtualBarndData);
};
featchData();
}, []);
const toolBarList = [
<Button key="getOffGoodsShelf" type="primary" onClick={() => setOperationVisible(true)}>
新增商品
</Button>,
<Button key="putGoodsShelf">模版</Button>,
<Button key="tags" type="primary">
批量修改库存
</Button>,
];
console.log(operationVisible);
return (
<PageHeaderWrapper>
<ProTable
className={styled.protable}
actionRef={actionRef}
columns={Goodscolumns({ categoryData, supplyPrice, setSupplyPrice, supplyPriceRef })}
params={{ ...supplyPrice }}
request={params => {
// 表单搜索项会从 params 传入,传递给后端接口。
const [supplyPriceMin, supplyPriceMax] = supplyPrice;
return query({ supplyPriceMin, supplyPriceMax, ...params });
}}
rowKey="skuId"
bordered
scroll={{ x: 1500 }}
search={{
collapsed: false,
}}
toolBarRender={() => toolBarList}
pagination={{
showQuickJumper: true,
defaultPageSize: 10,
size: 'default',
showTitle: false,
showTotal: () => null,
}}
onReset={() => {
setSupplyPrice([]);
}}
options={{
density: false,
fullScreen: false,
setting: false,
reload: false,
}}
/>
<OperationModal
operationVisible={operationVisible}
setOperationVisible={setOperationVisible}
categoryData={categoryData} // 实体商品类目
virtualTreeData={virtualTreeData} // 虚拟商品类目
shopList={shopList} // 供货商数据
barndList={barndList}
virtualBarndList={virtualBarndList}
/>
{/* rowSelection={rowSelection} */}
{/* <div>helloworld</div> */}
</PageHeaderWrapper>
);
};
import * as api from './service';
const Model = {
namespace: 'goodsManage',
state: {
tableData: {},
shopList: [],
statusList: [],
cid1List: [],
cid2List: [],
cid3List: [],
treeData: [],
},
effects: {
*getList({ payload }, { call, put }) {
const params = payload;
const productCategoryId = payload?.productCategoryId || [];
params.productCategoryId =
(productCategoryId.length && productCategoryId[productCategoryId.length - 1]) || '';
const { data } = yield call(api.searchList, params);
if (!data) return;
yield put({
type: 'saveData',
payload: {
tableData: data,
},
});
},
*getDataList({ payload }, { call, put, all }) {
const [[shopList], [statusList]] = yield all([
yield call(api.shopList, payload),
yield call(api.statusList, payload),
]);
if (!shopList && !statusList) return;
yield put({
type: 'dataList',
payload: {
shopList,
statusList,
},
});
},
*categoryList({ payload }, { call, put }) {
const [data] = yield call(api.categoryList, payload.value);
if (!data) return;
yield put({
type: 'saveCategory',
payload: {
[payload.categoryNum]: data,
},
});
},
},
reducers: {
saveData(state, action) {
const data = action.payload;
return { ...state, ...data };
},
dataList(state, action) {
const data = action.payload;
return { ...state, ...data };
},
saveCategory(state, action) {
const data = action.payload;
return { ...state, ...data };
},
},
};
export default Model;
import { Modal, Input, notification } from 'antd';
import React, { Component } from 'react';
const { TextArea } = Input;
// eslint-disable-next-line react/prefer-stateless-function
class reasonModal extends Component {
state = {
remarks: '',
};
inputChange = ({ target: { value } }) => {
this.setState({ remarks: value });
};
onCancel = () => {
this.setState({ remarks: '' });
this.props.onCancel();
};
submit = () => {
if (!this.state.remarks) {
notification.error({
message: '请输入下架原因',
});
return;
}
this.props.submit(this.state.remarks);
this.setState({ remarks: '' });
};
render() {
const { visible } = this.props;
return (
<Modal
title="下架原因"
visible={visible}
onOk={this.submit}
onCancel={() => this.onCancel()}
width="700px"
>
<div>
<TextArea value={this.state.remarks} rows={4} onChange={this.inputChange} />
</div>
</Modal>
);
}
}
export default reasonModal;
// import fileSaver from 'file-saver';
import request from '@/utils/request';
import config from '../../../config/env.config';
import { stringify } from 'qs';
import _ from 'lodash';
const { kdspApi } = config;
// const kdspApi = 'http://yapi.quantgroups.com/mock/389';
// 分页查询所有数据
const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
export const query = async params => {
const tempParams = {
...params,
startDate: params.dateRange?.[0],
endDate: params.dateRange?.[1],
pageNo: params.current,
};
delete tempParams.dateRange;
delete tempParams.current;
try {
const {
data: { current, records, total, size },
} = await request.post('/product/page', {
prefix: config.kdspApi,
data: stringify(_.omitBy(tempParams, v => !v)),
headers,
});
return {
current,
data: records,
total,
pageSize: size,
};
} catch (error) {
return {};
}
};
// export async function searchList(params) {
// return request.post('/product/page', {
// prefix: kdspApi,
// data: stringify(_.omitBy(params, v => !v)),
// headers,
// });
// }
// 新增商品
export async function addGoods(params) {
return request.post('/product/add', {
prefix: kdspApi,
params,
});
}
// 编辑商品
export async function editGoods(params) {
return request.post('/product/edit', {
prefix: kdspApi,
params,
});
}
// 供应商列表
export async function getSupplierList() {
return request.post('/channel/supplier/list', {
prefix: kdspApi,
});
}
// 获取商品品牌
export async function getBrandList() {
return request.post('/product/brand/list', {
prefix: kdspApi,
});
}
// 编辑--获取详情
export async function spuDetail(params) {
return request.post('/product/detail', {
prefix: kdspApi,
params,
headers,
});
}
// 商品规格
export async function getSpecList() {
return request.post('/product/spec/list', {
prefix: kdspApi,
});
}
// 查询图片素材
export async function getImageInfo(spuNo) {
return request.post('/product/imageInfo', {
params: { spuNo },
prefix: kdspApi,
headers,
});
}
// 状态
export async function statusList() {
return request.post('/api/kdsp/op/mch_sku/status_list');
}
// 商品来源
export async function skuSource() {
return request.post('/api/kdsp/sku/sku-info/getSkuSource');
}
// 商品分类
export async function categoryList() {
return request.post('/product/category/getAll', {
prefix: kdspApi,
headers,
});
}
// 批量修改
export async function uploadFile(file) {
const params = new FormData();
params.append('file', file);
const data = await request.post('/product/item/batchUpdate', params, {
rawData: true,
});
return data;
}
// 批量倒入京东skus
export async function importJdSkus(file, sourceType) {
const params = new FormData();
params.append('file', file);
params.append('sourceType', sourceType);
const data = await request.post('/product/importJdSkus', params, {
rawData: true,
});
return data;
}
// 批量上架
export async function pushed(data) {
return request.post('/api/kdsp/op/mch_sku/pushed', data);
}
// 批量下架
export async function offline(data) {
return request.post('/api/kdsp/op/mch_sku/offline', data);
}
// 最新批量上下架
export async function batchPushedOrOffline(data) {
return request.post('/product/pushedOrOffline/batch', data, {
emulateJSON: true,
});
}
// 商品修改
export async function update(params) {
return request.post('/api/kdsp/op/mch_sku/update', params, {
emulateJSON: true,
});
}
// 商品修改日志
export async function changeLog(params) {
return request.post('/product/logs/page', params, {
emulateJSON: true,
});
}
// 商品详情
export async function detail(params) {
return request.post('/product/supply/list', params, {
emulateJSON: true,
});
}
// 拉去京东图片
export async function getJdPicList(params) {
const [data] = await request.post('/product/item/getJdPicList', params, {
emulateJSON: true,
});
return data;
}
// 添加商品
export async function addSku(params) {
const [, error] = await request.post('/api/kdsp/op/mch_sku/add_sku', params);
return error;
}
// 修改商品
export async function updateSku(params) {
const [, error] = await request.post('/api/kdsp/op/mch_sku/update', params);
return error;
}
// 图片上传
export async function uploadImg(files) {
const params = new FormData();
files.map(file => params.append('file', file));
const data = await request.post('/image/upload', params, {
rawData: true,
});
return data;
}
// 修改商品价格
export async function updatePrice(params) {
const [, error] = await request.post('/product/item/updatePrice', params, {
emulateJSON: true,
});
return error;
}
// 修改商品库存
export async function updateStock(params) {
const [, error] = await request.post('/product/item/updateStock', params, {
emulateJSON: true,
});
return error;
}
// 获取虚拟商品类目
export async function getVirtualCategory() {
const data = await request.post('/product/category/getByParentId', {
prefix: kdspApi,
params: { id: 100018 },
headers,
});
return data;
}
import React from 'react';
import { Button, Popconfirm, Badge } from 'antd';
import styles from './style.less';
export const productType = [
{
value: 1,
title: '自营',
},
{
value: 2,
title: '京东开普勒',
},
{
value: 3,
title: '京东联盟',
},
{
value: 4,
title: '众联',
},
{
value: 5,
title: '企业购',
},
{
value: 6,
title: '企业购直连',
},
];
export function column() {
return [
{
title: 'SKU编码',
dataIndex: 'skuId',
width: 125,
align: 'center',
render: (_, row) => {
if (row.type !== 1) {
return (
<Badge
count={
<div
style={{
color: '#fff',
borderRadius: '3px',
background: '#f5222d',
padding: '2px',
fontSize: '10px',
opacity: 0.7,
}}
>
虚拟
</div>
}
>
<div
style={{
background: '#fbfbfb',
borderRadius: '3px',
padding: '2px',
}}
>
{row.skuId}
</div>
</Badge>
);
}
return (
<div
style={{
background: '#fbfbfb',
borderRadius: '3px',
padding: '2px',
}}
>
{row.skuId}
</div>
);
},
},
{
title: 'SKU商品名称',
width: 135,
align: 'center',
dataIndex: 'skuName',
},
{
title: '价格',
dataIndex: 'marketPrice',
width: 150,
align: 'center',
sorter: (a, b) => a.supplyPrice - b.supplyPrice,
render: (_, row) => (
<div className={styles.price} onClick={() => this.openModal(row)}>
<p>
供货价:<a>{row.supplyPrice.toFixed(2)}</a>
</p>
<p>
市场价:<a>{(row.marketPrice || 0).toFixed(2)}</a>
</p>
</div>
),
},
{
title: '库存',
width: 120,
dataIndex: 'stock',
align: 'center',
sorter: (a, b) => a.stock - b.stock,
render: (_, row) => (
<>
<p>
当前库存:<a onClick={() => this.openModal(row, 'productStock')}>{row.productStock}</a>
</p>
<p>可售库存:{_}</p>
{row.type === 1 && row.productStockWarning > -1 && (
<p>预警值:{row.productStockWarning}</p>
)}
</>
),
},
{
title: '商品状态',
dataIndex: 'stateDesc',
width: 200,
align: 'center',
},
{
title: '操作',
dataIndex: 'action',
width: 120,
align: 'center',
render: (_, row) => (
<div className={styles.actionBtn}>
{row.state !== 6 && (
<Popconfirm
key="up"
placement="topLeft"
title="确定要上架吗?"
onConfirm={() => this.updateStatus(row.skuId, 6, '')}
okText="确定"
cancelText="取消"
>
<Button key="up" size="small" type="primary" className={styles.button}>
上架
</Button>
</Popconfirm>
)}
{row.state === 6 && (
<Button
key="down"
size="small"
type="primary"
className={styles.button}
disabled={row.state !== 6}
onClick={() => this.offLine(row.skuId, 7)}
>
下架
</Button>
)}
<Button
key="edit"
type="primary"
size="small"
className={styles.button}
onClick={() => this.onUpdateInfo(row)}
>
编辑
</Button>
<Button
key="detail"
size="small"
type="primary"
className={styles.button}
onClick={() => this.showSupply(row.spuId, row.type)}
>
供货详情
</Button>
<Button
key="viewP"
type="primary"
size="small"
className={styles.button}
onClick={() => this.audit(row.skuId)}
>
预览
</Button>
<Button
key="log"
size="small"
type="primary"
className={styles.button}
onClick={() => this.viewLog(row.skuId)}
>
查看日志
</Button>
</div>
),
},
];
}
export const disSelectStatus = [2, 5];
export const stateList = [
{ value: 5, label: '未上架' },
{ value: 6, label: '已上架' },
{ value: 7, label: '已下架' },
];
export const productTypeList = [
{ value: 1, label: '实体商品' },
{ value: 2, label: '虚拟充值' },
{ value: 3, label: '虚拟卡券', disabled: true },
];
export const JDSHOPID = [3, 5, 6];
.formItem {
width: 300px;
}
.itemSection {
display: inline-block;
width: calc(50% - 12px);
margin-right: 0 !important;
}
.itemLine {
display: inline-block;
width: 24px;
text-align: center;
}
.button {
margin: 5px;
}
.selectWidth {
width: 200px;
}
.btngroup {
margin: 10px;
}
.filterModal {
margin: 20px 0;
}
.footerButton {
position: absolute;
bottom: 20px;
left: 0;
}
.tabletop {
margin-top: 20px;
}
.logBtn {
display: inherit;
margin: 20px auto;
}
.linkInput {
width: 310px !important;
}
.picBtn {
margin-top: 5px;
margin-left: 10px;
}
.pullBtn {
position: absolute;
top: -30px;
left: 700px;
}
.price {
// text-align: left;
cursor: pointer;
}
.searchForm {
:global {
.ant-form-item-label {
width: 120px;
}
}
}
.queryBtn {
margin-left: 45px;
}
.actionBtn {
button {
width: 75px;
}
}
.pagination {
margin-top: 10px;
}
.imgBorder {
margin: 5px 0;
padding-left: 20px;
background: #fff;
border: 1px solid #efefef;
border-radius: 10px;
}
.state {
font-size: 13px;
}
.card {
margin-top: 15px;
margin-bottom: 15px;
}
.modal {
background: #ddd;
}
.warning {
margin-top: -20px;
color: red;
}
.iptNumRight {
margin-right: 0 !important;
}
import React, { useEffect } from 'react';
import { categoryList } from './service';
// export const useCategory = () => {
// useEffect(() => {
// const { data } = await categoryList();
// return [data];
// }, [])
// };
import { Modal, Button, Row, Col } from 'antd';
import React, { Component } from 'react';
import styles from '../style.less';
// eslint-disable-next-line react/prefer-stateless-function
class imgModal extends Component {
state = {
imgModal: false,
selectImg: '',
};
render() {
const { visible, data = [] } = this.props;
const { detailImageList = [], skuSpecImageList = [] } = data;
return (
<Modal
title="图片素材"
visible={visible}
footer={null}
onCancel={this.props.onCancel}
width="700px"
>
<div>
{skuSpecImageList.map(skuImg => (
<Row type="flex" justify="start" align="middle">
<Col span={4}>{`滑动图${skuImg.colorSpecValue || ''}:`}</Col>
<Col span={20}>
{skuImg.skuSpecImageList.map(item => (
<img
onClick={() => {
this.setState({ imgModal: true, selectImg: item });
}}
key={item}
width={200}
alt=""
src={item}
style={{ margin: 5 }}
></img>
))}
</Col>
</Row>
))}
<Row type="flex" justify="start" align="middle">
<Col span={4}>{detailImageList.length ? '详情图:' : ''}</Col>
<Col span={20}>
{detailImageList.map(item => (
<img
onClick={() => {
this.setState({ imgModal: true, selectImg: item });
}}
key={item}
width={200}
alt=""
src={item}
style={{ margin: 5 }}
></img>
))}
</Col>
</Row>
</div>
<Button type="primary" onClick={this.props.onCancel} className={styles.logBtn}>
取消
</Button>
<Modal
title="图片详情"
visible={this.state.imgModal}
footer={null}
onCancel={() => this.setState({ imgModal: false })}
width="800px"
>
<img width="700px" src={this.state.selectImg} alt=""></img>
</Modal>
</Modal>
);
}
}
export default imgModal;
import { Modal, Table, Button, Pagination } from 'antd';
import React, { useState, useEffect } from 'react';
import styles from '../style.less';
import { changeLog } from '../service';
const LogModal = props => {
const [tableData, setTableData] = useState([]);
const [pageNo, setPageNo] = useState(1);
const [pageSize] = useState(20);
const columns = [
{
title: '时间',
dataIndex: 'createdAt',
align: 'center',
},
{
title: '变更字段',
align: 'center',
dataIndex: 'changeType',
},
{
title: '变更后内容',
dataIndex: 'afterChangeValue',
align: 'center',
},
{
title: '变更前内容',
dataIndex: 'beforeChangeValue',
align: 'center',
},
{
title: '变更原因',
dataIndex: 'reason',
align: 'center',
},
{
title: '操作账号',
dataIndex: 'operator',
align: 'center',
},
];
const handleSearch = async (page = 1) => {
setPageNo(page);
const { data = {} } = await changeLog({ id: props.id, pageNo: page, pageSize });
setTableData(data);
};
const onPageChange = page => {
handleSearch(page);
};
useEffect(() => {
if (!props.id) return;
handleSearch();
}, [props.id]);
const { visible } = props;
return (
<Modal title="日志详情" visible={visible} footer={null} onCancel={props.onCancel} width="800px">
<Table
dataSource={tableData.records}
bordered
columns={columns}
rowKey={record => record.id}
pagination={false}
scroll={{ y: 300 }}
/>
{tableData.records && (
<Pagination
onChange={onPageChange}
total={tableData.total}
showTotal={total => `共${total}条`}
current={pageNo}
pageSize={pageSize}
className={styles.pagination}
/>
)}
<Button type="primary" onClick={props.onCancel} className={styles.logBtn}>
关闭
</Button>
</Modal>
);
};
export default LogModal;
import { Button, Form, Input, Select, notification, Upload, Cascader, InputNumber } from 'antd';
import React, { Component } from 'react';
import { connect } from 'dva';
import styles from '../style.less';
import { stateList } from '../staticdata';
import { batchPushedOrOffline, offline, uploadFile, importJdSkus } from '../service';
const FormItem = Form.Item;
const { Option } = Select;
@connect(({ goodsManage }) => ({
goodsManage,
}))
class goodsManage extends Component {
componentDidMount() {
this.props.onRef(this);
this.handleSearch();
}
getFieldsValue() {
const { form } = this.props;
return form.getFieldsValue();
}
handleSearch = () => {
this.props.handleSearch(1);
};
onReset = () => {
this.props.form.resetFields();
this.props.onReset();
};
onConfirm = async isOffline => {
const data = isOffline
? await offline(this.props.selectedRowKeys)
: await batchPushedOrOffline({
ids: this.props.selectedRowKeys.join(),
type: 2,
productState: 6,
offlineReason: '',
});
if (data.businessCode !== '0000') {
this.props.onLoad(data.error);
} else {
this.props.onLoad(null);
}
};
addSpu = () => {
this.props.addSpu();
};
addVirtualSpu = () => {
this.props.addVirtualSpu();
};
importSkus = async (info, type) => {
const result = await importJdSkus(info.file, type);
if (result.businessCode === '0000') {
this.handleSearch();
notification.success({
message: '商品上传成功',
});
} else {
notification.warning({
message: result.msg,
});
}
};
render() {
const {
form: { getFieldDecorator, getFieldValue },
treeData,
} = this.props;
const selectW = { width: 250 };
const iptNumWidth = { width: 118 };
const that = this;
const uploadProps = {
name: 'file',
async customRequest(info) {
const result = await uploadFile(info.file);
if (result && result.businessCode === '0000') {
that.handleSearch();
notification.success({
message: '操作成功',
});
} else {
notification.warning({
message: result.msg,
description: (
<div>
{result.data?.length &&
result.data.map(item => <p>{item.skuNo + item.errSkuMessage}</p>)}
</div>
),
duration: 6,
});
}
},
accept: '.xlsx',
showUploadList: false,
};
const filterOption = (input, op) => op.props.children.includes(input);
return (
<Form name="horizontal_login" layout="inline" className={styles.searchForm}>
<FormItem label="SKU编码">
{getFieldDecorator('skuId', {})(<Input allowClear style={selectW} />)}
</FormItem>
<FormItem label="商品名称">
{getFieldDecorator('skuName', {})(<Input allowClear style={selectW} />)}
</FormItem>
<FormItem label="类目">
{getFieldDecorator('productCategoryId', {})(
<Cascader
style={selectW}
changeOnSelect
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
options={treeData}
/>,
)}
</FormItem>
<FormItem label="第三方SKU编码">
{getFieldDecorator('thirdSkuNo', {})(<Input allowClear style={selectW} />)}
</FormItem>
<FormItem label="审核状态">
{getFieldDecorator('state', {})(
<Select style={selectW} allowClear showSearch filterOption={filterOption}>
{stateList?.map(item => (
<Option key={item.value} value={item.value}>
{item.label}
</Option>
))}
</Select>,
)}
</FormItem>
<FormItem label="供货价区间">
<FormItem className={styles.iptNumRight}>
{getFieldDecorator('supplyPriceMin', {})(<InputNumber style={iptNumWidth} />)}
</FormItem>
<span>--</span>
<FormItem className={styles.iptNumRight}>
{getFieldDecorator('supplyPriceMax', {})(
<InputNumber style={iptNumWidth} min={getFieldValue('supplyPriceMin')} />,
)}
</FormItem>
</FormItem>
<FormItem className={styles.queryBtn}>
<Button onClick={() => this.handleSearch()} type="primary" className={styles.button}>
查询
</Button>
<Button onClick={() => this.onReset()} type="primary" className={styles.button}>
重置
</Button>
</FormItem>
<FormItem style={{ width: '100%', textAlign: 'right' }}>
<Button type="primary" className={styles.button} onClick={this.addSpu}>
新增商品
</Button>
<Button
className={styles.button}
type="primary"
icon="download"
ghost
onClick={() => {
window.location.href =
'https://kdspstatic.q-gp.com/batchUpdateSkuImportTempletv3.xlsx';
}}
>
模版
</Button>
<Upload {...uploadProps}>
<Button type="primary" className={styles.button}>
批量库存修改
</Button>
</Upload>
</FormItem>
</Form>
);
}
}
export default Form.create()(goodsManage);
import { Modal, Table, Button } from 'antd';
import React, { Component } from 'react';
import styles from '../style.less';
class DetailModal extends Component {
getColumns = (firstSpe, secondSpe, type) => {
const arr = [
{
title: '供应商',
dataIndex: 'supplierName',
key: 'supplierName',
align: 'center',
width: 50,
render: (val, row) => {
const obj = {
children: val,
props: {},
};
if (row.listLength) {
obj.props.rowSpan = row.listLength;
} else {
obj.props.rowSpan = 0;
}
return obj;
},
},
];
if (firstSpe) {
arr.push({
title: firstSpe,
dataIndex: 'firstSpecValue',
key: 'firstSpecValue',
align: 'center',
width: 50,
render: (val, row) => {
const obj = {
children: val,
props: {},
};
if (row.length) {
obj.props.rowSpan = row.length;
} else {
obj.props.rowSpan = 0;
}
return obj;
},
});
}
if (secondSpe) {
arr.push({
title: secondSpe,
align: 'center',
key: 'secondSpecValue',
dataIndex: 'secondSpecValue',
width: 50,
});
}
const newTable = arr.concat([
{
title: '供应商编码',
align: 'center',
key: 'supplierCode',
dataIndex: 'supplierCode',
width: 50,
},
{
title: '库存',
align: 'center',
key: 'stock',
dataIndex: 'stock',
width: 50,
},
{
title: '供货价',
align: 'center',
key: 'supplyPrice',
dataIndex: 'supplyPrice',
width: 50,
},
{
title: '市场价',
align: 'center',
key: 'marketPrice',
dataIndex: 'marketPrice',
width: 50,
},
{
title: '国际编码',
align: 'center',
key: 'thirdSkuNo',
dataIndex: 'thirdSkuNo',
width: 50,
},
]);
if (type === 1) {
newTable.splice(newTable.length - 1, 0, {
title: '重量(kg)',
align: 'center',
key: 'weight',
dataIndex: 'weight',
width: 90,
});
}
return newTable;
};
updateStatus = (row, productState) => {
this.props.updateStatus(row, productState);
};
dataInit = list => {
const obj = {};
let finialList = [];
list.map(item => {
obj[item.firstSpecValue] = [];
return obj;
});
list.map(item => obj[item.firstSpecValue].push(item));
const keys = Object.keys(obj);
// eslint-disable-next-line no-return-assign
keys.map(key => {
obj[key].forEach((i, index) => {
if (index === 0) {
i.length = obj[key].length;
}
});
finialList = finialList.concat(obj[key]);
return finialList;
});
if (finialList.length) {
finialList[0].listLength = finialList.length;
}
return finialList;
};
render() {
const { visible, data = [], type } = this.props;
const initdata = this.dataInit(data);
const firstName = data.length ? data[0].firstSpec : '';
const secondName = data.length ? data[0].secondSpec : '';
return (
<Modal
title="供货详情"
visible={visible}
footer={null}
onCancel={this.props.onCancel}
width="900px"
>
<Table
dataSource={initdata}
bordered
columns={this.getColumns(firstName, secondName, type)}
rowKey="id"
pagination={false}
scroll={{ x: '100%' }}
/>
<Button type="primary" onClick={this.props.onCancel} className={styles.logBtn}>
关闭
</Button>
</Modal>
);
}
}
export default DetailModal;
This diff is collapsed.
.inputNW {
width: 200px;
}
.center {
text-align: center;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment