ソースを参照

比较&rich-text

panhui 5 年 前
コミット
24b0b968d0

+ 9 - 3
project.config.json

@@ -69,10 +69,16 @@
                 },
                 {
                     "id": -1,
-                    "name": "全球产品",
-                    "pathName": "pagesHome/Contrast",
+                    "name": "对比",
+                    "pathName": "pagesProduct/Contrast",
                     "scene": null
-                }
+                },{
+                      "id": -1,
+                      "name": "对比",
+											"pathName": "pagesProduct/Detail",
+											"query":"id=14574",
+                      "scene": null
+                  }
             ]
         }
     }

+ 11 - 1
src/components/product/RowMini.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="product" @click="choose">
         <van-icon
-            v-if="hasCheck"
+            v-if="hasCheck && checkPosition === 'left'"
             :name="checked ? 'checked' : 'circle'"
             :color="checked ? $colors.warn : '#BCC1CC'"
             :size="16"
@@ -16,6 +16,12 @@
             </div>
         </div>
         <van-icon v-if="hasDel" @click.stop="del" :size="20" color="#BCC1CC" name="delete" />
+        <van-icon
+            v-if="hasCheck && checkPosition === 'right'"
+            :name="checked ? 'checked' : 'circle'"
+            :color="checked ? $colors.warn : '#BCC1CC'"
+            :size="16"
+        />
     </div>
 </template>
 <script>
@@ -42,6 +48,10 @@ export default {
         imgKey: {
             type: String,
             default: 'img'
+        },
+        checkPosition: {
+            type: String,
+            default: 'left'
         }
     },
     computed: {

+ 22 - 9
src/pagesProduct/Contrast.vue

@@ -54,7 +54,10 @@
         <van-dialog id="van-dialog">
             <div class="dialog-center">
                 <van-icon name="info" :size="55" :color="$colors.warn" />
-                <div class="dialog-title">
+                <div class="dialog-title" v-if="isMoreWarn">
+                    最多可选择两个产品进行对比<br />在对比时可以随时切换其他产品
+                </div>
+                <div class="dialog-title" v-else>
                     产品对比仅限同类别产品进行对比<br />是否立即清空当前对比列表<br />添加当前商品对比
                 </div>
             </div>
@@ -67,7 +70,7 @@
                 @click="navigateTo('/pagesProduct/FilterVendor?categoryIds=' + (category.id || ''))"
                 >添加产品</van-button
             >
-            <van-button block :color="$colors.warn" :radius="4" :disabled="chooseIds.length < 2"
+            <van-button block :color="$colors.warn" :radius="4" :disabled="chooseIds.length < 2" @click="compare"
                 >开始对比({{ chooseIds.length }})</van-button
             >
         </fixed-button>
@@ -84,7 +87,8 @@ export default {
             chooseIds: [],
             list: [],
             addList: [],
-            reader: false
+            reader: false,
+            isMoreWarn: false
         };
     },
     onLoad(options) {
@@ -137,8 +141,8 @@ export default {
             });
         },
         loginMethods() {
-            this.chooseIds = [this.productId];
-            console.log(this.chooseIds);
+            // this.chooseIds = [this.productId];
+            // console.log(this.chooseIds);
             this.$store
                 .dispatch('getContrastInfo')
                 .then(() => {
@@ -154,9 +158,6 @@ export default {
                             })
                             .then(() => {
                                 return this.addContrast(this.productId);
-                            })
-                            .catch(e => {
-                                this.chooseIds = [];
                             });
                     }
                 });
@@ -197,6 +198,9 @@ export default {
             const chooseIds = [...this.chooseIds];
             if (chooseIds.includes(id)) {
                 chooseIds.splice(chooseIds.indexOf(id), 1);
+            } else if (chooseIds.length >= 2) {
+                this.isMoreWarn = true;
+                this.dialog('', false, true);
             } else {
                 chooseIds.push(id);
             }
@@ -213,9 +217,18 @@ export default {
         },
         chooseAdd(id) {
             this.addContrast(id).then(() => {
-                this.chooseIds.push(id);
+                if (this.chooseIds.length < 2) {
+                    this.chooseIds.push(id);
+                }
+
                 this.$store.dispatch('getContrastInfo');
             });
+        },
+        compare() {
+            if (this.chooseIds.length < 2) {
+                return;
+            }
+            this.navigateTo('/pagesProduct/ContrastDetail?chooseIds=' + [...this.chooseIds].join(','));
         }
     },
     components: {

+ 457 - 4
src/pagesProduct/ContrastDetail.vue

@@ -1,27 +1,480 @@
 <config>
 {
-'navigationBarTitleText': '产品对比'
+'navigationBarTitleText': '产品对比',
+'disableScroll': true
 }
 </config>
 <template>
-    <div></div>
+    <scroll-view style="height:100vh" :scroll-y="!showPopup" enable-flex @scroll="scroll">
+        <div class="content" v-if="!loading">
+            <div class="top tr" :class="{ fixedTop: fixedTop }">
+                <div class="left td" :class="{ fixedLeft: fixedLeft }">产品</div>
+                <div class="td pro" v-for="(item, index) in showContrastList" :key="index" @click="showMore(item.id)">
+                    <div class="name">
+                        {{ getName(item) }}
+                    </div>
+                    <van-icon v-if="contrastList.length > 2" name="arrow-down" />
+                </div>
+            </div>
+
+            <div class="tr">
+                <div class="left td" :class="{ fixedLeft: fixedLeft }">
+                    图片
+                </div>
+                <div class="td pro" v-for="(item, index) in showContrastList" :key="index">
+                    <van-image :src="getList(item.img)" :width="60" :height="60" fit="cover" />
+                </div>
+            </div>
+            <div class="tr">
+                <div class="left td" :class="{ fixedLeft: fixedLeft }">
+                    {{ $t('chan-pin-pin-pai') }}
+                </div>
+                <div class="td pro" v-for="(item, index) in showContrastList" :key="index">
+                    {{ item.brand }}
+                </div>
+            </div>
+            <div class="tr">
+                <div class="left td" :class="{ fixedLeft: fixedLeft }">
+                    产品类别
+                </div>
+                <div class="td pro" v-for="(item, index) in showContrastList" :key="index">
+                    {{ getName(item.productCategory) }}
+                </div>
+            </div>
+            <div class="tr">
+                <div class="left td" :class="{ fixedLeft: fixedLeft }">
+                    {{ $t('ying-yong-ling-yu') }}
+                </div>
+                <div class="td pro" v-for="(item, index) in showContrastList" :key="index">
+                    {{ getApplicationField(item) }}
+                </div>
+            </div>
+            <div class="tr">
+                <div class="left td" :class="{ fixedLeft: fixedLeft }">
+                    {{ $t('chan-pin-biao-qian') }}
+                </div>
+                <div class="td pro" v-for="(item, index) in showContrastList" :key="index">
+                    <div class="tags">
+                        <div class="tag" v-for="(tag, tagIndex) in gstTags(item)" :key="tagIndex">
+                            {{ tag }}
+                        </div>
+                        <span v-if="gstTags(item).length === 0">-</span>
+                    </div>
+                </div>
+            </div>
+            <div class="tr">
+                <div class="left td" :class="{ fixedLeft: fixedLeft }">
+                    {{ $t('ping-jun-jiao-huo-zhou-qi') }}
+                </div>
+                <div class="td pro" v-for="(item, index) in showContrastList" :key="index">
+                    {{ item.averageLeadTime || '-' }}
+                </div>
+            </div>
+            <div class="tr">
+                <div class="left td" :class="{ fixedLeft: fixedLeft }">
+                    线下体验
+                </div>
+                <div class="td pro" v-for="(item, index) in showContrastList" :key="index">
+                    {{ item.offlineExperience ? '可以' : '-' }}
+                </div>
+            </div>
+            <block v-for="(p, pIndex) in tableParameters" :key="pIndex">
+                <div class="tr firstLevel" v-if="p.isPre">
+                    <div class="td left" :class="{ fixedLeft: fixedLeft }">{{ p.chName }}</div>
+                    <template v-if="!p.children">
+                        <div class="td pro" v-for="item in showContrastList" :key="item.id">
+                            {{ getKey(item.id, p.chName) }}
+                        </div>
+                    </template>
+                </div>
+                <div class="tr secondLevel" v-else>
+                    <div class="td left" :class="{ fixedLeft: fixedLeft }">{{ p.chName }}</div>
+                    <template v-if="!p.children">
+                        <div class="td pro" v-for="item in showContrastList" :key="item.id">
+                            {{ getKey(item.id, p.chName) }}
+                        </div>
+                    </template>
+                </div>
+            </block>
+        </div>
+
+        <van-popup :show="showPopup" round position="bottom" custom-style="height: 656rpx" @close="showPopup = false">
+            <div class="title">选择商品</div>
+            <scroll-view scroll-y style="height:576rpx">
+                <div class="product-list">
+                    <product-row
+                        :checked="[...chooseIds].includes(item.id)"
+                        v-for="item in popList"
+                        :key="item.id"
+                        :info="item"
+                        :hasDel="false"
+                        @choose="choose"
+                        checkPosition="right"
+                    ></product-row>
+                </div>
+            </scroll-view>
+        </van-popup>
+    </scroll-view>
 </template>
 <script>
 import { mapState } from 'vuex';
+import ProductRow from '../components/product/RowMini.vue';
 export default {
     data() {
-        return {};
+        return {
+            parameters: [],
+            fixedTop: false,
+            fixedLeft: false,
+            chooseIds: [],
+            showPopup: false,
+            nowChooseId: 0,
+            loading: false
+        };
     },
     computed: {
         ...mapState(['contrastInfo']),
         contrastList() {
             return this.contrastInfo.product || [];
+        },
+        popList() {
+            return [...this.contrastList].filter(item => {
+                return !this.chooseIds.includes(item.id) || item.id === this.nowChooseId;
+            });
+        },
+        showContrastList() {
+            return [...this.chooseIds].map(item => {
+                return [...this.contrastList].find(_child => {
+                    return _child.id === item;
+                });
+            });
+        },
+        tableParameters() {
+            const parameters = { ...this.parameters };
+            const _map = new Map();
+            for (let productId in parameters) {
+                let _parameters = parameters[productId];
+
+                if (![...this.chooseIds].includes(Number(productId))) {
+                    continue;
+                }
+                _parameters.forEach(_parameter => {
+                    if (!_map.has(_parameter.chName)) {
+                        _map.set(_parameter.chName, {
+                            chName: _parameter.chName,
+                            enName: _parameter.enName,
+                            sort: _parameter.value ? 1 : 0
+                        });
+                    } else if (_parameter.value) {
+                        let before = { ..._map.get(_parameter.chName) };
+                        _map.set(_parameter.chName, {
+                            ...before,
+                            sort: before.sort + 1
+                        });
+                    }
+
+                    if (_parameter.children) {
+                        _parameter.children.forEach(_child => {
+                            if (!_map.has(_child.chName)) {
+                                _map.set(_child.chName, {
+                                    chName: _child.chName,
+                                    enName: _child.enName,
+                                    pre: _parameter.chName,
+                                    sort: _child.value ? 1 : 0
+                                });
+                            } else if (_child.value) {
+                                let before = { ..._map.get(_child.chName) };
+                                _map.set(_child.chName, {
+                                    ...before,
+                                    sort: before.sort + 1
+                                });
+                            }
+                        });
+                    }
+                });
+            }
+
+            let backList = [..._map.values()].sort((a, b) => {
+                return b.sort - a.sort;
+            });
+
+            let list = backList.filter(item => {
+                return !item.pre;
+            });
+
+            list = list.map(item => {
+                let _num = backList
+                    .filter(_c => {
+                        return _c.pre === item.chName;
+                    })
+                    .reduce((total, curr) => {
+                        return total + curr.sort;
+                    }, 0);
+                return [
+                    {
+                        ...item,
+                        children: backList.filter(_c => {
+                            return _c.pre === item.chName;
+                        }).length,
+                        isPre: true,
+                        sort: _num || item.sort
+                    },
+                    backList.filter(_c => {
+                        return _c.pre === item.chName;
+                    })
+                ];
+            });
+
+            list = list.sort((a, b) => {
+                return b[0].sort - a[0].sort;
+            });
+
+            list = list.flat(Infinity);
+
+            return list;
+        },
+        productParameterMap() {
+            const _map = new Map();
+            const parameters = { ...this.parameters };
+            for (let productId in parameters) {
+                let _parameters = parameters[productId];
+                let _parametersMap = new Map();
+                _parameters.forEach(_parameter => {
+                    if (_parameter.value) {
+                        _parametersMap.set(_parameter.chName, _parameter.value);
+                    }
+
+                    if (_parameter.children) {
+                        _parameter.children.forEach(_child => {
+                            if (_child.value) {
+                                _parametersMap.set(_child.chName, _child.value);
+                            }
+                        });
+                    }
+                });
+                _map.set(productId, _parametersMap);
+            }
+
+            return _map;
+        }
+    },
+    onLoad(options) {
+        this.loading = true;
+        this.$loading('加载中...');
+        if (options.chooseIds) {
+            this.chooseIds = options.chooseIds.split(',').map(item => {
+                return Number(item);
+            });
         }
     },
     methods: {
         loginMethods() {
-            this.$store.dispatch('getContrastInfo');
+            this.$store.dispatch('getContrastInfo').then(res => {
+                if (res.product.length > 0) {
+                    this.getParameters();
+                    if (this.chooseIds.length === 0) {
+                        this.chooseIds = res.product
+                            .filter((item, index) => {
+                                return index < 2;
+                            })
+                            .map(item => {
+                                return item.id;
+                            });
+                    }
+
+                    this.loading = false;
+                    this.$loading.close();
+                }
+            });
+        },
+        getParameters() {
+            this.$nextTick(() => {
+                this.$http
+                    .post('/productParameter/productsParameter', {
+                        ids: this.contrastList
+                            .map(item => {
+                                return item.id;
+                            })
+                            .join(',')
+                    })
+                    .then(res => {
+                        for (let key in res) {
+                            res[key] = this.filter(res[key]);
+                        }
+                        console.log(res);
+                        this.parameters = res;
+                    });
+            });
+        },
+        filter(list) {
+            return list.map(item => {
+                if (item.children.length > 0) {
+                    return {
+                        ...item,
+                        chName: item.chName.replace(/ /g, ''),
+                        children: this.filter(item.children)
+                    };
+                } else {
+                    return {
+                        ...item,
+                        chName: item.chName.replace(/' '/g, '')
+                    };
+                }
+            });
+        },
+        getKey(productId, name) {
+            return this.productParameterMap.get(productId.toString()).has(name)
+                ? this.productParameterMap.get(productId.toString()).get(name)
+                : '-';
+        },
+        getApplicationField(productInfo) {
+            let list = productInfo.applicationField ? [...productInfo.applicationField] : [];
+            list = list.map(item => {
+                return this.getName(item);
+            });
+            if (productInfo.customApplicationField) {
+                const _list = productInfo.customApplicationField.split(',');
+
+                list = [...list, ..._list];
+            }
+            return list.join('、');
+        },
+        gstTags(productInfo) {
+            return productInfo.tag || [];
+        },
+        scroll(e) {
+            if (e.detail.scrollTop > 0) {
+                this.fixedTop = true;
+            } else {
+                this.fixedTop = false;
+            }
+
+            // if (e.detail.scrollLeft > 0) {
+            //     this.fixedLeft = true;
+            // } else {
+            //     this.fixedLeft = false;
+            // }
+        },
+        choose(id) {
+            const chooseIds = [...this.chooseIds];
+            chooseIds.splice(chooseIds.indexOf(this.nowChooseId), 1, id);
+            this.chooseIds = chooseIds;
+            this.showPopup = false;
+        },
+        showMore(id) {
+            if (this.contrastList.length <= 2) {
+                return;
+            }
+            this.nowChooseId = id;
+            this.showPopup = true;
         }
+    },
+    components: {
+        ProductRow
     }
 };
 </script>
+<style lang="less">
+.tr {
+    display: flex;
+    width: 100%;
+    z-index: 1;
+    .td {
+        padding: 9px;
+        background: #ffffff;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        text-align: center;
+        z-index: 1;
+        box-sizing: border-box;
+
+        font-size: 12px;
+        font-weight: bold;
+        color: #292c33;
+        line-height: 22px;
+
+        &:not(:last-child) {
+            border-right: 1px solid #f2f3f5;
+        }
+    }
+
+    .left {
+        width: 90px;
+        min-width: 90px;
+        position: sticky;
+        left: 0px;
+        z-index: 10;
+    }
+
+    .pro {
+        min-width: 142px;
+        flex-grow: 1;
+    }
+
+    &.top {
+        position: sticky;
+        top: 0;
+        z-index: 20;
+    }
+    &.secondLevel {
+        .td {
+            background: #f5f7fa;
+        }
+    }
+
+    border-top: 1px solid #f2f3f5;
+}
+
+.tags {
+    display: flex;
+    flex-wrap: wrap;
+    .tag {
+        font-size: 10px;
+        color: #565b66;
+        line-height: 18px;
+        padding: 0 3px;
+        border-radius: 2px;
+        border: 1px solid #dcdfe6;
+        margin-right: 4px;
+        margin-bottom: 4px;
+    }
+}
+
+.fixedLeft {
+    box-shadow: 2px 0px 2px 0px rgba(0, 0, 0, 0.08);
+}
+
+.fixedTop {
+    box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.08);
+}
+
+.name {
+    margin-right: 8px;
+}
+
+.product-list {
+    padding: 0 16px;
+    .product {
+        padding: 16px 0;
+        position: relative;
+    }
+    .product + .product {
+        &::after {
+            content: '';
+            position: absolute;
+            top: 0;
+            left: 28px;
+            right: 0px;
+            height: 1px;
+            background: #f5f7fa;
+        }
+    }
+}
+.title {
+    font-size: 16px;
+    height: 40px;
+    line-height: 40px;
+    padding: 0 20px;
+    font-weight: bold;
+}
+</style>

+ 21 - 2
src/pagesProduct/Detail.vue

@@ -80,7 +80,8 @@
                 >
                     <van-tab title="图文详情">
                         <div class="detail-content">
-                            <rich-text v-if="detailImg" :nodes="detailImg"></rich-text>
+                            <!-- <rich-text v-if="detailImg" :nodes="detailImg"></rich-text> -->
+                            <div v-html="detailImg" v-if="detailImg"></div>
                             <van-empty v-else description="暂无详情" />
                         </div>
                     </van-tab>
@@ -184,7 +185,7 @@ export default {
         detailImg() {
             let detail = this.productInfo.detailImg || '';
             if (detail) {
-                // detail = detail.replace(/<img/g, `<img class='detaiImg' `);
+                detail = detail.replace(/<img/g, `<img bindtap='clickImg' `);
                 detail = detail.replace(/<video/g, `<video style='width:345px;height:180px' `);
             }
             return detail;
@@ -218,6 +219,24 @@ export default {
                     this.vendorInfo = res;
                 });
         }
+
+        setTimeout(() => {
+            wx.createSelectorQuery()
+                .selectAll('.a-class')
+                .boundingClientRect(function(rects) {
+                    rects.forEach(function(rect) {
+                        rect.id; // 节点的ID
+                        rect.dataset; // 节点的dataset
+                        rect.left; // 节点的左边界坐标
+                        rect.right; // 节点的右边界坐标
+                        rect.top; // 节点的上边界坐标
+                        rect.bottom; // 节点的下边界坐标
+                        rect.width; // 节点的宽度
+                        rect.height; // 节点的高度
+                    });
+                })
+                .exec();
+        }, 1500);
     },
     onPageScroll() {},
     methods: {

+ 9 - 5
src/pagesProduct/FilterProduct.vue

@@ -28,19 +28,22 @@ export default {
             categoryIds: '',
             vendorId: '',
             list: [],
-            category: {}
+            category: null
         };
     },
     onLoad(options) {
         if (options.categoryIds) {
             this.categoryIds = options.categoryIds;
+
+            this.$http.get('/productCategory/get/' + options.categoryIds).then(res => {
+                this.category = res;
+            });
+        }
+        if (options.vendorId) {
             this.vendorId = options.vendorId;
             wx.setNavigationBarTitle({
                 title: options.vendorName
             });
-            this.$http.get('/productCategory/get/' + options.categoryIds).then(res => {
-                this.category = res;
-            });
         }
         this.getList();
     },
@@ -67,7 +70,8 @@ export default {
                     {
                         query: {
                             productCategoryId: this.categoryIds,
-                            vendorInfoId: this.vendorId
+                            vendorInfoId: this.vendorId,
+                            deviceStatus: 'NOW_ON_SHELF'
                         }
                     },
                     {

+ 7 - 3
src/pagesProduct/FilterVendor.vue

@@ -9,8 +9,8 @@
 </config>
 <template>
     <div>
-        <div class="header">
-            <van-sticky v-if="category" :z-index="200" :offset-top="0">
+        <div class="header" v-if="category">
+            <van-sticky :z-index="200" :offset-top="0">
                 <div class="top">
                     <van-button :color="$colors.warn" plain block>{{ getName(category) }}</van-button>
                 </div>
@@ -18,7 +18,11 @@
         </div>
 
         <div class="main">
-            <van-index-bar :index-list="indexList" :sticky-offset-top="62" :highlight-color="$colors.warn">
+            <van-index-bar
+                :index-list="indexList"
+                :sticky-offset-top="category ? 62 : 0"
+                :highlight-color="$colors.warn"
+            >
                 <block v-for="(item, index) in dataList" :key="index">
                     <van-index-anchor :index="item[0]"></van-index-anchor>
                     <van-cell

+ 5 - 3
src/pagesVendor/About.vue

@@ -139,9 +139,11 @@ export default {
     },
     mounted() {
         this.$http.post('/decoration/hotProduct?vendorId=' + this.$mp.options.id).then(res => {
-            this.productList = res.filter(item => {
-                return item.deviceStatus !== 'NOW_ON_SHELF';
-            });
+            this.productList = res
+                ? res.filter(item => {
+                      return item.deviceStatus == 'NOW_ON_SHELF';
+                  })
+                : [];
         });
 
         this.$http

+ 5 - 1
src/pagesVendor/Detail.vue

@@ -22,7 +22,7 @@
             id="scroller"
             @scroll="onScroll"
             :style="{ height: `calc(100vh - ${barHeight}px)` }"
-            :scroll-y="true"
+            scroll-y
             class="scrollView"
             efresher-background="#0F264D"
         >
@@ -73,6 +73,7 @@ import collection from '../mixins/collection';
 import About from './About.vue';
 import Product from './Product.vue';
 import News from './News.vue';
+import { mapState } from 'vuex';
 
 export default {
     components: { SearchBar, About, Product, FixedButton, News },
@@ -93,6 +94,9 @@ export default {
             offsetTop: 0
         };
     },
+    computed: {
+        ...mapState(['barHeight'])
+    },
     mixins: [collection],
     methods: {
         submitSearch(e) {

+ 1 - 0
src/store/index.js

@@ -128,6 +128,7 @@ export default new Vuex.Store({
         getContrastInfo(context) {
             return http.http.get('/intentionList/myCompared').then(res => {
                 context.commit('updateContrastInfo', res || {});
+                return Promise.resolve(res);
             });
         },
         getBarHeight(context) {