|
| 1 | +<template> |
| 2 | + <div class="shoplist_container"> |
| 3 | + <ul v-load-more="loadmore" v-if="shopListArr.length" type="1"> |
| 4 | + <router-link v-for="item in shopListArr" tag="li" :key="item.id" class="shop_li" :to="{path: 'shop', query: {geohash: geohash, id: item.id}}"> |
| 5 | + <section> |
| 6 | + <img :src="imageBaseUrl + item.image_path" class="shop_img"> |
| 7 | + </section> |
| 8 | + <hgroup class="shop_right"> |
| 9 | + <headedr class="shop_detail_header"><!--preminum额外费用--> |
| 10 | + <h4 :class="item.is_premium ? 'preminum' : ''" class="shop_title ellipsis">{{item.name}}</h4> |
| 11 | + <ul class="shop_detal_ul"> |
| 12 | + <li v-for="item in item.supports" :key="item.id" class="supports">{{item.icon_name}}</li> |
| 13 | + </ul> |
| 14 | + </headedr> |
| 15 | + <h5 class="rating_order_num"> |
| 16 | + <section class="rating_order_num_left"> |
| 17 | + <section class="rating_section"> |
| 18 | + <rating-star :rating="item.rating"></rating-star> |
| 19 | + <span class="rating_num">{{item.rating}}</span> |
| 20 | + </section> |
| 21 | + <section class="order_section"> |
| 22 | + 月售{{item.recent_order_num}}单 |
| 23 | + </section> |
| 24 | + </section> |
| 25 | + <section class="rating_order_num_right"> |
| 26 | + <span class="delivery_style delivery_left" v-if="item.delivery_mode">{{item.delivery_mode.text}}</span> |
| 27 | + <span class="delivery_style delivery_right" v-if="zhunshi(item.supports)">准时达</span> |
| 28 | + </section> |
| 29 | + </h5> |
| 30 | + <h5 class="fee_distance"> |
| 31 | + <p class="fee"> |
| 32 | + ¥{{item.minimum_order_amount}}起送<span class="segmentation">/</span>{{item.piecewise_agent_fee.tips}} |
| 33 | + </p> |
| 34 | + <p class="distance_time"> |
| 35 | + <span v-if="Number(item.distance)">{{item.distance > 1000 ? (item.distance/1000).toFixed(2) + 'km': item.distance + 'm'}} |
| 36 | + <span class="segemtation">/</span> |
| 37 | + </span> |
| 38 | + <span v-else>{{item.distance}}</span> |
| 39 | + <span class="segemtation">/</span> |
| 40 | + <span class="order_time">{{item.order_load_time}}</span> |
| 41 | + </p> |
| 42 | + </h5> |
| 43 | + </hgroup> |
| 44 | + </router-link> |
| 45 | + </ul> |
| 46 | + <ul v-else class="animation_opacity"> |
| 47 | + <li class="list_back_li" v-for="item in 10" :key="item"> |
| 48 | + <img :src="imageUrl" class="list_back_svg"> |
| 49 | + </li> |
| 50 | + </ul> |
| 51 | + <p class="empty_data" v-if="touched">没有更多了</p> |
| 52 | + <aside class="return_top" @click="backTop" v-if="showBackStatus"> |
| 53 | + <svg class="back_top_svg"> |
| 54 | + <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#backtop"></use> |
| 55 | + </svg> |
| 56 | + </aside> |
| 57 | + <div ref="abc" style="background-color: red;"></div> |
| 58 | + <transition name="loading"> |
| 59 | + <loading v-show="showLoading"></loading> |
| 60 | + </transition> |
| 61 | + </div> |
| 62 | +</template> |
| 63 | +<script> |
| 64 | +import {mapState} from 'vuex' |
| 65 | +import {imgBaseUrl} from '@/config/env' |
| 66 | +import {showBack, animate} from '@/config/mUtils' |
| 67 | +import {loadMore, getImgPath} from './mixin' |
| 68 | +import loading from './loading' |
| 69 | +import ratingStar from './ratingStar' |
| 70 | +import urls from '../../config/urls' |
| 71 | +export default { |
| 72 | + data () { |
| 73 | + return { |
| 74 | + imageUrl: '../images/shopback.svg', |
| 75 | + offset: 0, // 批次加载店铺列表,每次加载20个 limit = 20 |
| 76 | + shopListArr: [], // 店铺列表数据 |
| 77 | + preventRepeatReuqest: false, // 到达底部加载数据,防止重复加载 |
| 78 | + showBackStatus: false, // 显示返回顶部按钮 |
| 79 | + showLoading: true, // 显示加载动画 |
| 80 | + touchend: false, // 没有更多数据 |
| 81 | + imgBaseUrl: imgBaseUrl |
| 82 | + } |
| 83 | + }, |
| 84 | + mounted () { |
| 85 | + this.initData() |
| 86 | + }, |
| 87 | + components: { |
| 88 | + loading, |
| 89 | + ratingStar |
| 90 | + }, |
| 91 | + props: ['restaurantCategoryId', 'restaurantCategoryIds', 'sortByType', 'deliveryMode', 'supportIds', 'confirmSelect', 'geohash'], |
| 92 | + mixins: [loadMore, getImgPath], |
| 93 | + computed: { |
| 94 | + ...mapState([ |
| 95 | + 'latitude', 'longitude' |
| 96 | + ]) |
| 97 | + }, |
| 98 | + methods: { |
| 99 | + async initData () { |
| 100 | + let res = await this.getShopList((this.latitude, this.longitude, this.offset, this.restaurantCategoryId)) |
| 101 | + this.shopListArr = [...res] |
| 102 | + if (res.length < 20) { |
| 103 | + this.touchend = true |
| 104 | + } |
| 105 | + this.hideLoading() |
| 106 | + // 开始监听scrollTop的值,达到一定程度后显示返回顶部按钮 |
| 107 | + showBack(callbackStatus => { |
| 108 | + this.showBackStatus = callbackStatus |
| 109 | + }) |
| 110 | + }, |
| 111 | + getShopList (latitude, longitude, offset, restaurantCategoryId = '', restaurantCategoryIds = '', orderBy = '', deliveryMode = '', supportIds = []) { |
| 112 | + let supportStr = '' |
| 113 | + supportIds.forEach(item => { |
| 114 | + if (item.status) { |
| 115 | + supportStr += '&support_ids[]=' + item.id |
| 116 | + } |
| 117 | + }) |
| 118 | + let query = '?latitude=' + latitude + '&longitude=' + longitude + '&offset=' + offset + '&limit=20' + '&restaurant_category_id=' + restaurantCategoryId + 'restaurant_category_ids[]' + restaurantCategoryIds |
| 119 | + query += '&order_by=' + orderBy + '&delivery_mode[]=' + deliveryMode + supportStr |
| 120 | + this.$axios.get(urls.getShopList + query).then((res) => { |
| 121 | + return res |
| 122 | + }) |
| 123 | + }, |
| 124 | + async loadMore () { |
| 125 | + if (this.touchend) { |
| 126 | + return |
| 127 | + } |
| 128 | + // 防止重复请求 |
| 129 | + if (this.preventRepeatReuqest) { |
| 130 | + return |
| 131 | + } |
| 132 | + this.showLoading = true |
| 133 | + this.preventRepeatReuqest = true |
| 134 | + // 数据的定位加20位 |
| 135 | + this.offset += 20 |
| 136 | + let res = await this.getShopList(this.latitude, this.longitude, this.offset, this.restaurantCategoryId) |
| 137 | + this.hideLoading() |
| 138 | + this.shopListArr = [...this.shopListArr, ...res] |
| 139 | + // 当获取数据小于20,说明没有更多数据,不需要再次请求数据 |
| 140 | + if (res.length < 20) { |
| 141 | + this.touchend = true |
| 142 | + return |
| 143 | + } |
| 144 | + this.preventRepeatReuqest = false |
| 145 | + }, |
| 146 | + // 返回顶部 |
| 147 | + backTop () { |
| 148 | + animate(document.body, {scrollTop: '0'}, 400, 'ease-out') |
| 149 | + }, |
| 150 | + // 监听父级传来的数据发生变化时,触发此函数重新根据属性值获取数据 |
| 151 | + async listenPropChange () { |
| 152 | + this.showLoading = true |
| 153 | + this.offset = 0 |
| 154 | + let res = await this.getShopList(this.latitude, this.longitude, this.offset, '', this.restaurantCategoryIds, this.sortByType, this.deliveryMode, this.supportIds) |
| 155 | + this.hideLoading() |
| 156 | + // 考虑到本地模拟数据是引用类型,所以返回一个新的数组 |
| 157 | + this.shopListArr = [...res] |
| 158 | + }, |
| 159 | + // 开发环境与编译环境loading隐藏方式不同 |
| 160 | + hideLoading () { |
| 161 | + this.showLoading = false |
| 162 | + }, |
| 163 | + zhunshi (supports) { |
| 164 | + let zhunStatus |
| 165 | + if ((supports instanceof Array) && supports.length) { |
| 166 | + supports.forEach(item => { |
| 167 | + if (item.icon_name === '准') { |
| 168 | + zhunStatus = true |
| 169 | + } |
| 170 | + }) |
| 171 | + } else { |
| 172 | + zhunStatus = false |
| 173 | + } |
| 174 | + return zhunStatus |
| 175 | + } |
| 176 | + }, |
| 177 | + watch: { |
| 178 | + // 监听父级传来的restaurantCategoryIds,当值发生变化的时候重新获取餐馆数据,作用于排序和筛选 |
| 179 | + restaurantCategoryIds: function (value) { |
| 180 | + this.listenPropChange() |
| 181 | + }, |
| 182 | + // 监听父级传来的排序方式 |
| 183 | + sortByType: function (value) { |
| 184 | + this.listenPropChange() |
| 185 | + }, |
| 186 | + // 监听父级的确认按钮是否被点击,并且返回一个自定义事件通知父级,已经接收到数据,此时父级才可以清除已选状态 |
| 187 | + confirmSelect: function (value) { |
| 188 | + this.listenPropChange() |
| 189 | + } |
| 190 | + } |
| 191 | +} |
| 192 | +</script> |
| 193 | +<style lang="scss"> |
| 194 | +@import '../../style/shoplist.scss'; |
| 195 | +</style> |
0 commit comments