Vue移动端项目---尚硅谷外卖
使用Vue实现移动端外卖app
文章目录
Vue移动端项目–尚硅谷外卖
本项目是前后端分离的移动端外卖项目,做的功能包含:用户的注册登录、主页商家列表的展示、商家详情页、添加购物车、搜索商家等。使用axios发送请求,获取数据,用ajax来实现前后端交互。其中还使用了mockjs来产生和API请求接口格式相同的数据,以满足后端接口暂未完成的情况。在部分功能上使用BScroll实现滚动。可以使用懒加载的方式来引入路由,来实现项目优化。
项目地址
项目目录结构介绍
├─api ------------------请求文件夹,放一些请求接口的代码。或者封装的请求接口函数
├─common ----------------普通文件夹,存放一些资源文件
│ ├─stylus -------------stylus文件夹,存放stylus样式文件
│ └─utils -------------存放资源类js文件
├─components -----------组件文件夹,存放vue的公共组件(注册于全局)
│ ├─AlertTip ----------提示组件,封装了一个提示,可以直接调用,用于提示错误等
│ ├─CartControl -------项目中加入购物车时,点击+/-来改变食物数量
│ ├─Food -------------商家中食品的详情组件
│ ├─FooterGuide --------底部导航组件,点击不同的按钮,跳至不同的路由页面。根据页面会选择是否显示底部的导航(如登录注册页面不需要显示)
│ ├─HeaderTop ---------头部组件,有个title参数,在不同的页面显示不同的标题
│ ├─RatingSelect -------评价筛选组件,在商家评价页面调用
│ ├─ShopCart -----------购物车组件
│ ├─ShopHeader ---------商家头部组件,每个商家页面都会展示不同的头部,可以重复调用。
│ ├─ShopList -----------商家列表组件
│ ├─Split --------------分割组件,用于不同块之间的分割。
│ └─Stars --------------星星组件,在评分时用到。根据不同的分数显示不同的星星
├─filters ---------------自定义过滤器的文件夹
├─mock ------------------有时后端无法及时提供接口,可以先按照后端接口的数据格式,使用mock产生随机数据。使用时与调用接口相似
├─pages -----------------存放主体页面。一般可以是充当路由的组件。
│ ├─Login --------------登录注册页面。登录时有两种登录方式。使用表单验证。
│ ├─Msite --------------主题页面。进入app时显示的页面。包含头部、导航、商家列表以及底部导航。
│ ├─Order --------------订单页面。显示订单列表,如果没有登录,需要先登录。(未实现)
│ ├─Profile -----------个人页面。未登录时,不显示信息。可以跳转至登录注册页面。如果已登录,会在最下方添加退出登录按钮。
│ ├─Search -------------搜索商家页面。根据关键字搜索商家列表,如果搜索时没有相应的商家,则显示“抱歉。。。。”
│ └─Shop ---------------商家页面。包含三个子路由。
│ ├─ShopGoods ------商家食品组件。展示商家的所有食品。分为两块:左侧分类和右侧食物列表。使用了BScroll(使用这个时,需要注意调用的时间)
│ ├─ShopInfo -------商家信息组件。展示商家的详细信息。
│ └─ShopRatings ----客户对商家的评价。
├─router ----------------路由文件夹。用来存放路由配置js文件。
└─store -----------------Vuex。
项目源码地址: https://github.com/weiyuewang14/gshop
移动端适配
首先在index.html文件中添加一个meta标签
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
其他的适配方式还有
- 动态REM
- vw
头部和底部导航
首先头部定义一个组件HeaderTop,有个属性名title。当其他组件调用时,传入一个属性值即可。
<HeaderTop :title="value"></HeaderTop>
底部导航,在App组件中使用。底部导航只有在一些情况下才需要展示,因此在路由配置meta中添加showFooter属性(true为显示,false为不显示)来标识是否显示底部导航。
<FooterGuide v-if="$route.meta.showFooter"></FooterGuide>
在底部导航中,当显示哪个路由组件时才会有相应的样式,通过动态绑定class的值实现。
登录注册页面
包含两种登录方式:密码登录和短信登录。两种登录方式会根据选择来显示,用到了一个值loginWay(1:表示密码登录;2:表示短信登录)和class属性值on。登录时需要提交表单,此处用到了表单验证,使用正则表达式对输入的数据做的相应的校验。
密码登录
密码登录时,会发送请求获取验证码。发送验证码时,需要注意每次请求的路径需要不同,所以在请求链接后加入一个当前时间的param参数。
src = 'http://xxx/xxxx/xxx?time' + Date.now()
密码输入框有一个按钮来选择是否显示密码。按钮的动态变化是根据class属性值on来决定。定义一个showPwd标识,来控制显示。
短信登录
短信登录时,首先需要输入格式正确的手机号,否则发送验证码按钮无效。点击发送验证码之后,会有一个已发送时间的倒计时,在倒计时归0之前不能再次点击发送验证码。当登录失败时,还需要重置倒计时。
Profile页面
我的页面分两种状态:已登录和未登录。
未登录
用户未登录时,不显示登录信息,而且可以点击登录到登录注册页面。
已登录
用户已登录时,则会显示用户名。当还未绑定手机号的情况下,会显示暂无绑定手机号。
在Profile页面,如果用户已经登录,在最下方会添加一个退出登录按钮,点击退出登录会发送请求。
Msite页面
打开外卖移动端时,会直接到达Msite页面,在路由配置中设置了重定向。
在页面右上角,会显示用户是否登录。当页面挂载的时候,就会请求接口,获得导航部分的分类数据和商家列表数据。
首页导航
使用轮播图实现。轮播图的创建时,可能会出现创建时数据还未加载,因此需要监听请求到的分类数据。此处使用Vue提供的nextTick方法。
Vue.$nextTick()的定义:在下次 DOM 更新循环结束之后执行延迟回调。
当数据更新后,先等待DOM更新。因此在此时创建轮播图对象。
// 监听食品分类数据的变化
categories (value) {
// 修改数据之后,会立即使用这个数据并等待DOM更新
this.$nextTick(() => {
//创建轮播图
new Swiper('.swiper-container', {
loop: true,
//如果要是分页
pagination: {
el: '.swiper-pagination'
}
})
})
}
ShopList
将请求到的数据通过v-for展示在页面上,然后给每个列表项添加一个click事件,跳转路由至商家页面。
商家页面
商家页面包含商家页面的头部、三个子路由(点餐、评价、商家信息)。使用了BScroll实现滑动,使用BScroll时注意其会将默认click事件关闭,需要通过属性值来打开。
better-scroll是基于父元素固定高度,溢出才滚动的,所以父元素务必定高,否则无法滚动。
this.foodsScroll = new BScroll('.foods-wrapper', {
probeType: 2, // 1:,2:,3:
click: true, // 开启click事件。BScroll是默认关闭点击事件的。
})
点餐
分为菜单列和食品列表。当选择某个菜单时,食品列表滑动到相应的位置。使用BScroll来实现。
需要收集当前分类的index和每个食品分类的头部值,形成映射。
添加数量
点击添加或减少食品数量进入购物车时,需要派发action实现同步更新food中的count值。当第一次加入购物车时,food中不存在count属性,需要添加属性。
Vue.set(obj, 'propertyName', value)
购物车
购物车的显示基于购物车中的食品数量和用户的点击事件。
当没有食品加入购物车时,无法查看购物车。当购物车中有食品时,用户可以点击购物车查看购物车中的食品。
评价
难点:筛选评论时的条件。
/!*
selectType: 2, //全部 // rating.rateType(0/1)
onlyContent: true // 是否只看有内容的 //rating.text
result = !onlyContent || rating.text.length > 0
*!/
/!*
selectType: 0/1/2 如果是0/1需要判断是否与rating.rateType相等, 如果是2就不需要
onlyContent: true/false 如果为true需要判断rating.text必须有值, 如果是false就不需要
result= selectType === rateType && (!onlyContent || rating.text.length > 0)
*!/
评价时间使用自定义过滤器
import moment from 'moment'
import Vue from 'vue'
Vue.filter('dateString', function (value, format) {
return moment(value).format(format || 'YYYY-MM-DD HH:mm:ss')
})
商家
商家信息展示时,有两处使用了BScroll,整体的一个和商家实景。分为垂直滑动(scrollY)和水平滑动(scrollX)。
在商家实景处,创建BScroll对象后,还是不能滑动,是因为ul没有被撑开,所以需要计算ul的宽度。
总结
搭建整个项目时,需要先考虑布局,如何划分模块?如何划分是几级路由?然后查看后端接口,先进行测试(postman)。还有解决跨域问题(使用代理)。
- 使用axios发送请求
- 使用ajax进行前后台交互
- 组件之间的数据传递(子传父ref、父传子props、兄弟之间的传递等)vuex可以实现所有的数据传递
- 当出现某个值不存在某个属性的错误时,可能是由于数据还未能加载的时候,取了其中的数据。
更多推荐
所有评论(0)