Vue2电商实战项目(六)个人中心

个人中心Center组件

  • 先搞定静态组件
### router.routes.js

import Center from '@/pages/Center'
export default [
	{
		name:"center",
		path: "/center",
		component: Center,
		meta: {
			show: true
		}
	}......
  • 拆分Center组件,把我的订单团购订单拆分成两个子路由组件(注意:并不是子组件而是子路由组件)
### myOrder.index.vue(把Center.index.vue结构中,'右侧的内容'拷贝过来)

<template>
	<div class="order-right">
		<div class="order-content">
			<div class="title">
				<h3>我的订单</h3>
			</div>
			<div class="chosetype">
				<table>
					<thead>
						<tr>
							<th width="29%">商品</th>
							<th width="31%">订单详情</th>
							<th width="13%">收货人</th>
							<th>金额</th>
							<th>状态</th>
							<th>操作</th>
						</tr>
					</thead>
				</table>
			</div>
			<div class="orders">

				<table class="order-item">
					<thead>
						<tr>
							<th colspan="5">
								<span class="ordertitle">2017-02-11 11:59 订单编号:7867473872181848 <span
										class="pull-right delete"><img src="../images/delete.png"></span></span>
							</th>
						</tr>
					</thead>
					<tbody>
						<tr>
							<td width="60%">
								<div class="typographic">
									<img src="../images/goods.png">
									<a href="#" class="block-text">包邮 正品玛姬儿压缩面膜无纺布纸膜100粒 送泡瓶面膜刷喷瓶 新款</a>
									<span>x1</span>
									<a href="#" class="service">售后申请</a>
								</div>
							</td>
							<td rowspan="2" width="8%" class="center">小丽</td>
							<td rowspan="2" width="13%" class="center">
								<ul class="unstyled">
									<li>总金额¥138.00</li>
									<li>在线支付</li>

								</ul>
							</td>
							<td rowspan="2" width="8%" class="center">
								<a href="#" class="btn">已完成 </a>
							</td>
							<td rowspan="2" width="13%" class="center">
								<ul class="unstyled">
									<li>
										<a href="mycomment.html" target="_blank">评价|晒单</a>
									</li>

								</ul>
							</td>
						</tr>
						<tr>
							<td width="50%">
								<div class="typographic">
									<img src="../images/goods.png">
									<a href="#" class="block-text">包邮 正品玛姬儿压缩面膜无纺布纸膜100粒 送泡瓶面膜刷喷瓶 新款</a>
									<span>x1</span>
									<a href="#" class="service">售后申请</a>
								</div>
							</td>
						</tr>
					</tbody>
				</table>

				<table class="order-item">
					<thead>
						<tr>
							<th colspan="5">
								<span class="ordertitle">2017-02-11 11:59 订单编号:7867473872181848 <span
										class="pull-right delete"><img src="../images/delete.png"></span></span>
							</th>
						</tr>
					</thead>
					<tbody>
						<tr>
							<td width="60%">
								<div class="typographic">
									<img src="../images/goods.png">
									<a href="#" class="block-text">包邮 正品玛姬儿压缩面膜无纺布纸膜100粒 送泡瓶面膜刷喷瓶 新款</a>
									<span>x1</span>
									<a href="#" class="service">售后申请</a>
								</div>
							</td>
							<td rowspan="2" width="8%" class="center">小丽</td>
							<td rowspan="2" width="13%" class="center">
								<ul class="unstyled">
									<li>总金额¥138.00</li>
									<li>在线支付</li>

								</ul>
							</td>
							<td rowspan="2" width="8%" class="center">
								<a href="#" class="btn">已完成 </a>
							</td>
							<td rowspan="2" width="13%" class="center">
								<ul class="unstyled">
									<li>
										<a href="mycomment.html" target="_blank">评价|晒单</a>
									</li>

								</ul>
							</td>
						</tr>
						<tr>
							<td width="50%">
								<div class="typographic">
									<img src="../images/goods.png">
									<a href="#" class="block-text">包邮 正品玛姬儿压缩面膜无纺布纸膜100粒 送泡瓶面膜刷喷瓶 新款</a>
									<span>x1</span>
									<a href="#" class="service">售后申请</a>
								</div>
							</td>
						</tr>
					</tbody>
				</table>
			</div>
			<div class="choose-order">
				<div class="pagination">
					<ul>
						<li class="prev disabled">
							<a href="javascript:">«上一页</a>
						</li>
						<li class="page actived">
							<a href="javascript:">1</a>
						</li>
						<li class="page">
							<a href="javascript:">2</a>
						</li>
						<li class="page">
							<a href="javascript:">3</a>
						</li>
						<li class="page">
							<a href="javascript:">4</a>
						</li>

						<li class="next disabled">
							<a href="javascript:">下一页»</a>
						</li>
					</ul>
					<div>
						<span>&nbsp;&nbsp;&nbsp;&nbsp;共2页&nbsp;</span>
					</div>
				</div>
			</div>
		</div>
		<!--猜你喜欢-->
		<div class="like">
			<h4 class="kt">猜你喜欢</h4>
			<ul class="like-list">
				<li class="likeItem">
					<div class="p-img">
						<img src="../images/itemlike01.png" />
					</div>
					<div class="attr">
						<em>DELL戴尔Ins 15MR-7528SS 15英寸 银色 笔记本</em>
					</div>
					<div class="price">
						<em>¥</em>
						<i>3699.00</i>
					</div>
					<div class="commit">已有6人评价
					</div>
				</li>
				<li class="likeItem">
					<div class="p-img">
						<img src="../images/itemlike02.png" />
					</div>
					<div class="attr">
						Apple苹果iPhone 6s/6s Plus 16G 64G 128G
					</div>
					<div class="price">
						<em>¥</em>
						<i>4388.00</i>
					</div>
					<div class="commit">已有700人评价
					</div>
				</li>
				<li class="likeItem">
					<div class="p-img">
						<img src="../images/itemlike03.png" />
					</div>
					<div class="attr">DELL戴尔Ins 15MR-7528SS 15英寸 银色 笔记本
					</div>
					<div class="price">
						<em>¥</em>
						<i>4088.00</i>
					</div>
					<div class="commit">已有700人评价
					</div>
				</li>
				<li class="likeItem">
					<div class="p-img">
						<img src="../images/itemlike04.png" />
					</div>
					<div class="attr">DELL戴尔Ins 15MR-7528SS 15英寸 银色 笔记本
					</div>
					<div class="price">
						<em>¥</em>
						<i>4088.00</i>
					</div>
					<div class="commit">已有700人评价
					</div>
				</li>
			</ul>
		</div>
	</div>
</template>

<script>
	export default {
		name: 'MyOrder'
	}
</script>

<style>
</style>

### groupOrder.index.vue
<template>
	<div>
		我是团购订单的内容
	</div>
</template>

<script>
	export default {
		name:'GroupOrder'
	}
</script>

<style>
</style>
  • Center路由中,注册二级路由
### router.routes.js
......
{
	name:"center",
	path: "/center",
	component: Center,
	......
	children:[ // 注册二级路由
		{
			path: "myorder", // 注意,不要写成'/myorder'
			component: MyOrder,
		},
		{
			path: "grouporder",
			component: GroupOrder,
		},
	]
},
### Center.index.vue
......
<div class="order-body">
	<!--左侧列表-->
	<div class="order-left">
		<dl>
			<dt><i>·</i> 订单中心</dt>
			<dd><!--生成链接-->
				<router-link to="/center/myorder">我的订单</router-link> 
			</dd>
			<dd>
				<router-link to="/center/grouporder">团购订单</router-link>
			</dd>
			<dd>本地生活订单 </dd>
	<!-- 右侧内容 -->
			<!-- <MyOrder></MyOrder> -->
			<!-- <GroupOrder></GroupOrder> -->
			<!--展示内容-->
			<router-view></router-view>
- 注意事项: Center组件的 <style lang="less" > 必须取消scoped,否则'我的订单'没有样式
  • 至此,二级路由组件配置完成,但是有一个问题,当用户访问Center页面的时候,右侧一片空白,用户需点击我的订单才有内容

    所以,当用户访问Center页面的时候,路由应当直接重定向myOrder,即默认就展示我的订单

### router.routes.js
......
{
		name:"center",
		......
		children:[
			{
				path: "myorder",
				component: MyOrder,
			},
			{
				path: "grouporder",
				component: GroupOrder,
			},
			{
				path: "/center",
				redirect: "/center/myorder", // 配置重定向
			},
		]
	},

获取个人中心数据

  • 配置请求,然后挂载组件以后发请求
### api.index.js
......
// 传入page和limie
export const reqMyOrderList = (page,limit)=>requests({url:`/order/auth/${page}/${limit}`,method:'get'})

### myOrder.index.vue
......
<script>
	export default {
		name: 'MyOrder',
		data(){
			return {
				page:1, // 初始化数据
				limit:3,
				myOrder:{}
			}
		},
		mounted(){
			this.getData(); // 封装异步函数
		},
		methods:{
			async getData(){
				const {page,limit} = this
				let res = await this.$API.reqMyOrderList(page,limit)
				// console.log(res)
				if(res.code == 200){
					this.myOrder = res.data
				}
			}
		}
	}
</script>

  • 数据的渲染
### myOrder.index.vue

<template>
	<div class="order-right">
		......
			<div class="orders">
				
				<!--开始渲染-->
				<table class="order-item" v-for="(order,index) in myOrder.records" :key="order.id">
					<thead>
						<tr>
							<th colspan="5">			<!--订单创建时间和订单编号-->
								<span class="ordertitle">{{order.createTime}} 订单编号:{{order.outTradeNo}} <span
										class="pull-right delete"><img src="../images/delete.png"></span></span>
							</th>
						</tr>
					</thead>
					<tbody>
						<!--渲染购物车商品数据-->
						<tr v-for="(cart,index) in order.orderDetailList" :key="cart.id">
							<td width="60%">
								<div class="typographic">
									<!--渲染图片并指定样式-->
									<img :src="cart.imgUrl" style="width: 100px;height: 100px;">
									<a href="#" class="block-text">{{cart.skuName}}</a>
									<span>{{cart.skuNum}}</span>
									<a href="#" class="service">售后申请</a>
								</div>
							</td>
							<!-- <td rowspan="2" width="8%" class="center">{{order.consignee}}</td> -->
							<!--渲染'行数'-->
							<td :rowspan="order.orderDetailList.length" v-if="index==0" width="8%" class="center">{{order.consignee}}</td>
							<td :rowspan="order.orderDetailList.length" v-if="index==0" width="13%" class="center">
								<ul class="unstyled">
									<li>总金额¥{{order.totalAmount}}</li>
									<li>在线支付</li>

								</ul>
							</td>
							<td :rowspan="order.orderDetailList.length" v-if="index==0" width="8%" class="center">
								<a href="#" class="btn">{{order.orderStatusName}} </a>
							</td>
							<td :rowspan="order.orderDetailList.length" v-if="index==0" width="13%" class="center">
								......
							</td>
						</tr>
					</tbody>
				</table>

			</div>
			<div class="choose-order">
				......
			</div>
		</div>
		<!--猜你喜欢-->
		<div class="like">
			......
		</div>
	</div>
</template>
  • 最后,搞定分页(注意:分页已是全局组件,搞过来用即可)
### myOrder.index.vue
......
<div class="choose-order">
	<!--引入全局组件Pagination-->
	<Pagination
		:pageNo="page"
		:pageSize="limit"
		:total="myOrder.total"
		:continues="5"
		@getPageNo="getPageNo" <!--绑定事件-->
	></Pagination>
	
</div>
......
methods:{
	......
	getPageNo(page){ // 获取用户点击的当前页面,再次发请求
		this.page = page
		this.getData()
	}
}
  • JS API补充: 翻译url文本
- decodeURIComponent('=%2Fcenter%2Fmyorder')
>>>'=/center/myorder'

路由独享守卫组件守卫

  • 先搞定登录之后的跳转链接(有链接就跳链接,没链接就跳到/home)
### Login.index.vue
......
methods:{
	async userLogin(){
		try{
			......
			// 优先获取 redirct值进行跳转
			let toPath = this.$route.query.redirect || '/home'
			this.$router.push(toPath)
		}
		......
	}
},
  • 全局守卫作文章: 当未登录用户访问/trade/pay/center的时候,应先到登录页再到目的页
  • 讲白了,先把目的页path存储到url的query参数,适时再获取该参数 并实现跳转
- 思路: 若没有检测到token
	- 获取用户即将访问的url,先跳到'/login'(带着这个url)
	- '/login'页登录的时候优先判断是否有query url,有就跳到 query url,没有就跳到'/home'
### router.index.js
......
router.beforeEach(async (to,from,next)=>{
	......
	if(token){
		......
		}
	}else{
		let toPath = to.path // 获取用户要去的path
		// 如果要去这些页面,先带着toPath跳到'/login'
		if(toPath.indexOf('/trade') != -1 || toPath.indexOf('/pay') != -1 || toPath.indexOf('/center') != -1){
			next('/login?redirect='+toPath) // 带着query跳转
		}else{
			next()
		}
		
	}
	
})
  • 场景分析: 当用户登录以后,可以直接跳到/pay页面么?显然不可以,必须从指定的页面进,才可以跳转

    所以,类似这样的逻辑,我们使用路由独享守卫来解决(本质就一个钩子配置项)

  • 以必须由/shopcart页面跳转到/trade页面为例,来说明问题

### router.routes.js
......
{
	name:"trade",
	......
	beforeEnter:(to,from,next)=>{
		if(from.path == '/shopcart'){ // 只有来自 /shopcart 的 path才放行
			next()
		}else{
			next(false) // 其他path均停留在原地
		}
	}
},

- '/pay'路径是一模一样的写法,只有来自'/trade'的路径才放行(不再演示)
  • 组件内守卫例子(这个例子用路由守卫来做也完全没问题)
### PaySuccess.index.vue
......
<script>
  export default {
    name: 'PaySuccess',
	beforeRouteEnter(to,from,next){
		if(from.path == '/pay'){ // 只允许来自'/pay'的path进来
			next()
		}else{
			next(false)
		}
	}
  }
</script>
  • 小结
- 实战中,全局守卫 和 路由独享守卫 使用较多, 组件内守卫用得比较少

图片的懒加载

  • 什么是懒加载: 当发送请求展示图片的时候,这是一个耗时操作,这个瞬间图片是无法显示的

    解决办法: 先从本地搞一张图片来展示,等图片请求回来了,再展示图片

  • 使用插件```vue-lazyload``来完成

- 安装坑之一: npm i vue-lazyload (不要去安装最新版的,使用的时候会报错)
	- Failed to resolve directive: lazy(懒图片无法加载出来)
	
- 安装旧版 vue-lazyload 即可: npm i vue-lazyload@1 -S 
- assert目录,预先放置一张gif图片,比如 'lazy.gif'

### main.js
......
// 导入插件和图片
import lazyPic from '@/assets/lazy.gif'
import VueLazyload from 'vue-lazyload'

Vue.use(VueLazyload,{ // 使用插件
	loading:lazyPic // 加载默认图片
})

### Seach.index.vue
......
<router-link :to="`/detail/${good.id}`">
	<!-- <img :src="good.defaultImg" /> -->
	<!--把':src'修改为'v-lazy'即可-->
	<img v-lazy="good.defaultImg" />
</router-link>


自定义插件演示

  • 必须实现install方法
### plugins.myPlugins.js

let myPlugins = {};
myPlugins.install = function(){
	console.log('自定义插件被调用了')
}

export default myPlugins;

### main.js
......
// 导入插件并使用
import myPlugins from '@/plugins/myPlugins'
Vue.use(myPlugins)

- 随便开一个页面,测试正常
  • 现在,自己写一个插件,把字母转换成大写
### plugins.myPlugins.js

let myPlugins = {};
myPlugins.install = function(Vue,options){
	
	// 注册v-xxx指令
	Vue.directive(options.name,(element,params)=>{
		element.innerHTML = params.value.toUpperCase();
	})
}

export default myPlugins;

### main.js
......
// 导入并使用,注意要指定name配置项
import myPlugins from '@/plugins/myPlugins'
Vue.use(myPlugins,{
	name:'upper'
})

### App.vue 使用(别的组件里面使用也可以的,这里随便测试一下)
......
<template>
  <div id="app">
	<h1 v-upper='msg'></h1> <!--运用-->
	......
</template>
......
<script type="text/javascript">
	......
	export default {
		name:'App',
		......
		data(){
			return {
				msg:'abc' // 初始化数据
			}
		},
		......
	}
</script>
  • 小结: 其实平常使用v-xxx指令,本质就是一个vue插件
- 插件就是指令,指令就是插件
posted @ 2023-06-27 11:26  清安宁  阅读(122)  评论(0)    收藏  举报