Эх сурвалжийг харах

商品选择规格详情页

Csy817 6 жил өмнө
parent
commit
d2b734a3fb

+ 36 - 0
src/app.css

@@ -58,4 +58,40 @@ a {
 
 .content-wrap {
     padding-top: 45px;
+}
+
+.selected {
+    width: 50%;
+    min-width: 100px;
+    position: absolute;
+    bottom: 0;
+}
+
+.selected_button {
+    width: 30px;
+    height: 30px;
+    /*background-color: #fff;*/
+    color: white;
+    background-color: #f44;
+    border: none;
+    /*border: 1px solid #ebedf0;*/
+}
+
+.selected_button:active {
+    background-color: #e8e8e8;
+}
+
+.selected_button-disabled {
+    background-color: #f8f8f8;
+    color: black;
+}
+
+.selected_input {
+    width: 33px;
+    height: 30px;
+    border: 1px solid #ebedf0;
+    border-radius: 0;
+    color: black;
+    font-size: 14px;
+    text-align: center;
 }

+ 0 - 32
src/pages/cart/all/index.css

@@ -7,38 +7,6 @@
     color: #ff6d6d;
 }
 
-.selected {
-    width: 50%;
-    min-width: 100px;
-    position: absolute;
-    bottom: 0;
-}
-
-.selected_button {
-    width: 30px;
-    height: 30px;
-    background-color: #fff;
-    border: 1px solid #ebedf0;
-}
-
-.selected_button:active {
-    background-color: #e8e8e8;
-}
-
-.selected_button-disabled {
-    background-color: #f8f8f8;
-}
-
-.selected_input {
-    width: 33px;
-    height: 30px;
-    border: 1px solid #ebedf0;
-    border-radius: 0;
-    color: black;
-    font-size: 14px;
-    text-align: center;
-}
-
 .cart-content-wrap {
     padding: 45px 0;
 }

+ 3 - 2
src/pages/cart/pay/index.css

@@ -34,14 +34,14 @@
     float: right;
 }
 
-.pay-footer {
+.confirm-footer {
     position: fixed;
     bottom: 0;
     width: 100%;
     height: 50px;
 }
 
-.pay-button {
+.confirm-button {
     width: 100%;
     height: 100%;
     color: #fff;
@@ -49,6 +49,7 @@
     border: 1px solid #f44;
     font-size: 16px;
     text-align: center;
+    letter-spacing: 2px;
 }
 
 .pay-disabled {

+ 2 - 2
src/pages/cart/pay/index.js

@@ -55,10 +55,10 @@ class Pay extends Component {
                         </div>
                     </div>
                 </div>
-                <div className="pay-footer">
+                <div className="confirm-footer">
                     <button
                             className={classNames({
-                                'pay-button': true,
+                                'confirm-button': true,
                                 'pay-disabled': !checked
                             })}
                             onClick={()=>{

+ 169 - 0
src/pages/home/detail/index.css

@@ -128,3 +128,172 @@
     background: url("https://ece-img-1254337200.cos.ap-chengdu.myqcloud.com/icon/ship.png") no-repeat;
     background-size: cover;
 }
+
+.am-modal-popup {
+    height: 425px;
+    overflow: auto;
+}
+
+.am-modal-content {
+    text-align: left;
+}
+
+.main-goods-box {
+    padding: 12px;
+    color: #333;
+}
+
+.close-popup {
+    width: 42px;
+    height: 52px;
+    position: absolute;
+    right: 0;
+    top: 0;
+    font-size: 30px;
+    font-weight: lighter;
+    text-align: center;
+    z-index: 5;
+}
+
+.goods-box {
+    width: 100%;
+    height: 100%;
+    background-color: #fff;
+    position: relative;
+    padding-top: 95px;
+}
+
+.goods-info {
+    position: absolute;
+    top: 0;
+    left: 0;
+    display: flex;
+}
+
+.goods-info img {
+    width: 100%;
+    height: 100%;
+}
+
+.goods-info .left {
+    width: 80px;
+    height: 80px;
+    overflow: hidden;
+}
+
+.goods-info .mid {
+    flex: 1;
+    display: flex;
+    flex-wrap: wrap;
+    align-content: flex-end;
+    height:  80px;
+    margin-left: 10px;
+}
+
+.goods-info .right {
+    display: flex;
+    align-items: flex-end;
+    width: 40px;
+    text-align: right;
+    font-size: 10px;
+    line-height: 15px;
+    color: #999;
+    margin-left:  10px;
+    white-space: nowrap;
+}
+
+.goods_price {
+    height: 20px;
+    line-height: 20px;
+    font-size: 17px;
+    font-weight: 500;
+}
+
+.selected-type {
+    width: 100%;
+    max-height: 32px;
+    line-height: 16px;
+    font-size: 11px;
+    margin-top: 2px;
+    overflow: hidden;
+}
+
+.goods_type {
+    padding: 8px 0;
+    position: relative;
+}
+
+li, ol, ul {
+    list-style: none;
+}
+
+.goods_type ul {
+    padding-inline-start: 0;
+}
+
+.goods_type ul li dl {
+    width: 100%;
+    display: block;
+    overflow: auto;
+}
+
+.goods_type ul li dl .spec-red {
+    border: 1px solid red;
+    color: red;
+}
+
+.goods_type ul li dl .spec-gray {
+    border: 1px solid #989898;
+    color: #989898;
+}
+
+.goods_type ul li dl dd {
+    display: block;
+    float: left;
+    margin-top: 8px;
+    margin-right:  8px;
+    padding: 4px 10px;
+    border: 1px solid #333;
+    font-size: 12px;
+    line-height: 12px;
+    transition: all .2s;
+    box-sizing: content-box;
+}
+
+.goods_type:after {
+    position: absolute;
+    display: block;
+    background: #eee;
+    content: "";
+    left: 0;
+    bottom: 0;
+    width: 100%;
+    height: 1px;
+    transform: scaleY(.5);
+}
+
+.type-title {
+    width: 100%;
+    font-size: 12px;
+    line-height: 16px;
+    height: 16px;
+}
+
+.edit-product {
+    display: block;
+    padding-top: 15px;
+    overflow: hidden;
+    position: relative;
+    line-height: 28px;
+}
+
+.edit-product-text {
+    float: left;
+    font-size: 14px;
+    color: #2c2f34;
+}
+
+.edit-product-count {
+    overflow: hidden;
+    float: right;
+}

+ 271 - 13
src/pages/home/detail/index.js

@@ -2,9 +2,10 @@ import React, {Component} from 'react'
 import {withRouter} from 'react-router-dom'
 import {Query} from "react-apollo"
 import gql from "graphql-tag"
-import {NavBar, Icon, ActivityIndicator, Badge} from 'antd-mobile'
+import {NavBar, Icon, ActivityIndicator, Badge, Modal} from 'antd-mobile'
+import classNames from 'classnames'
 
-import {productbyid} from "../../../utils/gql"
+import {productAndSpec_by_id} from "../../../utils/gql"
 import './index.css'
 
 class Detail extends Component {
@@ -38,7 +39,7 @@ class Detail extends Component {
                     </NavBar>
                 </div>
 
-                <Query query={gql(productbyid)} variables={{id}}>
+                <Query query={gql(productAndSpec_by_id)} variables={{id}}>
                     {
                         ({loading, error, data}) => {
                             if (loading) {
@@ -51,8 +52,9 @@ class Detail extends Component {
                             if (error) {
                                 return 'error!'
                             }
+                            // console.log('productAndSpec_by_id data',data)
                             return (
-                                <DetailRender data={data.productbyid} history={this.props.history}/>
+                                <DetailRender data={data} history={this.props.history}/>
                             )
                         }
                     }
@@ -68,24 +70,39 @@ class DetailRender extends Component {
     constructor(props) {
         super(props)
         this.state = {
-            cartCount: localStorage.getItem('cartCount')
+            cartCount: localStorage.getItem('cartCount'),
+            openSelect: false
         }
     }
 
+    showModal = key => (e) => {
+        e.preventDefault(); // 修复 Android 上点击穿透
+        this.setState({
+            [key]: true,
+        });
+    }
+
+    onClose = key => () => {
+        this.setState({
+            [key]: false,
+        });
+    }
+
     render() {
         let {data} = this.props
-        let {cartCount} = this.state
+        let {name, img, price, stock} = data.productbyid
+        let {cartCount, openSelect} = this.state
 
         return (
             <div className='detail-wrapper content-wrap'>
                 <div className='detail-simple-show'>
-                    <div className='detail-img' style={{backgroundImage: "url('"+ data.img + "')"}}/>
+                    <div className='detail-img' style={{backgroundImage: "url('"+ img + "')"}}/>
                     <div className='detail-intro'>
-                        <div className='detail-name detail-padding'>{data.name}</div>
+                        <div className='detail-name detail-padding'>{name}</div>
                         <div className='detail-price detail-padding'>
-                            <span>¥{data.price}</span>&nbsp;&nbsp;
-                            <span>¥{data.price}</span>
-                            <span className='detail-stock'>库存 {data.stock}</span>
+                            <span>¥{price}</span>&nbsp;&nbsp;
+                            <span>¥{price}</span>
+                            <span className='detail-stock'>库存 {stock}</span>
                         </div>
                     </div>
                 </div>
@@ -104,11 +121,252 @@ class DetailRender extends Component {
                                  <span style={{display: 'inline-block' }} />
                             </Badge>
                         </span>
-                        <span className='detail-bottom-button add' onClick={()=>{}}>加入购物袋</span>
-                        <span className='detail-bottom-button buy' onClick={()=>{}}>立即购买</span>
+                        <span className='detail-bottom-button add' onClick={this.showModal('openSelect')}>加入购物袋</span>
+                        <span className='detail-bottom-button buy' onClick={this.showModal('openSelect')}>立即购买</span>
+                        <SelectModal onClose={this.onClose} openSelect={openSelect} goods={data.spec} price={price} img={img}/>
                     </div>
                 </div>
             </div>
         )
     }
+}
+
+class SelectModal extends Component {
+    constructor(props) {
+        super(props)
+        this.state = {
+            count: 1,
+            selectColor: '',
+            specList: [],
+            colorList: [],
+            goods: []
+        }
+    }
+
+    componentWillMount() {
+        let {goods} = this.props
+        this.handleData(goods)
+    }
+
+    handleData = (goods) => {
+        let flag = true, selectFlag = true
+        let specObject = {}, i = 0, specList = []
+        let colorObject = {}, j = 0, colorList = [], selectColor = ''
+        goods.forEach((item) => {
+            let {id,color,size,stock,status} = item
+            if(flag && status > 0) {
+                selectColor = color
+                flag = false
+            }
+            specObject[color] ? specList[specObject[color] - 1].spec.push({size, stock, status}) : specObject[color] = ++i && specList.push({
+                id,
+                color,
+                spec: [{size, stock, status}],
+            })
+            if(!colorObject[color]) {
+                colorObject[color] = ++j
+                colorList.push({
+                    id,
+                    color
+                })
+            }
+        })
+
+        specList.forEach((items)=>{
+            let {spec} = items
+            spec.forEach((item)=>{
+                item.select = false
+                if(selectFlag && item.status > 0) {
+                    item.select = true
+                    selectFlag = false
+                }
+            })
+            selectFlag = true
+        })
+
+        this.setState({
+            selectColor,
+            specList,
+            colorList
+        })
+        // console.log('specList',specList)
+        // console.log('colorList',colorList)
+    }
+
+    changeState = (state,val) => {
+        this.setState({
+            [state]:val
+        })
+    }
+
+    //获取输入框的值
+    getInputValue=(e)=>{
+        this.setState({
+            count:e.target.value
+        })
+    }
+
+    // 增加
+    augment=()=>{
+        this.setState({
+            count:this.state.count*1+1
+        })
+    }
+
+    // 减少
+    reduce=()=> {
+        this.setState({
+            count:this.state.count*1-1
+        })
+    }
+
+    render() {
+        let {price, img} = this.props
+        let {count, selectColor, specList, colorList} = this.state
+        let specFilter = specList.filter(item=>item.color === selectColor)[0].spec.filter(item=> item.select && item.status > 0)[0]
+        let specStock =  specFilter.stock || 0
+        let selectSize =  specFilter.size
+
+        return(
+            <Modal
+                popup
+                visible={this.props.openSelect}
+                onClose={this.props.onClose('openSelect')}
+                animationType="slide-up"
+                afterClose={() => { console.log('close model')}}
+            >
+                <div className="popup-box" >
+                    <div className="main-goods-box">
+                        <div className="close-popup" onClick={this.props.onClose('openSelect')}>
+                            ×
+                        </div>
+                        <div className="goods-box">
+                            <div className="goods-info">
+                                <div className="left">
+                                    <img src={img || "https://gw.alipayobjects.com/zos/rmsportal/nywPmnTAvTmLusPxHPSu.png"} alt="商品图片"/>
+                                </div>
+                                <div className="mid">
+                                    <div className="goods_price"> ¥ {price}</div>
+                                    <div className="selected-type">已选择: {selectColor} / {selectSize}</div>
+                                </div>
+                                <div className="right">库存
+                                    {specStock}
+                                </div>
+                            </div>
+                            <div className="scroll-body">
+                                <div className="goods_type">
+                                    <ul>
+                                        <li>
+                                            <div className="type-title">颜色</div>
+                                            <dl>
+                                                {
+                                                    colorList.map((spec)=>(
+                                                        <dd
+                                                            className={classNames({
+                                                                'spec-red': spec.color === selectColor
+                                                            })}
+                                                            key={'color'+spec.id}
+                                                            onClick={()=>{
+                                                                this.changeState('selectColor',spec.color)
+                                                            }}
+                                                        >
+                                                            {spec.color}
+                                                        </dd>
+                                                    ))
+                                                }
+                                            </dl>
+                                        </li>
+                                        <Specification specList={specList.filter(item=>item.color === selectColor)[0]} changeState={this.changeState}/>
+                                    </ul>
+                                </div>
+                                <div className="edit-product">
+                                    <div className="edit-product-text">购买数量</div>
+                                    <div className="edit-product-count">
+                                        <button
+                                            className={classNames({
+                                                'selected_button': true,
+                                                'selected_button-disabled': count <= 1
+                                            })}
+                                            disabled={count <= 1}
+                                            onClick={this.reduce}
+                                        >-</button>
+                                        <input className="selected_input" type="text" value={count} onChange={(e)=>{this.getInputValue(e)}}/>
+                                        <button className="selected_button" onClick={this.augment}>+</button>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div className='confirm-footer'>
+                        <button
+                            className='confirm-button'
+                            onClick={()=>{
+                            }}>
+                            <span>确认</span>
+                        </button>
+                    </div>
+                </div>
+            </Modal>
+        )
+    }
+}
+
+
+class Specification extends Component {
+    constructor(props) {
+        super(props)
+        this.state = {
+            spec:this.props.specList.spec
+        }
+    }
+
+    componentWillReceiveProps(nextProps, nextContext) {
+        this.setState({
+            spec:nextProps.specList.spec,
+        })
+    }
+
+    // 改变选择
+    changeSelectedStatus=(i)=>{
+        this.setState((prevState, props) => ({
+            spec: prevState.spec.map((item,index)=>{
+                if(index===i){
+                    item.select=true
+                    this.props.changeState('selectSize',item.size)
+                }else {
+                    item.select=false
+                }
+                return item
+            })
+        }))
+    }
+
+    render() {
+        let {spec} = this.state
+        // console.log('spec',spec)
+
+        return (
+            <li>
+                <div className="type-title">规格</div>
+                <dl>
+                    {
+                        spec.map((item,index)=>(
+                            <dd
+                                className={classNames({
+                                    'spec-gray': item.status < 1,
+                                    'spec-red': item.status > 0 && item.select
+                                })}
+                                key={'spec'+index}
+                                onClick={()=>{
+                                    if(item.status > 0)  this.changeSelectedStatus(index)
+                                }}
+                            >
+                                {item.size}
+                            </dd>
+                        ))
+                    }
+                </dl>
+            </li>
+        )
+    }
 }

+ 10 - 2
src/utils/gql.js

@@ -28,7 +28,7 @@ const productbyprops = `
     }
 `
 
-const productbyid = `
+const productAndSpec_by_id = `
     query productbyid($id: ID) {
         productbyid: product_by_id(id: $id) {
             category_id{
@@ -45,6 +45,14 @@ const productbyid = `
             img
             stock
         }
+        
+        spec: specificationStock_by_props(product_id: $id ) {
+            id
+            color
+            size
+            stock
+            status
+        }
     }
 `
 
@@ -356,7 +364,7 @@ const create_shop = `
 export {
     category_by_props,
     productbyprops,
-    productbyid,
+    productAndSpec_by_id,
     cart_by_userid,
     delete_userCart_by_id,
     userAddressbyprops,