From 92b5f6baff9495b89d01133960f6d99f217b7866 Mon Sep 17 00:00:00 2001 From: YunaiV <> Date: Mon, 18 Mar 2019 19:05:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=AB=AF=EF=BC=9A=E5=95=86=E5=93=81?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E3=80=82=E6=8F=90=E4=BA=A4=E9=83=A8=E5=88=86?= =?UTF-8?q?=EF=BC=8C=E8=AE=A9=E5=B0=8F=E8=8C=83=E5=B8=AE=E5=BF=99=E7=9C=8B?= =?UTF-8?q?=E7=9C=8B~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Product/ProductAttrSelectFormItem.js | 101 ++++++++ .../Product/ProductSkuAddOrUpdateTable.js | 85 ++++++ admin-web/src/locales/zh-CN/menu.js | 5 + .../models/product/productSpuAddOrUpdate.js | 106 +++++++- .../pages/Product/ProductSpuAddOrUpdate.js | 245 ++++++++++++------ admin-web/src/services/product.js | 7 + .../admins/AdminsProductSpuController.java | 2 +- 7 files changed, 474 insertions(+), 77 deletions(-) create mode 100644 admin-web/src/components/Product/ProductAttrSelectFormItem.js create mode 100644 admin-web/src/components/Product/ProductSkuAddOrUpdateTable.js diff --git a/admin-web/src/components/Product/ProductAttrSelectFormItem.js b/admin-web/src/components/Product/ProductAttrSelectFormItem.js new file mode 100644 index 000000000..3c019d940 --- /dev/null +++ b/admin-web/src/components/Product/ProductAttrSelectFormItem.js @@ -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(); + } + // 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(); // + '' 的原因是,多选必须是字符串 + } + } + // 3. 拼装最终,添加到 attrTreeHTML 中 + return
+ + +
; + } + +} \ No newline at end of file diff --git a/admin-web/src/components/Product/ProductSkuAddOrUpdateTable.js b/admin-web/src/components/Product/ProductSkuAddOrUpdateTable.js new file mode 100644 index 000000000..8e9452b92 --- /dev/null +++ b/admin-web/src/components/Product/ProductSkuAddOrUpdateTable.js @@ -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 + } + +} + +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 ; + } + }); + columns.push({ + title: '库存', + dataIndex: 'quantity', + render(value, record, index) { + let props = { + record: record, + index: index, + dispatch: dispatch, + dataIndex: 'quantity' + }; + return ; + } + }); + return ; + // return
; + } + +} \ No newline at end of file diff --git a/admin-web/src/locales/zh-CN/menu.js b/admin-web/src/locales/zh-CN/menu.js index be480f4df..44bdf9a3f 100644 --- a/admin-web/src/locales/zh-CN/menu.js +++ b/admin-web/src/locales/zh-CN/menu.js @@ -44,4 +44,9 @@ export default { 'menu.account.settings': '个人设置', 'menu.account.trigger': '触发报错', 'menu.account.logout': '退出登录', + // 商品相关 + 'menu.product': '商品管理', + 'menu.product.product-spu-list': '商品管理', + 'menu.product.product-spu-add': '商品添加', + 'menu.product.product-category-list': '商品分类', }; diff --git a/admin-web/src/models/product/productSpuAddOrUpdate.js b/admin-web/src/models/product/productSpuAddOrUpdate.js index f457e333c..da9f0afc8 100644 --- a/admin-web/src/models/product/productSpuAddOrUpdate.js +++ b/admin-web/src/models/product/productSpuAddOrUpdate.js @@ -1,5 +1,5 @@ import { message } from 'antd'; -import { productCategoryTree, productCategoryAdd, productCategoryUpdate, productCategoryUpdateStatus, productCategoryDelete } from '../../services/product'; +import { productCategoryTree, productSpuAdd, productCategoryUpdate, productCategoryUpdateStatus, productCategoryDelete } from '../../services/product'; export default { namespace: 'productSpuAddOrUpdate', @@ -15,6 +15,16 @@ export default { // name: // // }] // } + ], + skus: [ + // { + // attrs: [{ + // id: // 规格值编号 + // name: // 规格值名 + // }], + // price: // 价格 + // quantity: // 数量 + // } ] }, @@ -63,7 +73,7 @@ export default { *addAttr({ payload }, { call, put }) { // const { queryParams } = payload; // const response = yield call(productCategoryTree, queryParams); - message.info('调试:添加规格成功!'); + // message.info('调试:添加规格成功!'); yield put({ type: 'addAttrSuccess', payload: { @@ -74,12 +84,46 @@ export default { *selectAttr({ payload }, { call, put }) { // const { queryParams } = payload; // const response = yield call(productCategoryTree, queryParams); - message.info('调试:添加规格成功!'); + // message.info('调试:选择规格成功!'); yield put({ type: 'selectAttrSuccess', 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: { @@ -99,6 +143,62 @@ export default { ...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 }) { return { ...state, diff --git a/admin-web/src/pages/Product/ProductSpuAddOrUpdate.js b/admin-web/src/pages/Product/ProductSpuAddOrUpdate.js index 7308e0474..2aa0d6660 100644 --- a/admin-web/src/pages/Product/ProductSpuAddOrUpdate.js +++ b/admin-web/src/pages/Product/ProductSpuAddOrUpdate.js @@ -7,6 +7,8 @@ import {Card, Form, Input, Radio, Button, Table, Select} from 'antd'; import PageHeaderWrapper from '@/components/PageHeaderWrapper'; import styles from './ProductSpuAddOrUpdate.less'; +import ProductAttrSelectFormItem from "../../components/Product/ProductAttrSelectFormItem"; +import ProductSkuAddOrUpdateTable from "../../components/Product/ProductSkuAddOrUpdateTable"; const FormItem = Form.Item; const RadioGroup = Radio.Group; @@ -19,7 +21,8 @@ const Option = Select.Option; productAttrList, productSpuAddOrUpdate, allAttrTree: productAttrList.tree, - attrTree: productSpuAddOrUpdate.attrTree + attrTree: productSpuAddOrUpdate.attrTree, + skus: productSpuAddOrUpdate.skus, })) @Form.create() @@ -44,18 +47,18 @@ class ProductSpuAddOrUpdate extends Component { }); } - handleSubmit = e => { - const { dispatch, form } = this.props; - e.preventDefault(); - form.validateFieldsAndScroll((err, values) => { - if (!err) { - dispatch({ - type: 'form/submitRegularForm', - payload: values, - }); - } - }); - } + // handleSubmit = e => { + // const { dispatch, form } = this.props; + // e.preventDefault(); + // form.validateFieldsAndScroll((err, values) => { + // if (!err) { + // dispatch({ + // type: 'form/submitRegularForm', + // payload: values, + // }); + // } + // }); + // } handleAddAttr = e => { // alert('你猜'); @@ -67,74 +70,171 @@ class ProductSpuAddOrUpdate extends Component { }); } - handleSelectAttr = (value, option) => { - console.log(value); - console.log(option); + handleSubmit = e => { 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, - } - }, + 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({ + type: 'productSpuAddOrUpdate/add', + payload: { + body: { + ...values, + skuStr: JSON.stringify(skuStr) + } + }, + }); + } }); + // 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() { // debugger; - const { form, data, attrTree, allAttrTree } = this.props; - const that = this; - - // 规格明细 - const columns = [ - { - title: '颜色', - dataIndex: 'price' - }, - { - title: '价格', - dataIndex: 'price', - render(val) { - return {status[val]}; - }, - }, - { - title: '库存', - dataIndex: 'quantity', - } - ]; + const { form, skus, attrTree, allAttrTree, dispatch } = this.props; + // const that = this; // 添加规格 // debugger; let attrTreeHTML = []; if (attrTree && attrTree.length > 0) { + // 已选择的的规格集合 + let selectedAttrIds = new Set(); for (let i in attrTree) { let attr = attrTree[i]; - // console.log('i: ' + i); - // 1. 规格 - let options = []; - for (let j in allAttrTree) { - let attr = allAttrTree[j]; - options.push(); - } - // 2. 规格值 - - // 3. 拼装最终,添加到 attrTreeHTML 中 - attr =
- -
; - attrTreeHTML.push(attr); + selectedAttrIds.add(attr.id); + } + // 创建每个规格下拉框的 HTML + for (let i in attrTree) { + let attr = attrTree[i]; + let itemProps = { + attr: attr, + allAttrTree: allAttrTree, + dispatch: dispatch, + selectedAttrIds: selectedAttrIds, + index: i // 位置。不然无法正确修改 Model 指定位置的数据 + }; + attrTreeHTML.push(); } } + // 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(); + // } + // // 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(); // + '' 的原因是,多选必须是字符串 + // } + // } + // // 3. 拼装最终,添加到 attrTreeHTML 中 + // attr =
+ // + // + //
; + // attrTreeHTML.push(attr); + // } + // } + // 规格明细 + let productSkuProps = { + attrTree: attrTree, + skus: skus, + dispatch: dispatch, + }; + // console.log(productSkuProps); return ( @@ -189,13 +289,12 @@ class ProductSpuAddOrUpdate extends Component {
)} - {/**/} - {/*{form.getFieldDecorator('visible', {*/} - {/*initialValue: 1, // TODO 修改*/} - {/*})(*/} - {/*
*/} - {/*)}*/} - {/**/} + + {/*
*/} + + + + diff --git a/admin-web/src/services/product.js b/admin-web/src/services/product.js index c0a51274e..f8181bf02 100644 --- a/admin-web/src/services/product.js +++ b/admin-web/src/services/product.js @@ -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 export async function productAttrTree(params) { diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductSpuController.java b/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductSpuController.java index 0c64f6dab..d598b03dd 100644 --- a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductSpuController.java +++ b/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductSpuController.java @@ -52,7 +52,7 @@ public class AdminsProductSpuController { @RequestParam("sellPoint") String sellPoint, @RequestParam("description") String description, @RequestParam("cid") Integer cid, - @RequestParam("picURLs") List picUrls, + @RequestParam("picUrls") List picUrls, @RequestParam("visible") Boolean visible, @RequestParam("skuStr") String skuStr) { // TODO 芋艿,因为考虑不使用 json 接受参数,所以这里手动转。 // 创建 ProductSpuAddDTO 对象