其四:附近店铺和商家详情
post、get请求封装
前后端通信时,一般会根据接口选择post或get请求。前面使用模拟接口实现登陆时,只封装了post方法。在进行接下来的开发时,先把get方法也封装一下。
在src/utils/request.js下。将post部分的代码复制一份,改成get请求。将data改为params,去掉headers的参数。
export const get = (url, params = {}) => {
return new Promise((resolve, reject) => {
axios.get(url, { params },{
baseURL:...
}).then(
(res) => {
resolve(res)
},
(err) => {
reject(err)
}
)
})
}
再改进一下,将post和get中相同的部分抽离出来
const instance = axios.create({
baseURL:
"...",
timeout: 10000,
})
将两个请求的axios换成instance,并去掉第三个参数的配置。
之后,其他组件需要发送post/get请求,只需要导入这个函数。
附近店铺数据动态化
前面介绍过将数据存在List中,用v-for循环。这里可以把List的数据动态化,即数据从请求接口来。
因为根据实际需求,附近店铺应该是与使用者的位置有关的,不同位置会显示不同的店铺。在进入首页后,应该先获取一下定位信息,然后将定位信息发给后端,再向后端请求数据,这时后端会返回对应位置的附近店铺的数据。这样才能在页面展示正确的店铺信息。
首先在fastmock上新建一个接口,get请求。数据返回的格式根据List一致。比如
dockerList:[
{
icon:"",
title:"首页"
},
{
icon:"",
title:"购物车"
}
]
//接口返回的数据:
{
"error":0,
"data":[
{
icon:"",
title:"首页"
},
{
icon:"",
title:"购物车"
}
]
}
(因为我是用自己的接口,所以数据格式可以自己定。为了方便我就设置成一样的了。实际开发中,应根据实际接口返回的数据调整你的html结构。)
然后调用下之前封装的get请求。
getNearByInfo() {
get("/nearby-list").then(this.getNearByInfoSucc)
},
getNearByInfoSucc(res) {
const data = res.data
if (data.error != 0) return
this.items = data.data
},
在页面加载完成时调用下getNearByInfo
商家详情页开发
组件复用和样式定制
根据设计图发现有很多可以复用的地方。比如NearBy那个展示店铺的框。
将那块代码抽离出来封装成ShopInfo组件,当然一下class的名称可以改。
Nearby页面引入这个组件,将item传入。
<shop-info v-for="item in items" :key="item.item_id" :item="item" />
Shop页面也可以使用这个组件,但是根据设计图,Shop页面是没有border-bottom的,这时候可以再传入一个参数:hideBorder,并将ShopInfo中关于border-bottom的样式拆出来放另一个类里。通过判断hideBorder的值来决定border-bottom显示不显示。
跳转至商家详情
设置对应路由/shop,再给shop-info设置跳转链接
{
path: "/shop",
name: "Shop",
component: () =>
import(/* webpackChunkName: "shop" */ "../views/Shop.vue"),
}
<router-link
to="/shop"
v-for="item in items"
:key="item.item_id"
>
<shop-info :item="item" />
</router-link>
获取对应的商家详情
从不同店铺点击,进入的商家详情页面应该是不一样的。给/shop路由加一个参数,跳转时需要传入这参数才能显示页面。
同样,shop-info的跳转链接也要有变化
path: "/shop/:id",
router-link:
:to="`/shop/${item.item_id}`"
这样点击附近店铺下的店铺,跳转至shop页面的时候,url会加上/id。
接下来,要在shop页面获取对应的数据。
先写一个示范接口,在fastmock里创建一个新的接口,get请求,路径为/shop/:id。请求成功的内容如下“
{
"error":0,
"data":[{
item_id: 1,
item_img: {
src: "http://www.dell-lee.com/imgs/vue3/near.png",
alt: "沃尔玛",
},
item_info: {
title: "沃尔玛",
desc: ["月售1万+", "起送¥0", "基础运费¥5"],
},
item_heighlight: "VIP尊享满89元减4元运费券(每月3张)",
}]
}
Shop页面也使用了shop-info这个组件,
<shop-info :item="item" :hideBorder="true" />
定义请求接口,获取的值赋值给data里的item,并在mounted周期里调用。
getShopInfo() {
get("/shop/1").then(this.getShopInfoSucc)
},
getShopInfoSucc(res) {
if (res.data.error !== 0) return
const data = res.data
this.item = data.data[0]
},
这样页面能渲染出来,但是会报一个警告。是因为mounted周期组件已经渲染完成,但是data里的item还是空的。可以加一个v-if判断
<shop-info v-if="item.item_id" :item="item" :hideBorder="true" />
等item有值了再渲染这个组件。
接下来,请求的地址应该根据url里id的值变化。
使用this.$route获取url里的id
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QuQ1VeEP-1616756714912)(D:\Information\typoraMD\京东\images\route.png)]
get("/shop/1")=>
get(`/shop/${this.$route.params.id}`)
布局
组件
大概就是布局分的那样,有三部分
search__bar
样式没啥好说的,按钮绑定事件,通过this.$route.back()返回上一页。
shop-info
直接复用的代码,item的值由该页面传入。
shop-content
分为左侧的catalog和右侧的shop。
期望实现的功能是,点击左侧,对应样式改变,右侧的商品也改变。
可以在点击左侧的列表时,向后端发出一个请求,这个请求包含一个参数tab。tab为all的时候,返回全部商品的数据,tab为kill的时候,返回秒杀的数据。右侧的商品根据左侧获得的数据渲染。
在fastmock上新建一个接口,get请求,发送地址为/shop/:id/products。内部结构为
{
"error": 0,
"data": [
{
"_id": "1",
"name": "番茄 250g / 份",
"imgUrl": "http://www.dell-lee.com/imgs/vue3/tomato.png",
"sales": 10,
"price": 33.6,
"oldPrice": 39.6
},
{.....}
],
"message": "errno !== 0 时的错误信息"
}
在左侧标签绑定点击事件
getProdctInfo(tab) {
get("/shop/:id/products", { tab }).then(this.getProdctInfoSucc)
}
<li ...
@click="() => {
getProdctInfo(item.tab)
}"
></li>
这样,点击不同的标签会发送带不同参数值的请求。
如何让左侧的样式也一起改变呢?为了方便,左侧列表由一个数组渲染,数组的结构如下:
catalog_list: [
{
name: "全部商品",
tab: "all",
},
{
name: "秒杀",
tab: "kill",
},
{
name: "新鲜水果",
tab: "fruit",
},
],
首次进入页面时,应当展现第一个标签下的商品。即
dataTab = catalog_list[0].tab
直接在data里赋值是不行的,在mounted周期里赋值,并用这个值获取一次商品数据。
mounted() {
this.dataTab = this.catalog_list[0].tab
this.getProdctInfo(this.dataTab)
}
之后每次点击左侧的列表项时,也应该重新对dataTab赋值
getProdctInfo(tab) {
this.dataTab = tab
get("/shop/:id/products", { tab }).then(this.getProdctInfoSucc)
}
也就是说,当dataTab === item.tab的时候,这个item是被选中的,可以添加选中样式。那么li标签的样式可以更换成这样
:class="{catalog__item: true,'catalog__item--active': dataTab === item.tab,}"
路由加载优化
用这种方式加载
{
path: "/",
name: "Home",
component: () =>
import(/* webpackChunkName: "home" */ "../views/Home.vue"),
}