前端如何展示商品属性:SKU多维属性状态判断算法的应用-Vue 实现

由于公司开发了一个电商项目,涉及到前台商品属性的展示,所以百度上找了一下!找到了 周琪力写的一个算法例子,因为作者只有jQuery 实现demo, 自己仿照 demo 实现了一个 vue 的!

周琪力原文: http://mp.weixin.qq.com/s?__biz=MzIwNjQwMzUwMQ==&mid=2247484853&idx=1&sn=bed59c1d83c3aeb4bf7881be8dbdd917&chksm=97236777a054ee61fc3cef07eb4b164fa28e26917ce0a409d876964ad3c2ee3f90a000e29beb#rd

原文的demo实现:http://codepen.io/keelii/pen/RoOzgb

效果图: 
这里写图片描述

附上代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue 实现商品属性的计算显示</title>
    <style>
        body {
            font-size: 12px;
        }
        dt {
            width: 100px;
            text-align: right;
        }
        dl {
            clear: both;
            overflow:hidden;
        }
        dl.hl {
            background:#ddd;
        }
        dt, dd {
            float:left;
            height: 40px;
            line-height: 40px;
            margin-left: 10px;
        }
        button {
            font-size: 14px;
            font-weight: bold;
            width: 100px;
            height: 30px;
            margin: 0 10px;
        }

        .disabled {
            color:#999;
            border: 1px dashed #666;
        }
        .active {
            color: red;
        }

        .top-but {
            margin: 10px;
        }

        #skuId {
            height: 24px;
            font-size: 14px;
            line-height: 24px;
        }
    </style>
</head>
<body>
    <textarea id="values" style="width:600px;height:100px">
        [
           { "颜色": "红", "尺码": "大", "型号": "A", "skuId": "3158055" },
           { "颜色": "白", "尺码": "大", "型号": "A", "skuId": "3158054" },
           { "颜色": "白", "尺码": "中", "型号": "B", "skuId": "3133859" },
           { "颜色": "蓝", "尺码": "小", "型号": "C", "skuId": "3516833" }
        ]
    </textarea>
    <div id="app">
        <label> 
            默认选中 :  
            <input type="text" name="skuId" id="skuId" v-bind:value="skuId">
        </label>
        <button @click="getTextareaData" class="top-but"> 重新加载数据 </button> 当前属性ID:{{ skuId }}
        <dl v-for="item, key in list.result" class="content" v-bind:class="{hl: highKeys[key]}">
            <dt> {{key}} : </dt> 
            <dd>
                <button
                    class="item"
                    v-for="value in item"
                    @click="handleActive(key, value)"
                    v-bind:class="{active: value.active, disabled: !value.active && value.disabled}" 
                    > {{ value.name }} </button> 
            </dd>
        </dl>
        已经选择:{{ message }}
    </div>
</body>
<script src="https://unpkg.com/vue "></script>
<script>
    let vue = new Vue({
        el: "#app",
        data(){
            return {
                data: [],
                skuId: "",
                skuName: "skuId",
                // 属性名称信息
                keys: [],
                // 数据集合{list.result list.items}
                list: {},
                // 分隔符
                spliter: '\u2299',
                result: {},
                message: "",
                highKeys: {},
            };
        },
        methods: {
            powerset(arr) {
                let ps = [[]];
                for (let i = 0; i < arr.length; i++) {
                    for (let j = 0, len = ps.length; j < len; j++) {
                        ps.push(ps[j].concat(arr[i]));
                    }
                }

                return ps;
            },

            /**
             * 初始化数据
             * @return 
             */
            initData() {
                this.result = {};
                this.keys = this.getAllKeys();
                for (let i = 0; i < this.keys.length; i ++) {
                    this.highKeys[this.keys[i]] = false;
                }

                this.list = this.combineAttr(this.data, this.keys);
                this.initSeleted(this.skuId);
                this.buildResult(this.list.items)
                this.updateStatus(this.getSelectedItem());
                this.showResult();
            },

            /**
             * 获取输入表单中的数据进行初始化
             * @return
             */
            getTextareaData() {
                let data = document.getElementById('values').value;
                let skuId = document.getElementById("skuId").value;

                try {
                    this.data = JSON.parse(data);
                    let isHas = false;
                    for (let i = 0; i < this.data.length; i ++) {
                        if (skuId == this.data[i][this.skuName]) {
                            isHas = true;
                            break
                        }
                    }

                    this.skuId = isHas ? skuId : this.data[0][this.skuName];
                    this.initData();
                } catch (e) {
                    this.data = [];
                }
            },

            /**
             * 正常属性点击
             */
            handleNormalClick(key, value) {
                for (let i in this.list.result[key]) {
                    if (i != value.name) {
                        this.list.result[key][i].active = false;
                    } else {
                        this.list.result[key][i].active = true;
                    }
                }
            },

            /**
             * 无效属性点击
             */
            handleDisableClick(key, value) {
                this.list.result[key][value.name]["disabled"] = false;
                // 清空高亮行的已选属性状态(因为更新的时候默认会跳过已选状态)
                for (let i in this.list.result) {
                    if (i != key) {
                        for (let x in this.list.result[i]) {
                            this.list.result[i][x].active = false;
                        }
                    }
                }

                this.updateStatus(this.getSelectedItem());
            },

            /**
             * 高亮行
             */
            highAttributes: function() {
                for (let key in this.list.result) {
                    this.highKeys[key] = true;
                    for (let attr in this.list.result[key]) {
                        if (this.list.result[key][attr].active === true) {
                            this.highKeys[key] = false;
                            break;
                        }
                    }
                }
            },

            /**
             * 点击事件处理
             * @param  key   点击的行
             * @param  value 点击的按钮的数据
             */
            handleActive: function(key, value) {
                if (value.active == true) {
                    return false;
                }

                this.handleNormalClick(key, value);
                if (value.disabled === true) {
                    this.handleDisableClick(key, value);
                }

                this.updateStatus(this.getSelectedItem());
                this.highAttributes();
                this.showResult();
            },

            /**
             * 计算属性
             * @param  {[type]} data [description]
             * @param  {[type]} keys [description]
             * @return {[type]}      [description]
             */
            combineAttr(data, keys) {
                let allKeys = []
                let result = {}

                for (let i = 0; i < data.length; i++) {
                    let item = data[i]
                    let values = []

                    for (let j = 0; j < keys.length; j++) {
                        let key = keys[j]
                        if (!result[key]) {
                            result[key] = {};
                        }

                        if (!result[key][item[key]]) {
                            result[key][item[key]] = {"name": item[key], "active": false, "disabled": true};
                        }

                        values.push(item[key]);
                    }

                    allKeys.push({
                        path: values.join(this.spliter),
                        sku: item['skuId']
                    });
                }

                return {
                    result: result,
                    items: allKeys
                }
            },

            /**
             * 获取所有属性
             * @return {[type]} [description]
             */
            getAllKeys() {
                let arrKeys = [];
                for (let attribute in this.data[0]) {
                    if (!this.data[0].hasOwnProperty(attribute)) {
                        continue;
                    } 

                    if (attribute !== this.skuName) {
                        arrKeys.push(attribute);
                    }
                }

                return arrKeys;
            },

            getAttruites(arr) {
                let result = []
                for (let i = 0; i < arr.length; i++) { 
                    result.push(arr[i].path) 
                }

                return result
            },

            /**
             * 生成所有子集是否可选、库存状态 map
             */
            buildResult(items) {
                let allKeys = this.getAttruites(items)

                for (let i = 0; i < allKeys.length; i++) {
                    let curr = allKeys[i];
                    let sku = items[i].sku;
                    let values = curr.split(this.spliter);
                    let allSets = this.powerset(values);

                    // 每个组合的子集
                    for (let j = 0; j < allSets.length; j++) {
                        let set = allSets[j]
                        let key = set.join(this.spliter)

                        if (this.result[key]) {
                            this.result[key].skus.push(sku)
                        } else {
                            this.result[key] = {
                                skus: [sku]
                            }
                        }
                    }
                }
            },

            /**
             * 获取选中的信息
             * @return Array 
             */
            getSelectedItem() {
                let result = [];
                for (let attr in this.list.result) {
                    let attributeName = '';
                    for (let attribute in this.list.result[attr]) {
                        if (this.list.result[attr][attribute].active === true) {
                            attributeName = attribute;
                        }
                    }

                    result.push(attributeName);
                }

                return result
            },

            /**
             * 更新所有属性状态
             */
            updateStatus(selected) {
                for (let i = 0; i < this.keys.length; i++) {
                    let key = this.keys[i],
                        data = this.list.result[key],
                        hasActive = !!selected[i],
                        copy = selected.slice();

                    for (let j in data) {
                        let item = data[j]["name"];
                        if (selected[i] == item) {
                            continue
                        }

                        copy[i] = item
                        let curr = this.trimSpliter(copy.join(this.spliter), this.spliter);
                        this.list.result[key][j]["disabled"]  = this.result[curr] ? false : true;
                    }
                }
            },

            trimSpliter(str, spliter) {
                // ⊙abc⊙ => abc
                // ⊙a⊙⊙b⊙c⊙ => a⊙b⊙c
                let reLeft    = new RegExp('^' + spliter + '+', 'g');
                let reRight   = new RegExp(spliter + '+$', 'g');
                let reSpliter = new RegExp(spliter + '+', 'g');
                return str.replace(reLeft, '')
                    .replace(reRight, '')
                    .replace(reSpliter, spliter)
            },

            /**
             * 初始化选中
             * @param  mixed|Int|String skuId 需要选中的skuId
             * @return {[type]}       [description]
             */
            initSeleted(skuId) {
                for (let i in this.data) {
                    if (this.data[i][this.skuName] == skuId) {
                        for (let x in this.data[i]) {
                            if (x !== this.skuName) {
                                this.list.result[x][this.data[i][x]].active = true;
                            }
                        }
                        break;
                    }
                }
            },

            /**
             * 显示选中的信息
             * @return 
             */
            showResult() {
                let result = this.getSelectedItem()
                let s = []
                for (let i = 0; i < result.length; i++) {
                    let item = result[i];
                    if (!!item) {
                        s.push(item)
                    }
                }

                if (s.length == this.keys.length) {
                    let curr = this.result[s.join(this.spliter)]
                    if (curr) {
                        s = s.concat(curr.skus)
                        this.skuId = curr.skus[0];
                    }

                    this.message = s.join('\u3000-\u3000');
                }
            }
        },

        created() {
            this.getTextareaData();
        }
    })
</script>
</html>
posted @ 2018-10-15 14:22  小菜与小鸟  阅读(750)  评论(0编辑  收藏  举报