feature-selector.vue 13.1 KB
<template>
    <div class="feature-selector" :class="{ 'slide-in': isVisible }">
        <div class="header">
            <div id="image-box" class="image-box">
                <img :src="selection.thumbnail | resize 45 60"/>
            </div>
            <div class="text-box">
                <h3 class="line-clamp-2">{{entity.productName}}</h3>
                <h4>{{entity.productPriceBo && entity.productPriceBo.formatSalesPrice}}</h4>
            </div>
        </div>

        <hr>

        <div>
            <section>
                <h4>颜色</h4>
                <feature-options name="color" :options="colors" :selection="selection.color"></feature-options>
            </section>
            <section>
                <h4>尺码</h4>
                <feature-options name="size" :options="sizes" :selection="selection.size"></feature-options>
            </section>
            <button @click="addToCart()"
                    class="button button-solid add-to-cart">{{buttonText || '加入购物袋'}}
            </button>
        </div>
    </div>
</template>
<style>
    .feature-selector {
        background: #fff;
        width: 100%;
        min-height: 608px;
        bottom: 0;
        position: fixed;
        padding: 20px 30px;
        z-index: 1001;
        transform: translate3d(0, 100%, 0);
        transition: all 0.1s ease-in-out;

        .header {
            height: 120px;

            h3 {
                margin: 0;
                font-weight: 300;
            }

            h4 {
                color: #b0b0b0;
                font-weight: 200;
                font-size: 30px;
                margin-top: 32px;
                margin-bottom: 0;
            }

            .image-box {
                width: 90px;
                height: 120px;
                display: inline-block;
                opacity: 1;
            }

            .text-box {
                display: inline-block;
                margin-left: 24px;
                max-width: 512px;
            }

        }

        hr {
            border: none;
            border-top: 1px solid #f0f0f0;
            margin-top: 30px;
            margin-bottom: 20px;
        }

        ul {
            list-style: none;
            border: none;
            margin-left: 18px;
            margin-top: 30px;
            margin-bottom: 0;
            padding: 0;
            max-width: 600px;
        }

        li {
            display: inline-block;
        }

        section {

            h4 {
                margin: 0;
                font-size: 25px;
                line-height: 80px;
                display: inline-block;
            }

        }

        .add-to-cart {
            width: 100%;
            margin-top: 50px;
            font-size: 27px;
        }

        &.slide-in {
            transform: translate3d(0, 0, 0);
        }

    }

    .rotate-animation-target {
        animation: rotate-animation 2000ms linear both;
    }

    /* Generated with Bounce.js. Edit at http://bouncejs.com#%7Bs%3A%5B%7BT%3A%22t%22%2Ce%3A%22b%22%2Cd%3A2000%2CD%3A0%2Cf%3A%7Bx%3A0%2Cy%3A0%7D%2Ct%3A%7Bx%3A10%2Cy%3A290%7D%2Cs%3A3%2Cb%3A4%7D%2C%7BT%3A%22r%22%2Ce%3A%22b%22%2Cd%3A2000%2CD%3A0%2Cf%3A0%2Ct%3A360%2Cs%3A3%2Cb%3A4%7D%2C%7BT%3A%22c%22%2Ce%3A%22b%22%2Cd%3A2000%2CD%3A0%2Cf%3A%7Bx%3A1%2Cy%3A1%7D%2Ct%3A%7Bx%3A0.01%2Cy%3A0.01%7D%2Cs%3A3%2Cb%3A4%7D%5D%7D */

    @keyframes rotate-animation {
        0% {
            -webkit-transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
            transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
        }
        4.3% {
            -webkit-transform: matrix3d(-0.489, -0.044, 0, 0, 0.044, -0.489, 0, 0, 0, 0, 1, 0, 5.143, 149.138, 0, 1);
            transform: matrix3d(-0.489, -0.044, 0, 0, 0.044, -0.489, 0, 0, 0, 0, 1, 0, 5.143, 149.138, 0, 1);
        }
        8.61% {
            -webkit-transform: matrix3d(0.094, -0.09, 0, 0, 0.09, 0.094, 0, 0, 0, 0, 1, 0, 8.786, 254.79, 0, 1);
            transform: matrix3d(0.094, -0.09, 0, 0, 0.09, 0.094, 0, 0, 0, 0, 1, 0, 8.786, 254.79, 0, 1);
        }
        12.91% {
            -webkit-transform: matrix3d(-0.04, -0.013, 0, 0, 0.013, -0.04, 0, 0, 0, 0, 1, 0, 10.523, 305.16, 0, 1);
            transform: matrix3d(-0.04, -0.013, 0, 0, 0.013, -0.04, 0, 0, 0, 0, 1, 0, 10.523, 305.16, 0, 1);
        }
        17.22% {
            -webkit-transform: matrix3d(-0.069, -0.046, 0, 0, 0.046, -0.069, 0, 0, 0, 0, 1, 0, 10.933, 317.067, 0, 1);
            transform: matrix3d(-0.069, -0.046, 0, 0, 0.046, -0.069, 0, 0, 0, 0, 1, 0, 10.933, 317.067, 0, 1);
        }
        28.33% {
            -webkit-transform: matrix3d(-0.01, -0.001, 0, 0, 0.001, -0.01, 0, 0, 0, 0, 1, 0, 10.206, 295.982, 0, 1);
            transform: matrix3d(-0.01, -0.001, 0, 0, 0.001, -0.01, 0, 0, 0, 0, 1, 0, 10.206, 295.982, 0, 1);
        }
        39.44% {
            -webkit-transform: matrix3d(0.016, -0.001, 0, 0, 0.001, 0.016, 0, 0, 0, 0, 1, 0, 9.938, 288.193, 0, 1);
            transform: matrix3d(0.016, -0.001, 0, 0, 0.001, 0.016, 0, 0, 0, 0, 1, 0, 9.938, 288.193, 0, 1);
        }
        61.66% {
            -webkit-transform: matrix3d(0.01, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 1, 0, 10.004, 290.121, 0, 1);
            transform: matrix3d(0.01, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 1, 0, 10.004, 290.121, 0, 1);
        }
        83.98% {
            -webkit-transform: matrix3d(0.01, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 1, 0, 10, 289.992, 0, 1);
            transform: matrix3d(0.01, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 1, 0, 10, 289.992, 0, 1);
        }
        100% {
            -webkit-transform: matrix3d(0.01, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 1, 0, 10, 290, 0, 1);
            transform: matrix3d(0.01, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 1, 0, 10, 290, 0, 1);
        }
    }
</style>
<script>
    /**
     * 颜色尺码选择组件
     *
     * @author: Aiden Xu<aiden.xu@yoho.cn>
     * @date: 2016/07/20
     */

    const tip = require('common/tip');
    const Overlay = require('common/overlay');

    /**
     * 颜色尺码选择组件
     **
     * @event feature.close 关闭事件
     * @event feature:color.select 选择颜色
     * @event feature:size.select 选择尺码
     */
    module.exports = {
        init() {
        },
        props: {
            /** 是否可见 */
            isVisible: Boolean,

            /** 实体对象 */
            entity: Object,

            /**
             * 加入购物车回调函数
             *
             * @param result 后端返回的数据
             */
            onAddToCart: Function,

            buttonText: String
        },
        watch: {
            isVisible() {
                if (this.isVisible) {
                    this.overlay.show();
                } else {
                    this.overlay.hide();
                    this.$parent.$emit('feature.close');
                }
            },
            entity() {
                const thumbnails = {};
                const selection = {};
                const colorSizes = {};
                const stocks = {};
                const self = this;

                // 更新颜色
                this.colors = this.entity.goodsList.filter((goods)=> {
                    // 确保商品启用
                    return goods.status !== 0;
                }).map((goods)=> {
                    // 缩略图
                    thumbnails[goods.colorId] = goods.colorImage;

                    // 更新颜色对应尺码  生成colorId 与 size的 映射
                    colorSizes[goods.colorId] = goods.goodsSizeBoList.map((size)=> {
                        if (!stocks[goods.colorId]) {
                            stocks[goods.colorId] = 0;
                        }

                        // 默认选中有库存的第一个颜色尺码
                        if (size.goodsSizeStorageNum > 0) {
                            if (!selection.color) {
                                self.selection.color = selection.color = {
                                    text: goods.colorName,
                                    value: goods.colorId,
                                    disabled: false
                                };
                            }

                            if (!selection.size && size.goodsSizeStorageNum > 0) {
                                self.selection.size = selection.size = {
                                    text: size.sizeName,
                                    value: size.goodsSizeSkuId,
                                    goodsId: size.goodsId,
                                    disabled: false
                                };

                            }

                            // 计算所有尺码的库存
                            stocks[goods.colorId] += size.goodsSizeStorageNum;
                        }

                        return {
                            text: size.sizeName,
                            value: size.goodsSizeSkuId,
                            disabled: size.goodsSizeStorageNum === 0,
                            goodsId: size.goodsId
                        };
                    });

                    return {
                        text: goods.colorName,
                        value: goods.colorId,
                        disabled: stocks[goods.colorId] === 0 // 是否售完
                    };
                });

                this.sizes = colorSizes[selection.color.value];
                this.colorSizes = colorSizes;
                this.thumbnails = thumbnails;

                // 选择默认值
                this.$emit('feature:color.select', selection.color);
                this.$emit('feature:size.select', selection.size);
            }
        },
        data() {
            return {
                colors: [],
                sizes: [],
                colorSizes: {},
                thumbnails: {},
                selection: {
                    color: null,
                    size: null,
                    thumbnail: ''
                }
            };
        },
        components: {
            featureOptions: require('./feature-options.vue')
        },
        created() {
            const self = this;

            this.overlay = new Overlay({
                disableScrolling: false,
                onClose: function() {
                    self.isVisible = false;
                }
            });

            // 选择颜色
            this.$on('feature:color.select', (opt)=> {
                const selection = {
                    color: opt,
                    size: ((color, size)=> {
                        // 切换颜色后选择匹配的尺码
                        const sizes = this.colorSizes[color];

                        if (sizes && sizes.length > 0) {
                            const oldSizes = sizes.filter((item) => {
                                return item.value === size;
                            });

                            if (oldSizes && oldSizes.length > 0) {
                                const newSizes = this.colorSizes[opt.value];

                                const matchedSize = newSizes.filter((item)=> {
                                    return !item.disabled && item.text === oldSizes[0].text;
                                });

                                if (matchedSize && matchedSize.length > 0) {
                                    return matchedSize[0];
                                }
                            }
                        }

                        return null;
                    })(self.selection.color.value, self.selection.size.value),

                    thumbnail: this.thumbnails[opt.value]
                };

                this.sizes = this.colorSizes[opt.value];
                Object.assign(this.selection, selection);
            });

            // 选择尺码
            this.$on('feature:size.select', (opt)=> {
                const selection = {
                    size: opt
                };

                Object.assign(this.selection, selection);
            });
        },
        methods: {
            /**
             * 将当前选择添加到购物车
             */
            addToCart() {
                // console.log(`${this.selection.color}:${this.selection.size}`);

                if (!this.selection.color) {
                    tip('请选择颜色');
                    return;
                }

                if (!this.selection.size) {
                    tip('请选择尺码');
                    return;
                }

                const param = Object.assign({goodsId: this.entity.id}, this.selection);

                this.onAddToCart(param, this);
            },

            /**
             * 播放加入购物车动画
             */
            playAnimation() {
                const imageBox = $('#image-box');
                const pos = imageBox.offset();

                const clone = imageBox.clone();

                clone[0].addEventListener('webkitAnimationEnd', ()=> {
                    clone.remove();
                });

                clone.addClass('rotate-animation-target').appendTo('body').css({
                    top: pos.top,
                    left: pos.left,
                    position: 'absolute',
                    'z-index': 1002
                });
            }
        }
    };

</script>