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> 共2页 </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插件
- 插件就是指令,指令就是插件

浙公网安备 33010602011771号