一、分页静态组件(Pagination)
掌握自定义分页系统
1.分页器所需数据
(1)当前是第几页pageNo
(2)每页展示的数据条数pageSize
(3)一共有多少页total
(4)分页器连续页码个数pageCount
2.自定义分页器
先用模拟数据调试
(1)Pagination组件接收父组件的数据,computed计算出分页所需数据
(2)计算连续页面的起始数字和结束数字,连续的页码数必须有5个
3.页码的起始数字与结束数字在计算时可能出现的情况
特殊情况:总页数不足,即总页数小于连续的页码个数时,start=1,end=totalPage
正常情况下,计算出显示的连续数字。
但是,如果当前页是第一页(因为当前页码太小,计算出的start小于1),计算可能得到负数,此时要纠正负数。则将start=1,end=pageCount(连续页码数)
end大于totalPage,则将end=totalPage,start=totoalPage-pageCount+1
4.分页器动态展示
(1)数字1的展示,当start大于1的时候显示
(2)…的展示,当start>2的时候展示
(3)中间数字,遍历pageCount(连续页码数,数字,1-这个数字),使用v-if选择显示,只有当遍历的数字大于start时才会显示该数字
(4)…当end<totalPage-1时显示
(5)结尾数字的展示,当end<totalPage时展示
5.分页器添加类名,添加当前页的样式
6.滚动行为
二、产品详情页
详情数据获取
(1)当点击商品图片,使用声明式导航,携带商品id跳转至商品详情页
<div class="p-img">
<!--商品的图片:需要路由跳转的时候,携带商品的ID-->
<router-link :to=" `/detail/${good.id}` ">
<img :src="good.defaultImg" /></router-link>
</div>
(2)在商品详情页面时,滚轮在顶部。
//第二步:暴露VueRouter类的实例
//对外暴露一个路由器,实质是VueRouter类的实例,一个路由器可以管理多个路由
const router = new VueRouter({
//配置路由
routes,
//设置滚动条的位置
scrollBehavior() {
//滚动行为这个函数,需要有返回值,返回值为一个对象。
//经常可以设置滚动条x|y位置 [x|y数值的设置一般最小是零]
return { y: 0 };
}
});
(3)api请求接口
(4)添加detail仓库(action mutation state)
(5)在index中引入、注册detail仓库
(6) 点击图片后,在Detail组件挂载完毕后,dispatch,获取产品详情信息
(7)仓库中进行操作,将信息存至detail中
1.产品详情展示动态
Deatail详情页:Detail中的结构,两个子组件
从vuex中获取数据,在仓库中的detail,返回数据时可以如下 防止浏览器报假错
categoryView(state) {
//研究这个问题:
//起始状态:state.detailInfo起始状态空对象,空对象.categoryView->undefined
//当服务器数据回来之后state.detailInfo,并非空对象,获取的即为服务器返回的数据{7个K}
//当前属性值:服务器的数据有值,用服务器的。服务器数据没有回来至少有一个空对象兜底【不能undefined兜底】
return state.detailInfo.categoryView || {}
},
展示产品的名称、详情、价格
2.放大镜Zoom与兄弟组件ImageList(放大镜下边的滑块图片)
(1)兄弟组件通信,点击滑块中的图片,通过兄弟组件切换图片
ImageList
<div
class="swiper-slide"
v-for="(slide, index) in skuInfo.skuImageList"
:key="index"
>
<img
:src="slide.imgUrl"
:class="{ active: currentIndex == index }"
@click="handler(index)"
/>
</div>
//小图的点击事件
handler(index) {
//修改响应式数据存储当前用户点击的索引值
this.currentIndex = index;
//全局事件总线,通知兄弟当前图片的索引值
this.$bus.$emit("sendIndex", index);
},
Zoom
mounted() {
//接受兄弟组件传递过来的索引值
this.$bus.$on('sendIndex', (index) => {
this.index = index;
})
}
放大镜功能的具体实现
handler(e) {
// 放大镜 鼠标始终在放大镜中心
// $ref获取的是dom对象
//获取蒙板
let mask = this.$refs.mask;
let big = this.$refs.big;
//计算蒙板的left|top数值 鼠标的水平位置 距离父盒子左边的值-自身蒙版宽度的一般
let l = e.offsetX - mask.offsetWidth / 2;
let t = e.offsetY - mask.offsetHeight / 2;
//约束蒙板的上下左右范围 防止超出父盒子 盒子宽度为两个蒙版的宽度 高度也是
if (l < 0) l = 0;
if (l > mask.offsetWidth) l = mask.offsetWidth;
if (t < 0) t = 0;
if (t > mask.offsetHeight) t = mask.offsetHeight;
mask.style.left = l + "px";
mask.style.top = t + "px";
// 设置放大后的图片的位置
big.style.left = -2 * l + "px";
big.style.top = -2 * t + "px";
},
3.展示商品售卖属性
(1)展示商品售卖属性值
//商品销售属性列表的数据
spuSaleAttrList() {
return state.detailInfo.spuSaleAttrList || []
}
选中属性的高亮显示
(2)商品售卖属性值排他操作
点谁谁亮,不点不亮
<!--每一个销售属性的属性值的地方-->
<dd
changepirce="0"
:class="{ active: saleAttrValue.isChecked == 1 }"
v-for="(
saleAttrValue
) in saleAttr.spuSaleAttrValueList"
:key="saleAttrValue.id"
@click="
changeChecked(saleAttrValue, saleAttr.spuSaleAttrValueList)
"
>
//第一个参数是当前点击的对象
//将所有的对象取消高亮操作,给当前点击的对象设置高亮,此时实现了排他操作
changeChecked(saleAttrValue, arr) {
console.log(this.skuInfo);
//响应式数据:对象、数组
//数组的响应式数据:变更、替换【基本类型数据、引用类型对象响应式的】
//数组里面是基本类型数据:替换、变更 如果对象,不管你怎么玩都是相应的!!!!
//排他操作
//底下的代码:修改数组里面的对象【相应的式的】,数据变化视图跟这变化!!!
arr.forEach((item) => {
item.isChecked = "0";
});
saleAttrValue.isChecked = "1";
},
4.购买产品个数
(1)通过加减按钮修改产品个数
<!-- 购物商品个数的操作地方 -->
<div class="controls">
<input
autocomplete="off"
class="itxt"
v-model="skuNum"
@change="handler"
/>
<a href="javascript:" class="plus" @click="skuNum++">+</a>
<a
href="javascript:"
class="mins"
@click="skuNum > 1 ? skuNum-- : 1"
>-</a
>
</div>
(2)通过用户输入改变产品个数
input框的change事件
//数量的表单元素的change回调
handler(e) {
//通过event事件对象获取用户输入内容[用户输入的内容一定是字符串类型的数据]
//包含非数字的字符串*1一定等于NaN
let value = e.target.value * 1;
//用户输入进来非法情况判断 判断特殊情况 是否为非数值 是否小于1
if (isNaN(value) || value < 1) {
this.skuNum = 1;
} else {
//正常情况 取整
this.skuNum = parseInt(value);
}
},
5.加入购物车
加入购物车的回调函数
1.发请求,将商品加入到数据库(通知服务器)
2.服务器存储成功(服务器没有返回数据,只有code=200代表此次操作成功)—进行路由跳转传递参数
3.失败,提示用户
4.将产品信息进行会话存储,以字符串的形式进行存储(本地存储不能存储对象)
5.路由跳转,展示商品信息,加入成功提示
<div class="add">
<!--点击加入购物车按钮:不能用声明式导航,第一个:要发请求(有业务),将购买的产品信息通知服务器存储-->
<a @click="addOrUpdateCart">加入购物车</a>
</div>
//加入购物车或者更新购物车中的产品个数
async addOrUpdateCart() {
//派发action:携带的载荷,分别商品的id、商品个数
//实质就是调用了小仓库里面相应的这个函数->addOrUpdateCart,声明部分加上asyc,这个函数执行的结构一定是Promise
//返回结果是一个Promise对象【三种状态:pending、成功、失败】,返回状态到底是什么,取决于这个函数addOrUpdateCart返回结果
//判断加入购物车是成功还是失败
//try是成功
try {
// 调用仓库中的函数
//await是成功
await this.$store.dispatch("addOrUpdateCart", {
//产品id产品数量
skuId: this.$route.params.skuId,
skuNum: this.skuNum,
});
//路由跳转:携带参数,携带参数一般都是基本类型数据【字符串、数字等等】,引用类型数据白扯【传递过来路由获取不到】!!!
//浏览器存储功能,在路由跳转在之前,存储到浏览器中
//会话存储 会话结束就消失
sessionStorage.setItem('SKUINFO',JSON.stringify(this.skuInfo));
//路由跳转 携带加入购物车的产品信息跳转至另一个组件
this.$router.push({
path: "/addcartsuccess",
query: { skuNum: this.skuNum},
});
} catch (error) {
//失败干什么
alert("加入购物车失败");
}
},
store中的detail.js
//加入购物车|将来修改商品个数的地方,右侧是载荷对象【两个K,两个V】
//加上async 返回的一定是Promise 要么成功 要么失败
async addOrUpdateCart({ state, commit, dispatch }, { skuId, skuNum }) {
//向服务器发送请求,在服务器存储数据,result为服务器的返回结果
//请求api接口 获取返回结果
let result = await reqAddOrUpdateCart(skuId, skuNum);
//如果加入购物车成功,返回promise即为成功
if (result.code == 200) {
//非空字符串代表成功
return "ok";
} else {
//如果加入购物车失败,返回失败的Promise
return Promise.reject();
}
}
三、路由传递参数结合会话存储
1.将商品信息存入本地会话存储,商品数量通过路由跳转,使用query参数进行传递