components公用组件

head头部导航

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

左边后退图标:图标是iconfont v-if来控制该标签的显示和隐藏 因为在某些组件中(如购物车组件 首页组件) 不使用该图标 监听该图标的点击 点击后返回上一页

<span class="go-back" v-if="goBack" @click="funGoBack()">
	<i class="iconfont">&#xe61c;</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">&#xe602;</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'">&#xe6da;</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'">&#xe6da;</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">&#xe605;</i>
          	<!--formData.title不为空 展示formData.title 为空展示“点击选择”-->
          	<span v-if="formData.title!==''">{{formData.title}}</span>
          	<span v-else>点击选择</span>
          	<i class="iconfont icon-right">&#xe63f;</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">&#xe641;</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">&#xe7d1;</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">&#xe613;</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">&#xe6a9;</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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值