前端:商品添加。提交部分,让小范帮忙看看~

pull/1/head
YunaiV 2019-03-18 19:05:43 +08:00
parent ecc5cf8887
commit 92b5f6baff
7 changed files with 474 additions and 77 deletions

View File

@ -0,0 +1,101 @@
import React, {PureComponent} from "react";
import {Select} from "antd";
const Option = Select.Option;
export default class ProductAttrSelectFormItem extends PureComponent {
handleSelectAttr = (value, option) => {
// console.log(value);
// console.log(option);
// debugger;
const { dispatch, index } = this.props;
// let attrIndex = option.key.substring(option.key.indexOf('option-attr-') + 'option-attr-'.length, option.key.lastIndexOf('-'));
// console.log('attrIndex: ' + attrIndex);
// debugger;
dispatch({
type: 'productSpuAddOrUpdate/selectAttr',
payload: {
attrIndex: index,
attr: {
id: option.props.value,
name: option.props.children,
values: []
}
},
});
}
handleSelectAttrValue = (values, options) => {
let attrValues = [];
const { dispatch, index } = this.props;
// debugger;
// console.log('x' + this.children[0]);
// let firstOption = this.children[0];
// let attrIndex = firstOption.key.substring(firstOption.key.indexOf('option-attr-value-') + 'option-attr-value-'.length, firstOption.key.lastIndexOf('-'));
for (let i in options) {
let option = options[i];
attrValues.push({
id: parseInt(option.props.value),
name: option.props.children,
});
}
dispatch({
type: 'productSpuAddOrUpdate/selectAttrValues',
payload: {
attrIndex: index,
attrValues: attrValues,
},
});
// debugger;
// console.log(value);
}
render() {
const {attr, allAttrTree, selectedAttrIds, index} = this.props;
// console.log('i: ' + i);
// 1. 规格
let attrOptions = [];
// allAttrTree.unshift(attr);
// debugger;
for (let j in allAttrTree) {
let allAttr = allAttrTree[j];
if (selectedAttrIds.has(allAttr.id) && allAttr.id !== attr.id) {
continue;
}
attrOptions.push(<Option key={`option-attr-${index}-${allAttr.id}`} value={allAttr.id}>{allAttr.name}</Option>);
}
// 2. 规格值
let attrValueOptions = [];
// debugger;
if (attr.id) {
// 2.1 先找到规格值的数组
let attrValues = [];
for (let j in allAttrTree) {
let allAttr = allAttrTree[j];
if (attr.id === allAttr.id) {
attrValues = allAttr.values;
break;
}
}
// 2.2 生成规格值的 HTML
for (let j in attrValues) {
let attrValue = attrValues[j];
attrValueOptions.push(<Option key={`option-attr-value-${index}-${attrValue.id}`}
value={attrValue.id + ''}>{attrValue.name}</Option>); // + '' 的原因是多选必须是字符串
}
}
// 3. 拼装最终,添加到 attrTreeHTML 中
return <div key={`div-attr-${index}`}>
<Select key={`select-attr-${index}`} style={{width: 120}} placeholder='请选择规格' onChange={this.handleSelectAttr}>
{attrOptions}
</Select>
<Select key={`select-attr-value-${index}`} mode={"tags"} style={{width: 260}} placeholder='请选择规格值'
onChange={this.handleSelectAttrValue}>
{attrValueOptions}
</Select>
</div>;
}
}

View File

@ -0,0 +1,85 @@
import React, {PureComponent} from "react";
import {InputNumber, Select, Table} from "antd";
import Input from "antd/es/input";
const Option = Select.Option;
class SkuInputNumber extends PureComponent {
handleChange = value => {
// debugger;
const { dispatch, index, dataIndex } = this.props;
if (dataIndex === 'price') {
dispatch({
type: 'productSpuAddOrUpdate/inputSkuPrice',
payload: {
index: index,
price: value
},
});
} else if (dataIndex === 'quantity') {
dispatch({
type: 'productSpuAddOrUpdate/inputSkuQuantity',
payload: {
index: index,
quantity: value
},
});
}
}
render() {
return <InputNumber placeholder="请输入" onChange={this.handleChange} />
}
}
export default class ProductSkuAddOrUpdateTable extends PureComponent {
render() {
let that = this;
// debugger;
// console.log('ProductSkuAddOrUpdateTable');
const {attrTree, skus, dispatch} = this.props;
let columns = [];
for (let i in attrTree) {
let attr = attrTree[i];
columns.push({
title: attr.name,
dataIndex: 'attrs[i]',
render(value, record) {
return record.attrs[i].name;
}
})
}
columns.push({
title: '价格',
dataIndex: 'price',
render(value, record, index) {
let props = {
record: record,
index: index,
dispatch: dispatch,
dataIndex: 'price'
};
return <SkuInputNumber {...props} />;
}
});
columns.push({
title: '库存',
dataIndex: 'quantity',
render(value, record, index) {
let props = {
record: record,
index: index,
dispatch: dispatch,
dataIndex: 'quantity'
};
return <SkuInputNumber {...props} />;
}
});
return <Table columns={columns} dataSource={skus} rowKey="index" />;
// return <div />;
}
}

View File

@ -44,4 +44,9 @@ export default {
'menu.account.settings': '个人设置', 'menu.account.settings': '个人设置',
'menu.account.trigger': '触发报错', 'menu.account.trigger': '触发报错',
'menu.account.logout': '退出登录', 'menu.account.logout': '退出登录',
// 商品相关
'menu.product': '商品管理',
'menu.product.product-spu-list': '商品管理',
'menu.product.product-spu-add': '商品添加',
'menu.product.product-category-list': '商品分类',
}; };

View File

@ -1,5 +1,5 @@
import { message } from 'antd'; import { message } from 'antd';
import { productCategoryTree, productCategoryAdd, productCategoryUpdate, productCategoryUpdateStatus, productCategoryDelete } from '../../services/product'; import { productCategoryTree, productSpuAdd, productCategoryUpdate, productCategoryUpdateStatus, productCategoryDelete } from '../../services/product';
export default { export default {
namespace: 'productSpuAddOrUpdate', namespace: 'productSpuAddOrUpdate',
@ -15,6 +15,16 @@ export default {
// name: // // name: //
// }] // }]
// } // }
],
skus: [
// {
// attrs: [{
// id: // 规格值编号
// name: // 规格值名
// }],
// price: // 价格
// quantity: // 数量
// }
] ]
}, },
@ -63,7 +73,7 @@ export default {
*addAttr({ payload }, { call, put }) { *addAttr({ payload }, { call, put }) {
// const { queryParams } = payload; // const { queryParams } = payload;
// const response = yield call(productCategoryTree, queryParams); // const response = yield call(productCategoryTree, queryParams);
message.info('调试:添加规格成功!'); // message.info('调试:添加规格成功!');
yield put({ yield put({
type: 'addAttrSuccess', type: 'addAttrSuccess',
payload: { payload: {
@ -74,12 +84,46 @@ export default {
*selectAttr({ payload }, { call, put }) { *selectAttr({ payload }, { call, put }) {
// const { queryParams } = payload; // const { queryParams } = payload;
// const response = yield call(productCategoryTree, queryParams); // const response = yield call(productCategoryTree, queryParams);
message.info('调试:添加规格成功!'); // message.info('调试:选择规格成功!');
yield put({ yield put({
type: 'selectAttrSuccess', type: 'selectAttrSuccess',
payload: payload, payload: payload,
}); });
}, },
*selectAttrValues({ payload }, { call, put }) {
// const { queryParams } = payload;
// const response = yield call(productCategoryTree, queryParams);
// message.info('调试:选择规格值成功!');
yield put({
type: 'selectAttrValueSuccess',
payload: payload,
});
},
*inputSkuPrice({ payload }, { call, put }) {
// debugger;
yield put({
type: 'inputSkuPriceSuccess',
payload: payload,
});
},
*inputSkuQuantity({ payload }, { call, put }) {
// debugger;
yield put({
type: 'inputSkuQuantitySuccess',
payload: payload,
});
},
*add({ payload }, { call, put }) {
const { callback, body } = payload;
const response = yield call(productSpuAdd, body);
if (callback) {
callback(response);
}
yield put({
type: 'tree',
payload: {},
});
},
}, },
reducers: { reducers: {
@ -99,6 +143,62 @@ export default {
...state ...state
} }
}, },
selectAttrValueSuccess(state, {payload}) {
// debugger;
// console.log(state);
state.attrTree[payload.attrIndex].values = payload.attrValues;
// 生成 skus 值
let skus = [];
let skuSize = 1;
for (let i in state.attrTree) { // 先计算 sku 数量
let attr = state.attrTree[i];
skuSize = skuSize * attr.values.length;
}
// console.log('skuSize: ' + skuSize);
for (let i = 0; i < skuSize; i++) { // 初始化 sku 格子
skus.push({
attrs: [],
price: undefined,
quantity: undefined,
});
}
for (let i = 0; i < state.attrTree.length; i++) { // 初始化 sku 格子里的 attrs
for (let j = 0; j < skuSize; j++) {
// let values = state.attrTree[i].values;
// let attr = values[j % values.length];
// skus[i].attrs.push({
// id: attr.id,
// name: attr.name,
// });
let values = state.attrTree[i].values;
let attr = values[j % values.length];
skus[j].attrs.push({
id: attr.id,
name: attr.name,
});
}
}
state.skus = skus;
// debugger;
// console.l og('skus: ' + skus);
return {
...state
}
},
inputSkuPriceSuccess(state, {payload}) {
// debugger;
state.skus[payload.index].price = payload.price;
return {
...state
}
},
inputSkuQuantitySuccess(state, {payload}) {
// debugger;
state.skus[payload.index].quantity = payload.quantity;
return {
...state
}
},
treeSuccess(state, { payload }) { treeSuccess(state, { payload }) {
return { return {
...state, ...state,

View File

@ -7,6 +7,8 @@ import {Card, Form, Input, Radio, Button, Table, Select} from 'antd';
import PageHeaderWrapper from '@/components/PageHeaderWrapper'; import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import styles from './ProductSpuAddOrUpdate.less'; import styles from './ProductSpuAddOrUpdate.less';
import ProductAttrSelectFormItem from "../../components/Product/ProductAttrSelectFormItem";
import ProductSkuAddOrUpdateTable from "../../components/Product/ProductSkuAddOrUpdateTable";
const FormItem = Form.Item; const FormItem = Form.Item;
const RadioGroup = Radio.Group; const RadioGroup = Radio.Group;
@ -19,7 +21,8 @@ const Option = Select.Option;
productAttrList, productAttrList,
productSpuAddOrUpdate, productSpuAddOrUpdate,
allAttrTree: productAttrList.tree, allAttrTree: productAttrList.tree,
attrTree: productSpuAddOrUpdate.attrTree attrTree: productSpuAddOrUpdate.attrTree,
skus: productSpuAddOrUpdate.skus,
})) }))
@Form.create() @Form.create()
@ -44,18 +47,18 @@ class ProductSpuAddOrUpdate extends Component {
}); });
} }
handleSubmit = e => { // handleSubmit = e => {
const { dispatch, form } = this.props; // const { dispatch, form } = this.props;
e.preventDefault(); // e.preventDefault();
form.validateFieldsAndScroll((err, values) => { // form.validateFieldsAndScroll((err, values) => {
if (!err) { // if (!err) {
dispatch({ // dispatch({
type: 'form/submitRegularForm', // type: 'form/submitRegularForm',
payload: values, // payload: values,
}); // });
} // }
}); // });
} // }
handleAddAttr = e => { handleAddAttr = e => {
// alert('你猜'); // alert('你猜');
@ -67,74 +70,171 @@ class ProductSpuAddOrUpdate extends Component {
}); });
} }
handleSelectAttr = (value, option) => { handleSubmit = e => {
console.log(value);
console.log(option);
debugger;
const { dispatch } = this.props;
let attrIndex = option.key.substring(option.key.indexOf('option-attr-') + 'option-attr-'.length, option.key.lastIndexOf('-'));
console.log('attrIndex: ' + attrIndex);
debugger; debugger;
e.preventDefault();
const { skus, dispatch } = this.props;
// 生成 skuStr 格式
let skuStr = []; // 因为提交是字符串格式
for (let i in skus) {
let sku = skus[i];
if (!sku.price || !sku.quantity) {
continue;
}
let newAttr = {
attrs: [],
price: sku.price,
quantity: sku.quantity,
}
for (let j in sku.attrs) {
newAttr.attrs.push(sku.attrs[j].id);
}
skuStr.push(newAttr);
}
if (skuStr.length === 0) {
alert('请设置商品规格!');
return;
}
this.props.form.validateFields((err, values) => {
if (!err) {
dispatch({ dispatch({
type: 'productSpuAddOrUpdate/selectAttr', type: 'productSpuAddOrUpdate/add',
payload: { payload: {
attrIndex: attrIndex, body: {
attr: { ...values,
id: option.props.value, skuStr: JSON.stringify(skuStr)
name: option.props.children,
} }
}, },
}); });
} }
});
// console.log(fields);
}
// handleSelectAttr = (value, option) => {
// // console.log(value);
// // console.log(option);
// // debugger;
// const { dispatch } = this.props;
// let attrIndex = option.key.substring(option.key.indexOf('option-attr-') + 'option-attr-'.length, option.key.lastIndexOf('-'));
// // console.log('attrIndex: ' + attrIndex);
// // debugger;
// dispatch({
// type: 'productSpuAddOrUpdate/selectAttr',
// payload: {
// attrIndex: attrIndex,
// attr: {
// id: option.props.value,
// name: option.props.children,
// values: []
// }
// },
// });
// }
//
// handleSelectAttrValue = (values, options) => {
// let attrValues = [];
// const { dispatch } = this.props;
// debugger;
// // console.log('x' + this.children[0]);
// let firstOption = this.children[0];
// // let attrIndex = firstOption.key.substring(firstOption.key.indexOf('option-attr-value-') + 'option-attr-value-'.length, firstOption.key.lastIndexOf('-'));
// let attrIndex = 0;
// for (let i in options) {
// let option = options[i];
// attrValues.push({
// id: parseInt(option.props.value),
// name: option.props.children,
// });
// }
// dispatch({
// type: 'productSpuAddOrUpdate/selectAttrValues',
// payload: {
// attrIndex: attrIndex,
// attrValues: attrValues,
// },
// });
// // debugger;
//
// // console.log(value);
// }
render() { render() {
// debugger; // debugger;
const { form, data, attrTree, allAttrTree } = this.props; const { form, skus, attrTree, allAttrTree, dispatch } = this.props;
const that = this; // const that = this;
// 规格明细
const columns = [
{
title: '颜色',
dataIndex: 'price'
},
{
title: '价格',
dataIndex: 'price',
render(val) {
return <span>{status[val]}</span>;
},
},
{
title: '库存',
dataIndex: 'quantity',
}
];
// 添加规格 // 添加规格
// debugger; // debugger;
let attrTreeHTML = []; let attrTreeHTML = [];
if (attrTree && attrTree.length > 0) { if (attrTree && attrTree.length > 0) {
// 已选择的的规格集合
let selectedAttrIds = new Set();
for (let i in attrTree) { for (let i in attrTree) {
let attr = attrTree[i]; let attr = attrTree[i];
// console.log('i: ' + i); selectedAttrIds.add(attr.id);
// 1. 规格
let options = [];
for (let j in allAttrTree) {
let attr = allAttrTree[j];
options.push(<Option key={`option-attr-${i}-${attr.id}`} value={attr.id}>{attr.name}</Option>);
} }
// 2. 规格值 // 创建每个规格下拉框的 HTML
for (let i in attrTree) {
// 3. 拼装最终,添加到 attrTreeHTML 中 let attr = attrTree[i];
attr = <div key={`div-attr-${i}`}> let itemProps = {
<Select key={`select-attr-${i}`} style={{ width: 120 }} placeholder='请选择规格' onChange={that.handleSelectAttr}> attr: attr,
{options} allAttrTree: allAttrTree,
</Select> dispatch: dispatch,
</div>; selectedAttrIds: selectedAttrIds,
attrTreeHTML.push(attr); index: i // 位置。不然无法正确修改 Model 指定位置的数据
};
attrTreeHTML.push(<ProductAttrSelectFormItem {...itemProps} />);
} }
} }
// if (attrTree && attrTree.length > 0) {
// for (let i in attrTree) {
// let attr = attrTree[i];
// // console.log('i: ' + i);
// // 1. 规格
// let attrOptions = [];
// for (let j in allAttrTree) {
// let attr = allAttrTree[j];
// attrOptions.push(<Option key={`option-attr-${i}-${attr.id}`} value={attr.id}>{attr.name}</Option>);
// }
// // 2. 规格值
// let attrValueOptions = [];
// // debugger;
// if (attr.id) {
// // 2.1 先招到规格值的数组
// let attrValues = [];
// for (let j in allAttrTree) {
// let allAttr = allAttrTree[j];
// if (attr.id === allAttr.id) {
// attrValues = allAttr.values;
// break;
// }
// }
// // 2.2 生成规格值的 HTML
// for (let j in attrValues) {
// let attrValue = attrValues[j];
// attrValueOptions.push(<Option key={`option-attr-value-${i}-${attrValue.id}`} value={attrValue.id + ''}>{attrValue.name}</Option>); // + '' 的原因是,多选必须是字符串
// }
// }
// // 3. 拼装最终,添加到 attrTreeHTML 中
// attr = <div key={`div-attr-${i}`}>
// <Select key={`select-attr-${i}`} style={{ width: 120 }} placeholder='请选择规格' onChange={that.handleSelectAttr}>
// {attrOptions}
// </Select>
// <Select key={`select-attr-value-${i}`} mode={"tags"} style={{ width: 260 }} placeholder='请选择规格值' onChange={that.handleSelectAttrValue}>
// {attrValueOptions}
// </Select>
// </div>;
// attrTreeHTML.push(attr);
// }
// }
// 规格明细
let productSkuProps = {
attrTree: attrTree,
skus: skus,
dispatch: dispatch,
};
// console.log(productSkuProps);
return ( return (
<PageHeaderWrapper title=""> <PageHeaderWrapper title="">
@ -189,13 +289,12 @@ class ProductSpuAddOrUpdate extends Component {
</div> </div>
)} )}
</FormItem> </FormItem>
{/*<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="规格明细">*/} <FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="规格明细">
{/*{form.getFieldDecorator('visible', {*/}
{/*initialValue: 1, // TODO 修改*/}
{/*})(*/}
{/*<Table defaultExpandAllRows={true} columns={columns} rowKey="id" />*/} {/*<Table defaultExpandAllRows={true} columns={columns} rowKey="id" />*/}
{/*)}*/} <ProductSkuAddOrUpdateTable {...productSkuProps} />
{/*</FormItem>*/}
<Button type="primary" htmlType="submit" style={{ marginLeft: 8 }} onSubmit={this.handleSubmit}>保存</Button>
</FormItem>
</Form> </Form>
</Card> </Card>
</PageHeaderWrapper> </PageHeaderWrapper>

View File

@ -44,6 +44,13 @@ export async function productSpuPage(params) {
}); });
} }
export async function productSpuAdd(params) {
return request(`/product-api/admins/spu/add?${stringify(params)}`, {
method: 'POST',
body: {},
});
}
// product attr + attr value // product attr + attr value
export async function productAttrTree(params) { export async function productAttrTree(params) {

View File

@ -52,7 +52,7 @@ public class AdminsProductSpuController {
@RequestParam("sellPoint") String sellPoint, @RequestParam("sellPoint") String sellPoint,
@RequestParam("description") String description, @RequestParam("description") String description,
@RequestParam("cid") Integer cid, @RequestParam("cid") Integer cid,
@RequestParam("picURLs") List<String> picUrls, @RequestParam("picUrls") List<String> picUrls,
@RequestParam("visible") Boolean visible, @RequestParam("visible") Boolean visible,
@RequestParam("skuStr") String skuStr) { // TODO 芋艿,因为考虑不使用 json 接受参数,所以这里手动转。 @RequestParam("skuStr") String skuStr) { // TODO 芋艿,因为考虑不使用 json 接受参数,所以这里手动转。
// 创建 ProductSpuAddDTO 对象 // 创建 ProductSpuAddDTO 对象