chose-panel-new.js 12.9 KB
/**
 * 购物车选择尺寸、颜色和数量面板
 * @author: feng.chen<feng.chen@yoho.cn>
 * @date: 2017/3/2
 * 示例:
 * let chosePanel = require('chose-panel-new');
 * chosePanel.show({
 *      data: {},
 *      buttonText: '加入购物车'
 * }).then((sku) => {
 *      //点击确认按钮回调,返回sku对象,各自业务在此处实现,该组件不做选择后的处理
 *      //...
 * }, () => {
 *      //关闭选择框回调
 * });
 * choselPanel.close(); //关闭选择框
 */
let $ = require('yoho-jquery'),
    tip = require('../plugin/tip'),
    innerScroll = require('../plugin/inner-scroll');

let panelT = require('common/chose-panel-new.hbs');

let $yohoPage = $('.yoho-page'),
    $chosePanel = $('.chose-panel'),
    $choseArea,
    $goodNum,
    $btnMinus,
    $thumbImg,
    $choseBtnSure,
    $noChoose,
    $chooseInfo;

class ChosePanel {
    constructor() {
        this.C_ID = window._ChannelVary[window.cookie('_Channel')];
        this._regEvents();
    }
    show(opt) {
        if (opt && opt.data) {
            this.data = opt.data;
            this.maxBuyNum = opt.data.cartInfo.limit || 0; // 0表示不限制购买
            this.skus = this.data.cartInfo.skus || [];
            this.props = this.data.cartInfo.props || [];
            this.modes = { // 商品特性
                discount: opt.data.discountBuy ? true : false, // 量贩
                tickets: opt.data.tickets ? true : false, // 门票
                limitCode: opt.data.cartInfo.limitProductCode ? true : false, // 限购
                limit: opt.data.cartInfo.limit ? true : false, // 限制购买数量
                soonSoldOut: $('.soon-sold-out-tag').length ? true : false, // 即将售罄
                seckill: $('.seckill-time').length ? true : false, // 秒杀
                disableNum: opt.disableNum // 禁用编辑
            };
            this.buyNum = this.minBuyNum = this._defaultBuyNum();
            this.setting = {
                disableNum: opt.disableNum,
                noChoseText: `请选择${this.props.map(prop => prop.name).join('、')}`,
                buttonText: opt.buttonText || this._getButtonText(),
                buyNum: this.buyNum
            };
        }

        if (!this.data) {
            throw new Error('未赋值');
        }
        if (!opt) {
            $('.chose-panel').show();
            return;
        }
        this._render();
        return new Promise((resolve, reject) => {
            this._resolve = resolve;
            this._reject = reject;
        });
    }
    close() {
        $('.chose-panel').hide();
        this._reject();
    }
    _render() {
        let html;

        html = panelT(Object.assign(this.data, this.setting));
        if ($chosePanel.length) {
            $chosePanel.replaceWith(html);
        } else {
            $yohoPage.append(html);
        }

        this._setVariable();
        this._initStatus();
        innerScroll.disableScroll($choseArea);
    }
    _getButtonText() {
        if (this.modes.tickets || this.modes.limitCode || this.modes.seckill) {
            return '立即购买';
        }
        return '加入购物车';
    }
    _defaultBuyNum() {
        return this.modes.discount && this.data.discountBuy.num || 1;
    }
    _initStatus() {
        let noStorageSkus = this.skus.filter(sku => sku.storage < this.minBuyNum);

        $('.block').removeClass('zero-stock');
        noStorageSkus.forEach(sku => {
            for (let propType in sku.prop) {
                if (propType) {
                    let prop = sku.prop[propType];
                    let zeroStock = !this.skus.some(_ => _.prop[propType].valId === prop.valId &&
                    _.storage >= this.minBuyNum);
                    let $block = $(`.block[data-prop-id="${propType}"][data-value-id="${prop.valId}"]`);

                    if (zeroStock) {
                        $block.addClass('zero-stock');
                    } else {
                        $block.removeClass('zero-stock');
                    }
                }
            }
        });
    }
    _setVariable() {
        $chosePanel = $('.chose-panel');
        $choseArea = $('.chose-panel .main .chose-items');
        $goodNum = $('#good-num');
        $btnMinus = $('.btn-minus');
        $thumbImg = $('.thumb-img');
        $choseBtnSure = $('#chose-btn-sure');
        $noChoose = $('.not-choose');
        $chooseInfo = $('.choosed-info');
    }
    _regEvents() {
        $yohoPage.on('touchstart', '.chose-panel', (e) => {
            return this._closeClick(e);
        });
        $yohoPage.on('touchstart', '.block', (e) => {
            this._blockClick(e);
        });
        $yohoPage.on('touchstart', '.btn-plus,.btn-minus', (e) => {
            this._numClick(e);
        });
        $yohoPage.on('touchstart', '.thumb', (e) => {
            this._thumbClick(e);
        });
        $yohoPage.on('touchstart', '#chose-btn-sure', (e) => {
            return this._choseBtnSureClick(e);
        });
    }
    _closeClick(e) {
        let $cur = $(e.target);

        if ($cur.closest('.main').length > 0 && !$cur.hasClass('close')) {
            return;
        }
        this.close();
        return false;
    }
    _blockClick(e) {
        let $block = $(e.currentTarget);

        if ($block.hasClass('chosed')) {
            $block.removeClass('chosed');
        } else {
            $block.addClass('chosed').siblings().removeClass('chosed');
        }
        this._selectBlock($block);
    }
    _numClick(e) {
        let buyNum;

        if (this.modes.disableNum) {
            return;
        }
        buyNum = $(e.currentTarget).hasClass('btn-minus') ? this.buyNum - 1 : this.buyNum + 1;
        if (this._checkSku(buyNum)) {
            this.buyNum = buyNum;
            $goodNum.val(this.buyNum);
            if (this.modes.discount) {
                if (buyNum <= this.minBuyNum) {
                    $('.btn-minus').addClass('discount-gray');
                } else {
                    $('.btn-minus').removeClass('discount-gray');
                }
            }
        }
    }
    _thumbClick() {
        if ($thumbImg.hasClass('hover')) {
            $thumbImg.removeClass('hover');
        } else {
            $thumbImg.addClass('hover');
        }
    }
    _choseBtnSureClick() {
        let selectSku = this._checkSku(this.buyNum);

        if (selectSku) {
            this._resolve({
                sku: selectSku,
                buyNum: this.buyNum,
                modes: this.modes
            });
            this.close();
            return false;
        }
    }
    _checkSku(buyNum) {
        if ($('.block.chosed.zero-stock').length) {
            return false;
        }
        let validSelects = this.props
            .filter(prop => !$(`.block[data-prop-id="${prop.type}"]`).hasClass('chosed'))
            .map(prop => {
                return prop.name;
            });

        if (validSelects.length) {
            tip.show(`请选择${validSelects.join('和')}~`);
            return false;
        }
        let selectSku = this._getSelectSku();

        if (!selectSku) {
            tip.show('无法选择该属性的商品');
            return false;
        }
        if (buyNum < this.minBuyNum) {
            if (this.modes.discount) {
                tip.show(`量贩商品,${this.minBuyNum}件起购`);
                return false;
            } else {
                tip.show('您选择的数量不能为零~');
                return false;
            }
        }
        if (this.modes.tickets && buyNum > this.maxBuyNum) {
            tip.show(`每人只可购买${this.maxBuyNum}张当日门票`);
            return false;
        }
        if (this.modes.limit && buyNum > this.maxBuyNum) {
            tip.show('您选择数量大于限购数量~');
            return false;
        }
        if (buyNum > selectSku.storage) {
            tip.show('您选择的数量超过了最大库存量~');
            return false;
        }
        return selectSku;
    }
    _selectBlock($selectBlock) {
        let propId = $selectBlock.data('prop-id'),
            valueId = $selectBlock.data('value-id');
        let chosed = $($selectBlock).hasClass('chosed');

        if ($('.block.chosed').length === 0) {
            this._initStatus();
        } else {
            let filterSkus = this.skus.filter(sku => (!chosed || sku.prop[propId].valId === valueId) &&
                sku.storage >= this.minBuyNum);
            let filterProps = this.props.filter(prop => prop.type !== propId);

            filterProps.forEach(prop => {
                $(`.block[data-prop-id="${prop.type}"]`).each((i, ele) => {
                    let $block = $(ele);
                    let valId = $block.data('value-id');
                    let existsSku = filterSkus.some(sku => sku.prop[prop.type].valId === valId);

                    if (!existsSku) {
                        $block.addClass('zero-stock');
                    } else {
                        $block.removeClass('zero-stock');
                    }
                });
            });
        }
        if (window._yas && window._yas.sendCustomInfo && chosed) {
            if (propId === 'color') {
                window._yas.sendCustomInfo({
                    op: 'YB_GDS_COLOR_C',
                    param: JSON.stringify({
                        C_ID: this.C_ID,
                        PRD_ID: this.data.cartInfo.productId,
                        COLOR: $selectBlock.text()
                    })
                }, true);
            } else if (propId === 'size') {
                window._yas.sendCustomInfo({
                    op: 'YB_GDS_SIZE_C',
                    param: JSON.stringify({
                        C_ID: this.C_ID,
                        PRD_ID: this.data.cartInfo.productId,
                        SIZE: $selectBlock.text()
                    })
                }, true);
            }
        }

        this._initBlockStatus();
    }
    _initBlockStatus() {
        let selectSku = this._getSelectSku();

        // 设置底部按钮文字
        if ($('.block.chosed.zero-stock').length) {
            $choseBtnSure.css('background-color', '#c0c0c0').text('已售罄');
        } else {
            $choseBtnSure.css('background-color', '#eb0313').text(this.setting.buttonText);
        }

        // 设置默认购买数量
        this.buyNum = this._defaultBuyNum();
        $goodNum.val(this.buyNum);
        if (this.modes.discount) {
            $btnMinus.addClass('discount-gray');
        }

        // 根据颜色属性设置缩略图
        let $colorBlock = $('.block.chosed[data-prop-id="color"]');

        if ($colorBlock.length) {
            let colorSkus = this.skus.filter(sku => sku.prop.color &&
                sku.prop.color.valId === $colorBlock.data('value-id'));

            if (colorSkus.length) {
                let thumb = colorSkus[0].thumb;

                if (!thumb) {
                    thumb = this.data.cartInfo.defaultThumb;
                }
                $chosePanel.find('.thumb').attr('src', thumb);
            }
        }

        // 设置选择属性提示
        let valueList = Array.from($('.block.chosed').map((index, ele) => {
            return $(ele).text();
        }));

        if (valueList.length) {
            $noChoose.addClass('hide');
            $chooseInfo.removeClass('hide');
            $chooseInfo.text(`已选:${valueList.join('、')}`);
        } else {
            $noChoose.removeClass('hide');
            $chooseInfo.addClass('hide');
        }

        if (selectSku) {
            // 设置left-num文字
            let cartInfo = this.data.cartInfo,
                numText;

            if (this.modes.limit) {
                numText = `限购${cartInfo.limit}件`;
            } else if (this.modes.soonSoldOut) {
                numText = '即将售罄';
            } else if (selectSku.storage < 4) {
                numText = `剩余${selectSku.storage}件`;
            } else {
                numText = '';
            }
            $chosePanel.find('.left-num').text(numText);
            $chosePanel.find('.size-info').text(selectSku.sizeInfo).removeClass('hide');
        } else {
            $chosePanel.find('.left-num').text('');
            $chosePanel.find('.size-info').text('').addClass('hide');
        }
    }
    _getSelectSku() {
        let selectValues,
            selectSku;

        if ($('.block.chosed').length !== this.props.length) {
            return;
        }
        selectValues = Array.from($('.block.chosed').map((index, ele) => {
            let $block = $(ele);

            return {
                propId: $block.data('prop-id'),
                valueId: $block.data('value-id')
            };
        }));
        selectSku = this.skus.find(sku => {
            return selectValues
                .map(value => {
                    return sku.prop[value.propId].valId === value.valueId;
                })
                .filter(match => match).length === this.props.length;
        });
        return selectSku;
    }
}


module.exports = new ChosePanel();