美团项目 --- 产品列表页 8

❤ 项目源码 ❤
GitHub地址:https://github.com/Umbrella001/mtapp

一、完成效果图

在这里插入图片描述

① 面包屑的使用

格式:<当前城市定位>美团 > <当前城市定位><所在城市地区名>

HTML:使用E-UI的面包屑页面代码
https://element.eleme.cn/#/zh-CN/component/breadcrumb

这种有静态属性的拼接,为了避免浏览器重绘闪动,使用ssr去服务端渲染;

  <div class="m-crumbs">
    <el-breadcrumb separator=">">
      <el-breadcrumb-item :to="{ path: '/' }">{{ $store.state.geo.position.city.replace('市','') }}美团</el-breadcrumb-item>
      <el-breadcrumb-item><a href="/">{{ $store.state.geo.position.city.replace('市','') }}{{ decodeURIComponent(keyword) }}</a></el-breadcrumb-item>
    </el-breadcrumb>
  </div>
② 设计多级分类的DOM结构

catagroy.vue:
总列表通过上下两部分去循环,此时是不带弹出层的,这个当成组件开发iselect.vue

    <dl class="classic">
      <dt>分类</dt>
      <dt>全部</dt>
      <dd
        v-for="(item,idx) in types"
        :key="idx">
        <iselect
          :name="item.type"
          :list="item.module"/>
      </dd>
    </dl>
    <dl class="classic">
      <dt>区域</dt>
      <dt>全部</dt>
      <dd
        v-for="(item,idx) in areas"
        :key="idx">
        <iselect
          :name="item.type"
          :list="item.module"/>
      </dd>
    </dl>

iselect.vue :
注意接收的数据是hover时对应的数据头,在请求

<template>
  <div class="m-product-select">
    <dl class="tab">
      <dt>{{ name }}<i class="el-icon-arrow-down el-icon--right"/></dt>
      <dd>
        <h3>{{ name }}</h3>
        <span
          v-for="(item,idx) in list"
          :key="idx">{{ item }}</span>
      </dd>
    </dl>
  </div>
</template>

// javascript
export default {
  props: {
    name: {
      type:String,
      default:''
    },
    list: {
      type: Array,
      default(){
        return []
      }
    }
  }
}
③ 产品卡片显示数据格式:

先循环每一个产品整体卡 list.vue > Item

  <div class="m-products-list">
    <dl>
      <dd
        v-for="item in nav"
        :key="item.name"
        :class="[item.name,item.active?'s-nav-active':'']"
        @click="navSelect"
      >{{ item.txt }}</dd>
    </dl>
    <ul>
      <Item
        v-for="(item,idx) in list"
        :key="idx"
        :meta="item"/>
    </ul>
  </div>

产品卡内部DOM结构 product.vue

》评分Rate使用E-UI的评分系统

<template>
  <dl class="s-item">
    <dt>
      <img
        :src="meta.img"
        alt="商品图片">
    </dt>
    <dd>
      <h3><nuxt-link :to="{path:'detail',query:{keyword:meta.name,type:meta.module}}">{{ meta.name }}</nuxt-link></h3>
      <el-rate
        v-model="meta.rate"
        :colors="['#ff9900', '#ff9900', '#FF9900']"
        disabled/>
      <span
        v-if="meta.rate>4"
        class="s-item-comment">很好</span><span
          v-else-if="meta.rate>3"
          class="s-item-comment">一般</span><span
            v-else
            class="s-item-comment">很差</span>
      <span class="s-item-value">{{ meta.rate }}分</span>
      <span class="s-item-comment-total">{{ meta.comment }}人评论</span>
      <p>
        <span class="s-item-type">{{ meta.type }}</span>
        <span class="s-item-addr">{{ meta.addr }}</span>
      </p>
      <p>
        <em class="s-item-price">¥{{ meta.price }}起</em>
        <b>{{ meta.status }}</b>
      </p>
      <ul>
        <!-- <li>
          <span class="detail-type">门票</span>{{meta.ticket}}
        </li>
        <li>
          <span class="detail-type">跟团</span>{{meta.group}}
        </li> -->
        <li v-if="meta.scene&&meta.scene.length">
          <span class="detail-type">景酒</span>{{ meta.scene }}
        </li>
        <li v-else>
          <span class="detail-type">景酒</span>暂无描述
        </li>
      </ul>
    </dd>
  </dl>
</template>

<script>
export default {
  props: {
    meta: {
      type:Object,
      default(){
        return {}
      }
    }
  }
}
</script>

注意:

注意这里中使用的数据结构就是是对象,但之前不是说循环必须使用数组Array吗?但是这里注意,我们在 list.vue 的确是循环list就是数组,但是它里面的数据,是分成另一个组件去开发,所以这里传过去的,当然可以是对象,也务必是对象,为什么? 因为前后端数据格式都不一样,前端要有自己的对象数据,这样即使后端偷偷该数据,我们也只是改个拿数据的地方,而并不需要改动HTML的地方,明白我意思没,前端要漫步映射后端的字段

④ 如何使用第三方插件库 》 高德地图API插件

百度搜索高德地图开放平台 https://lbs.amap.com/ → 成功注册后
→ 点击产品介绍下的地图在这里插入图片描述
→ 移动到页面底部选择【javascript API】
→ 点击控制台,进入【应用管理】,获取key值
在这里插入图片描述
→ 找到javascript的【地图控件】参考文档做即可(注意这里引入地图控件js的话在【准备】,用框架的话就使用异步导入的方式),最终的代码差不多为这样:

<template>
  <div
    :id="id"
    :style="{width:width+'px',height:height+'px',margin:'34px auto'}"
    class="m-map"/>
</template>

<script>
export default {
  props: {
    width: {
      type:Number,
      default:300
    },
    height: {
      type:Number,
      default:300
    },
    point: {
      type:Array,
      default(){
        return [116.46,39.92]
      }
    }
  },
  data() {
    return {
      id: `map`,
      key: '2fc3f4c37d30bd0b4cdecb85ed8c249f'
    }
  },
  watch: {
    point: function (val, old) {
      this.map.setCenter(val)
      this.marker.setPosition(val)
    }
  },
  mounted() {
    let self = this
    self.id = `map${Math.random().toString().slice(4, 6)}`

    window.onmaploaded = () => {
      let map = new window.AMap.Map(self.id, {
        resizeEnable: true,
        zoom: 11,
        center: self.point
      })
      self.map = map
      window.AMap.plugin('AMap.ToolBar', () => {
        let toolbar = new window.AMap.ToolBar()
        map.addControl(toolbar)
        let marker = new window.AMap.Marker({
          icon: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
          position: self.point
        })
        self.marker = marker
        marker.setMap(map)
      })
    }
    const url = `https://webapi.amap.com/maps?v=1.4.10&key=${self.key}&callback=onmaploaded`
    let jsapi = document.createElement('script')
    jsapi.charset = 'utf-8'
    jsapi.src = url
    document.head.appendChild(jsapi)
  },
}
</script>

关于地图相关控件,差不多按照上面的格式配合文档写就行
1.既然图片提出来做单独组件的话,那就不要定死数据,比如地图DOM节点的宽高和id名就应该使用动态数据

⑤ 将详情页设计的所用到的组件整合(page > product.vue),学习如何组合…
<template>
  <el-row class="page-product">
    <el-col :span="19">
      <crumbs :keyword="keyword"/>
      <categroy
        :types="types"
        :areas="areas"/>
      <list :list="list"/>
    </el-col>
    <el-col :span="5">
      <amap
        v-if="point.length"
        :width="230"
        :height="290"
        :point="point"/>
    </el-col>
  </el-row>

</template>

<script>
import Crumbs from '@/components/products/crumbs.vue'
import Categroy from '@/components/products/categroy.vue'
import List from '@/components/products/list.vue'
import Amap from '@/components/public/map.vue'
export default {
  components:{
    Crumbs,
    Categroy,
    List,
    Amap
  },
  data(){
    return {
      list:[],
      types:[],
      areas:[],
      keyword:'',
      point:[]
    }
  },
  async asyncData(ctx){
    let keyword = ctx.query.keyword
    let city = ctx.store.state.geo.position.city
    let {status,data:{count,pois}} = await ctx.$axios.get('/search/resultsByKeywords',{
      params:{
        keyword,
        city
      }
    })
    let {status:status2,data:{areas,types}} = await ctx.$axios.get('/categroy/crumbs',{
      params:{
        city
      }
    })
    if(status===200&&count>0&&status2===200){
      return {
        list: pois.filter(item=>item.photos.length).map(item=>{
          return {
            type: item.type,
            img: item.photos[0].url,
            name: item.name,
            comment: Math.floor(Math.random()*10000),
            rate: Number(item.biz_ext.rating),
            price: Number(item.biz_ext.cost),
            scene: item.tag,
            tel: item.tel,
            status: '可订明日',
            location: item.location,
            module: item.type.split(';')[0]
          }
        }),
        keyword,
        areas: areas.filter(item=>item.type!=='').slice(0,5),
        types: types.filter(item=>item.type!=='').slice(0,5),
        point: (pois.find(item=>item.location).location||'').split(',')
      }
    }
  }
}
</script>

总结:
有没有发现入口文件,发了主要的请求(对比page文件夹下的changeCity.vueproducts.vue),他们各自特点,前者是子组件中自己发送请求,自给自足,最后总入口文件 changeCity.vue 引入即可;后者则是入口组件发送请求,然后发放个对应的组件,也就是父传子的思想,而各自组件就接收 products.vue 传递过来的数据

❤ 项目源码 ❤
GitHub地址:https://github.com/Umbrella001/mtapp

发布了134 篇原创文章 · 获赞 80 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Umbrella_Um/article/details/100587803