目录
head头部导航
左边后退图标:图标是iconfont
v-if
来控制该标签的显示和隐藏 因为在某些组件中(如购物车组件 首页组件) 不使用该图标 监听该图标的点击 点击后返回上一页
<span class="go-back" v-if="goBack" @click="funGoBack()">
<i class="iconfont"></i>
</span>
<script>
funGoBack() { //返回上一页(不怎么懂)
//$route.fullPath: string 完成解析后的URL,包含查询参数和hash的完整路径。
if (this.$route.fullPath.indexOf('/store') !== -1) {
this.$router.push({path: '/index'})// 返回首页
} else {
this.$router.go(-1);// 如果是从商家详情页跳转过来的 则后退
}
}
</script>
中间展示标题:由父组件传入
<span class="title">{{title}}</span>
右边可能啥也没有 可能是… 可能是编辑/取消编辑购物车:使用v-if控制…的展示与隐藏 插槽来控制编辑/取消编辑购物车
<!--
在商家详情页组件中 右上角会有... 点击展示更多信息 此处只是展示了... 并没有实现点击展示更多的功能
该标签用来展示... ...也是iconfont v-if来控制该标签的显示和隐藏 因为也就在店家详情页组件会用到他
-->
<span class="more" v-if="more"><i class="iconfont"></i></span>
<!--下面是三个插槽-->
<slot name="save-address"></slot>
<!--下面两个用于购物车组件中 展示右上角编辑、取消编辑-->
<slot name="edit-cart"></slot>
<slot name="cancel-edit-cart"></slot>
bottom底部导航
因为点击之后会跳转到相应组件 所以使用router-link
包裹 三个组件使用三个router-link
包裹 里面放两张图片 一张未被点击时展示 一张被点击时展示 使用v-if-else
控制两张图片的显示与隐藏
<router-link to="/index" tag="li">
<div class="index">
<img src="../assets/index-active.png" v-if="active ==='index'">
<img src="../assets/index.png" v-else>
</div>
<span>首页</span>
</router-link>
变量active最开始值为index,展示首页被点击的图片,当路由跳转,挂载到真实DOM后,获取URL的绝对路径,赋给active变量,active和index、order、home进行匹配,展示相应的高亮的图片
mounted() {
//当前路由对象的路径,如'/index
this.active = this.$route.path.slice(1);//取/后的内容
}
loading
使用scss实现
alertTip弹出再消失组件
接收父组件传入的两个参数:展示的文本内容text、是否展示alertTip组件showTip
这个组件借鉴了vue官网案例,使用transition实现的
addressInfo
接收父组件传入的formData,监听formData的变化,一旦改变,向父组件发送一个事件,并把新数据传给父组件
watch: {
formData(val) {
this.$emit('update:formData', val);
}
}
这个组件就是一个form表单
<form>
<h3>联系人</h3>
<div class="name">
<label for="name">姓名:</label>
<div class="input-container">
<input type="text" v-model="formData.name" id="name" placeholder="请填写收货人的姓名">
</div>
</div>
<div class="gender">
<div @click="selectGender('male')">
<!--如果父组件传给我的formData的gender是male 则展示√这个iconfont-->
<i class="iconfont" v-if="formData.gender === 'male'"></i>
<i class="circle" v-else></i>
<span>先生</span>
</div>
<div @click="selectGender('female')">
<!--如果父组件传给我的formData的gender是female 则展示√这个iconfont 否则两个都不勾选-->
<i class="iconfont" v-if="formData.gender==='female'"></i>
<i class="circle" v-else></i>
<span>女士</span>
</div>
</div>
<div class="phone">
<label for="phone">电话:</label>
<div class="input-container">
<input type="text" v-model="formData.phone" id="phone" placeholder="请填写收货手机号码">
</div>
</div>
<h3>收货地址</h3>
<div class="location">
<label>小区/大厦/学校:</label>
<!--点击后跳转到location组件-->
<router-link :to="{path:'/add_address/location'}" class="to-locate input-container select-address-container">
<i class="iconfont"></i>
<!--formData.title不为空 展示formData.title 为空展示“点击选择”-->
<span v-if="formData.title!==''">{{formData.title}}</span>
<span v-else>点击选择</span>
<i class="iconfont icon-right"></i>
</router-link>
</div>
<div class="house-number">
<label for="house-number">楼号-门牌号:</label>
<div class="input-container">
<input type="text" v-model="formData.house_number" id="house-number" placeholder="例:16号楼427室">
</div>
</div>
</form>
<script>
selectGender(sex) {
if(sex === this.formData.gender) this.formData.gender = '';
else this.formData.gender = sex;
}
}
</script>
littleCart首页右下角的小购物车图标
展示一个小图标iconfont,点击后跳转到购物车组件,右上角展示商品总数
<router-link id="cart" to="/cart">
<span><i class="iconfont"></i></span>
<span class="num" v-if="num">{{num}}</span>
</router-link>
<script>
num() {
let totalNum = 0;
Object.values(this.cartList).forEach(value=>{
totalNum += value.totalNum;
})
return totalNum;
}
</script>
search搜索
左边是个图标,然后是个输入框,右边是个搜索按钮
<div class="search-container">
<div class="search-input">
<i class="iconfont"></i>
<!--
v-model.trim是去除输入框的内容value的前后空格
autofocus:自动获得焦点
search_val:输入值
-->
<input type="text" autofocus="autofocus" v-model.trim="search_val" :placeholder="placeholder">
</div>
<span class="btn-search" v-bind:class="{ active: btnActive }">搜索</span>
</div>
<script>
watch: {
//监听输入框内容的变化,如果输入的内容为空则btnActive为false,展示不活跃的状态,如果输入框有内容,
//则调用父组件传进来的fun_click函数,btnActive置为true,展示活跃的状态
search_val(data) {
if (data !== "") {
this.btnActive = true;
this.fun_click(data);
} else {
this.btnActive = false;
}
}
}
</script>
父组件给子组件传入展示的值placeholder和fun_click函数,fun_click函数会调用searchRestaurant函数,searchRestaurant会带着输入框的参数调用(使用get请求)后端searchRestaurant接口,后端searchRestaurant会带着参数查询数据库,若查询到了,返回状态码200和查询结果给前端,若没有查询到 则返回状态码-1和“搜索餐馆失败”给前端,查询是精准查询,没有对数据进行排序啥的,直接从数据库中按顺序取出来然后展示出来,数据在数据库中的顺序也没有规律。
fun_click(val) {
this.keyword = val;
searchRestaurant({keyword: val}).then((response) => {
let res = response.data;
if (res.status === 200) {
if (res.data.length) {
this.searchList = res.data;
} else {
this.alertText = '找不到该餐馆,输入汉堡试试';
this.showTip = true;
}
} else {
this.alertText = res.message;
this.showTip = true;
}
})
}
searchRestaurant = (data) => {//输入关键词搜索餐馆
let req = {data,url: 'v1/search/restaurant'}
return _get(req);
}
selector选择
显然,只有当该食品在购物车中时,才显示左边那个白色的-图标。点击两个图标,分别调用vuex中的cart模块的方法addCart、reduceCart
<div id="selector">
<!--food_num为0 没有添加食品到购物车 不展示标签-->
<div class="ball-container" @click="reduceCart" v-if="food_num">
<span class="reduce"><i class="iconfont icon-reduce"></i></span>
</div>
<span class="number" v-if="food_num">{{food_num}}</span>
<!--点击后 添加食品到购物车-->
<div class="ball-container" @click="addCart($event)">
<span class="add"><i class="iconfont icon-add"></i></span>
</div>
</div>
<script>
addCart(event) {
let {pic_url} = this.poi_info;// 获取商店图片
let restaurant_name = this.poi_info.name;// 获取商店名称
this.$store.dispatch('addCart', {
restaurant_name,
pic_url,
name: this.name,
price: this.price,
foods_pic: this.pic,
food_id: this.food_id,
restaurant_id: this.poi_info.id,
});
},
reduceCart() {
this.$store.dispatch('reduceCart', {restaurant_id: this.poi_info.id, food_id: this.food_id})
}
</script>
食品数量food_num的获取逻辑:先通过cartList.poi_info.id获取到相应的商店信息,若不存在商店信息,表示购物车中没有该商店的食品,返回0。若存在商店信息,再根据食品id获取相应的食品,若获取不到食品,说明该商店的该食品不在购物车中,可能是别的食品在购物车中,若能够获取食品,将食品数量返回cartList.poi_info.id.food_id.num
food_num() {
let restaurant = this.cartList[this.poi_info.id];
return restaurant ? restaurant[this.food_id] ? restaurant[this.food_id]['num'] : 0 : 0;
}
star星级图标
我们有三个iconfont,亮的星、亮一半的星、不亮的星。父组件给star组件传入得分,star组件计算,需要多少亮的星,多少亮一半的星,多少不亮的星。
computed:{
on(){// 得分取整数
return parseInt(this.score);
},
half(){// 得分-整数=小数,小数大于等于0.5取1,小于取0
return this.score - this.on >=0.5 ? 1 : 0;
},
off(){// 总分5分-整数-小数=差值
return 5 - this.on - this.half;
}
}
v-for可以遍历变量,那我们就遍历变量,变量的值为多少,就展示多少亮的星/亮一半的星/不亮的星
<div id="star">
<span class="on" v-for="item in on" :key="'on' + item"></span>
<span class="half" v-for="item in half" :key="'half' + item"></span>
<span class="off" v-for=" item in (5 - on - half)" :key="'off' + item"></span>
</div>