Vue(小案例_vue+axios仿手机app)_Vuex优化购物车功能
一、前言
1、用vuex实现加入购物车操作
2、购物车详情页面
3、点击删除按钮,删除购物详情页面里的对应商品
二、主要内容
1、用vuex加入购物车
(1)在src目录下创建store.js,
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
//vuex五大将
state:{
num:1//小球的数量默认为1
}, getters:{
getShopNum(state){
return state.num;
}
}, mutations:{
addShopNum(state,num){//增加小球数量
state.num +=num;
}, changeShopNum(state,num){//改变小球数量
state.num = num;
}
}, actions:{
addShopNumByAction({commit},num){
commit('addShopNum',num);
}, changeShopNum({commit}, num){
commit('changeShopNum',num)
}
} })
(2)在main.js入口文件中挂载,并且导入
import store from './store.js' /* eslint-disableo-new */
new Vue({
el: '#app',
router,
store,//一定要导入
components: { App },
template: '<App/>'
})
(3)在app.vue(底部导航组件)中用computed监听这个pickNum
computed:{
pickNum(){
return this.$store.getters.getShopNum
}
}
(4)在点击“加入购物车”那个组件,
afterEnter(){
this.isExist=false; //显示出来之后执行这个,又将小球隐藏 /* 不用这个$bus
this.$bus.$emit('sendPickNum',this.pickNum);*/ //用vuex, 触发action,
this.$store.dispatch('addShopNumByAction',this.pickNum);
//当触发了上面的事件之后,
GoodsTool.add({
id:this.goodsInfo.id,
num:this.$store.getters.getShopNum
})
},
2、购物车详情页面(上面点击+++,下面也要变化)
(1)在购物车详情页面,每次点加,减的时候让他去触发action
methods:{
addNum(shop){//每次点击都接受到当前的对象
shop.num++; //这里的值虽然加上了,但是,数据并没有响应上去,是因为created是一开始就加载的,后来点击修改了num的值,但是没有 响应视图
this.$store.dispatch('addShopNumByAction',1)//触发action
console.log(shop)
},
substract(shop){
if(shop.num==1){
return;
} shop.num--;
this.$store.dispatch('addShopNumByAction',-1)//触发action }
}
(2)要让底部导航栏里面的数量随着点击而发生变化
created(){
//当你的组件一创建好了后就挂载这个bus公交车,$on这个事件监听
/* this.$bus.$on(`sendPickNum`, (data)=>{
this.pickNum=this.pickNum + data;
}), this.pickNum=GoodsTool.getTotalCount();*/
//触发action里面的changShop方法,并且将当前的总数量传给他
this.$store.dispatch('changeShopNum',GoodsTool.getTotalCount()) }
3、点击删除按钮,删除购物详情页面里的对应商品
del(shop,index){//将当前的对象,和index传进来
this.shopCart.splice(index,1)//数组中的当前对象
delete GoodsTool[shop.id]
GoodsTool.removeGoods(shop.id) let num = shop.um;
this.$store.dispatch('addShopNumByAction',-num) }
4.通信的组件如下
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state:{
num:1//小球的数量默认为1
}, getters:{
getShopNum(state){
return state.num;
}
}, mutations:{
addShopNum(state,num){
state +=num;
}, changeShopNum(state,num){
state.num = num;
}
}, actions:{
addShopNumByAction({commit},num){
commit('addShopNum',num);
}, changeShopNum({commit}, num){
commit('changeShopNum',num)
}
} })
store.js
let obj={}; //这里需要存储数据
//{商品的id, 商品的数量} //保存商品
obj.saveGoods = function(goodsList){
window.localStorage.setItem('goodsList',JSON.stringify(goodsList))
} //获取商品的值,没有值传一个空对象 obj.getGoodsList = function(){
return JSON.parse(window.localStorage.getItem('goodsList'|| '{}'))
} //增加商品
obj.add = function(goods){
let goodsList = this.getGoodsList()//获取到storage里面的对象
if(goodsList[goods.id]){ //goods.id是商品的数量,对应有值的话就追加
goodsList[goods.id] = goodsList[goods.id] + goods.num;
}else{
goodsList[goods.id]=goods.num;
} //传进来之后还需要保存
this.saveGoods(goodsList);
} //获取购物车的总数量
obj.getTotalCount = function(){
let goodsList = this.getGoodsList();
let values = Object.values(goodsList);//Object.values返回的是一个数组,里面对应着每一个key的value
let sum = 0;
values.forEach(val => sum = sum +val);
return sum; } //删除
obj.removeGoods=function(id){
let goodsList = this.getGoodsList();
delete goodsList[id];
return this.saveGoods(goodsList)
}
export default obj;
GoodsTool.js
<template>
<div>
<go-back-header title="商品详情"></go-back-header>
<div class="outer-swiper">
<div class="swiper">
我真的是轮播图
</div>
</div>
<div class="product-desc">
<ul>
<li><span class="product-desc-span">
商品标题
</span></li>
<li class="price-li">市场价:
<s>¥{{goodsInfo.market_price}}</s> 销售价:<span>¥{{goodsInfo.sell_price}}</span></li>
<li class="number-li">购买数量:<span @click='substract'>-</span><span>{{pickNum}}</span><span @click='add'>+</span></li>
<li>
<mt-button type="primary">立即购买</mt-button>
<mt-button type="danger" size='small' @click='ballHandler'>加入购物车</mt-button>
</li>
</ul>
</div>
<transition name='ball' @after-enter='afterEnter'>
<div class="ball" v-if="isExist"></div>
</transition>
<!--<div class="ball" v-if='isExist'></div>-->
<div class="product-props">
<ul>
<li>商品参数</li>
<li>商品货号:{{goodsInfo.goods_no}}</li>
<li>库存情况:{{goodsInfo.stock_quantity}}件</li>
<li>上架时间:{{goodsInfo.add_time}}</li>
</ul>
</div>
<div class="product-info">
<ul>
<li>
<mt-button type="primary" size="large" plain @click.native='showShopInfo()'>图文介绍</mt-button>
</li>
<li>
<mt-button type="danger" size="large" plain @click.native=''>商品评论</mt-button>
</li>
</ul>
</div>
</div>
</template>
<script>
import GoodsTool from './GoodsTool.js'
export default{
name:'GoodsDetail',
data(){
return{
url:`getthumImages/${this.$route.params.id}`,
goodsInfo:{},//当前购物车的信息,里面有id
pickNum:1 ,
isExist:false //让小球默认是隐藏的状态,
}
}, methods:{ afterEnter(){
this.isExist=false; //显示出来之后执行这个,又将小球隐藏 /* 不用这个$bus
this.$bus.$emit('sendPickNum',this.pickNum);*/ //用vuex, 触发action,
this.$store.dispatch('addShopNumByAction',this.pickNum);
//当触发了上面的事件之后,
GoodsTool.add({
id:this.goodsInfo.id,
num:this.$store.getters.getShopNum
})
}, //点击加入购物车执行这个方法,然后让小球显示出来
ballHandler(){ this.isExist=true;
// this.$bus.$emit('sendPickNum',this.pickNum); //将当前的pickNum传过去,但是这个不能加在这里,否者一点击“加入购物车就传
}, add(){
//如果当前的数小于库存数,就让他做加
if(this.pickNum < this.goodsInfo.stock_quantity){
this.pickNum++;
} }, substract(){
if(this.pickNum ===1){ //减法的时候最少为1,当此时值为1的时候不做操作
return;
} this.pickNum--;
}, showShopInfo(){ //通过动态路由进行路由跳转
this.$router.push({
name:"photo.info",
query:{
id:this.$route.params.id
}
})
}, shopComment(){
this.$router.push({
name:'good.comment',
query:{
id:this.$route.params.id
}
})
} }, created(){
//每个商品都只有一个对应的id,
this.$axios.get(`goods/getinfo${this.$route.params.id}`)
.then(res=>{
this.goodsInfo = res.data.message[0]
})
.catch(err=>{
console.log('商品详情异常',err)
}); this.pickNum = GoodsTool.getTotalCount();//获取购物的所有总数 }
}
</script>
<style scoped>
.ball-enter-active {
/*给1s的时间让小球进入动画效果*/
animation: bounce-in 1s;
} .bass-leave{
/*元素进入以后,透明度0,整个动画都是0*/
/*元素离开默认是1,所以会闪一下,设置为0*/
opacity: 0; } @keyframes bounce-in {
0% {
transform: translate3d(0, 0, 0);
}
50% {
transform: translate3d(140px, -50px, 0);
}
75% {
transform: translate3d(160px, 0px, 0);
}
100% {
transform: translate3d(140px, 300px, 0);
}
} .swiper {
border: 1px solid rgba(0, 0, 0, 0.2);
margin: 8px;
width: 95%;
border-radius: 15px;
overflow: hidden;
} .outer-swiper,
.product-desc,
.product-props,
.product-info {
border-radius: 5px;
border: 1px solid rgba(0, 0, 0, 0.2);
margin: 3px;
} /*请ulpadding*/ .outer-swiper ul,
.product-desc ul,
.product-props ul,
.product-info ul {
padding: 0;
} .product-desc ul li,
.product-props ul li,
.product-info ul li {
list-style: none;
font-size: 15px;
color: rgba(0, 0, 0, 0.5);
margin-top: 8px;
} .product-desc ul >:nth-child(1) span {
color: blue;
font-size: 22px;
font-weight: bold;
} .product-desc ul >:nth-child(1) {
border-bottom: 1px solid rgba(0, 0, 0, 0.5);
} .product-desc ul,
.product-info ul,
.product-props ul {
padding-left: 10px;
} .price-li span {
color: red;
font-size: 25px;
} .price-li s {
margin-right: 16px;
} /*加减*/ .number-li span {
display: inline-block;
border: 2px solid rgba(0, 0, 0, 0.1);
width: 25px;
} /*商品参数*/ .product-props ul >:nth-child(1) {
text-align: left;
} .product-props ul:not(:nth-child(1)) {
margin-left: 40px;
} .product-info ul {
margin-bottom: 70px;
padding: 0 5;
} .number-li span {
text-align: center;
} .number-li >:nth-child(2) {
width: 40px;
} .ball {
border-radius: 50%;
background-color: red;
width: 24px;
height: 24px;
position: absolute;
top: 440px;
left: 120px;
display: inline-block;
z-index: 9999;
}
</style>
GoodsDetail.vue
<template>
<div id='app'>
<!--顶部-->
<mt-header title="信息管理系统" fixed>
<router-link to="/" slot="left">
<mt-button icon="back">返回</mt-button>
</router-link>
<mt-button icon="more" slot="right"></mt-button>
</mt-header> <!--底部-->
<div class="tabBar">
<ul>
<li v-for="(tab, index) in tabs">
<router-link :to="tab.routerName">
<img :src="tab.imgSrc">
<!--小球-->
<mt-badge size='small' color="#FC0107" v-if='index===2'>{{pickNum}}</mt-badge>
<p>{{tab.title}}</p>
</router-link>
</li>
</ul> </div>
<router-view></router-view> </div> </template> <script>
import index from './assets/index.png'
import vip from './assets/vip.png'
import shopcart from './assets/shopcart.png'
import search from './assets/search.png' let tabs = [
{id:1, title:"首页", imgSrc:index, routerName:{name:'home'}},
{id:2, title:"会员", imgSrc:vip, routerName:{name:'vip'}},
{id:3, title:"购物车", imgSrc:shopcart, routerName:{name:'cart'}},
{id:4, title:"查找", imgSrc:search, routerName:{name:'search'}} ] import GoodsTool from './GoodsTool.js'
export default { name: 'App',
data(){
return { tabs:tabs,
//pickNum:0,//底部栏小球 }
}, watch:{
selected:function(newV,oldV){ console.log(newV);
console.log(oldV);
console.log(this.selected);//id绑定的id
this.$router.push({name:this.selected}); }, computed:{
pickNum(){
return this.$store.getters.getShopNum
}
} created(){
//当你的组件一创建好了后就挂载这个bus公交车,$on这个事件监听
/* this.$bus.$on(`sendPickNum`, (data)=>{
this.pickNum=this.pickNum + data;
}), this.pickNum=GoodsTool.getTotalCount();*/
this.$store.dispatch('changeShopNum',GoodsTool.getTotalCount()) }
}
}
</script> <style scoped>
.tabBar{
width: 100%;
height: 55px;
background-color: #ccc;
position: absolute;
bottom: 0;
left: 0;
background-image: linear-gradient(180deg, #d9d9d9, #d9d9d9 50%, transparent 50%);
background-size: 100% 1px;
background-repeat: no-repeat;/*做一像素渐变线*/
background-position: top left;
background-color: #fafafa;
} .tabBar ul{
width: 100%;
overflow: hidden;
} .tabBar ul li{
float: left;
width: 25%;
height: 55px;
text-align: center;
} .tabBar ul li a{
display: inline-block;
width: 100%;
height: 100%;
padding-top: 10px; }
.tabBar ul li a.link-active{
background-color: pink;
position: relative;
}
.tabBar ul li a img{
width: 25px;
height: 25px;
}
.tabBar ul li a p{
font-size: 12px;
} /*重写一下小球的颜色*/
.mint-bage.is-size-small{
position: absolute;
top: 0;
right: 10px;
}
</style>
App.vue
<template>
<div>
<div class="pay-detail">
<ul>
<li class="p-list" v-for="(shop, index) in shopCart">
<mt-switch></mt-switch>
<img src="">
<div class="pay-calc">
<p>{{shop.title}}</p>
<div class="calc">
<span>¥777</span>
<span @click="substract(shop)">-</span>
<span>{{shop.num}}</span>
<span @click="addNum(shop)">+</span>
<a href="javascript:;">删除</a>
</div>
</div>
</li>
</ul>
</div>
<div class="show-price">
<div class="show-1">
<p>总计(不含运费):</p>
<span>已经选择商品1件,总价¥888元</span>
</div>
<div class="show-2">
<mt-button type="danger" size="large">去结算</mt-button>
</div>
</div>
</div>
</template>
<script>
import GoodTool from '@/GoodsTool.js'
export default {
name:'Cart',
data(){
return{
shopCart:[] }
},
methods:{
addNum(shop){//每次点击都接受到当前的对象
shop.num++; //这里的值虽然加上了,但是,数据并没有响应上去,是因为created是一开始就加载的,后来点击修改了num的值,但是没有 响应视图
this.$store.dispatch('addShopNumByAction',1)
console.log(shop)
},
substract(shop){
if(shop.num==1){
return;
} shop.num--;
this.$store.dispatch('addShopNumByAction',-1) }, del(shop,index){//将当前的对象,和index传进来
this.shopCart.splice(index,1)//数组中的当前对象
delete GoodsTool[shop.id]
GoodsTool.removeGoods(shop.id) let num = shop.um;
this.$store.dispatch('addShopNumByAction',-num) } },
computed:{
payment(){
let total = 0;//定义总金额
let count =0;//定义总数量 this.shopCart.forEach((shop)=>{
if(shop.isSelected){
count = count+shop.num;
total = total + shop.num * shop.sell_price;
}
}) return {
total, count
}
}
}, created(){
let goodsList = GoodsTool.getGoodsList();//{"88":5,"99":4} 第一个数商品id 第二个是商品数量
let ids = Object.key(goodsList).join(',');//Object.key()获取key值 [88,99]
if(ids){
this.$axios.get(`goods/getshopcarlist${ids}`)
.then(res=>{ this.shopCart=res.data.message;//返回的是一个数组
//给数组元素添加属性
for(var i=0; i<this.shopCart.length;i++){
let shop=this.shopCart[i];//获取到当前的对象
let num = goodsList[shop.id];//根据当前对象的id查找到对应的购物车数量 if(num){
shop.num = num; this.$set(shop, 'num', num);//自己给这个数据进行双向数据绑定
this.$set(shop,'isSelected',true); } }
})
}
}
}
</script>
<style scoped>
.pay-detail ul li {
list-style: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
margin-bottom: 3px;
} .pay-detail ul {
padding-left: 5px;
margin-top: 5px;
} .pay-detail ul li img {
width: 80px;
height: 80px;
display: inline-block;
vertical-align: top;
margin-top: 10px;
} .pay-detail ul li >:nth-child(1),
.pay-detail ul li >:nth-child(3) {
display: inline-block;
} .pay-calc p {
display: inline-block;
width: 250px;
overflow: hidden;
color: blue;
font-size: 15px;
margin-bottom: 10px;
} .pay-detail ul li >:nth-child(1) {
line-height: 80px;
} .calc:nth-child(1) {
color: red;
font-size: 20px;
} .calc span:not(:nth-child(1)) {
border: 1px solid rgba(0, 0, 0, 0.3);
display: inline-block;
width: 20px;
text-align: center;
} .calc a {
margin-left: 20px;
} .show-1,
.show-2 {
display: inline-block;
} .show-1,
.show-2 {
margin-left: 30px;
} .show-price {
background-color: rgba(0, 0, 0, 0.2);
}
</style>
Cart.vue
三、总结
Vue(小案例_vue+axios仿手机app)_Vuex优化购物车功能的更多相关文章
- Vue(小案例_vue+axios仿手机app)_购物车(二模拟淘宝购物车页面,点击加减做出相应变化)
一.前言 在上篇购物车中,如果用户刷新了当前的页面,底部导航中的数据又会恢复为原来的: 1.解决刷新,购物车上数值不变 ...
- Vue(小案例_vue+axios仿手机app)_购物车
一.前言 1.购物车 二.主要内容 1.效果演示如下,当我们选择商品数量改变的时候,也要让购物车里面的数据改变 2.具体实现 (1)小球从上面跳到下面的效果 (2)当点击上面的“加入购物车按钮”让小球 ...
- Vue(小案例_vue+axios仿手机app)_公共组件(路由组件传参)
一.前言 1.公共轮播图的实现 2.组件传参,公共组件的实现 二.主要内容 1.公共轮播图的实现 (1)分析:当渲染不同的轮 ...
- Vue(小案例_vue+axios仿手机app)_上拉加载
---恢复内容开始--- 一.前言 ...
- Vue(小案例_vue+axios仿手机app)_实现用户评论
一.前言 1.渲染评论列表 2.点击加载按钮,加载更多 3.提交评论 二.主要内容 1.评论列表一般是注册到一个全局的公共组件中 2.请求后台数据,渲染评论列表 (1)数据格式如下 地址 /ap ...
- Vue(小案例_vue+axios仿手机app)_图片列表操作
一.前言 1.让图片还没有被完全加载出来的时候给用户提示 2.图片查看器 二.主要内容 1.让图片还没有被完全加载出来的时候 ...
- Vue(小案例_vue+axios仿手机app)_go实现退回上一个路由
一.前言 this.$router.go(-1)返回上级路由 二.主要内容 1.小功能演示: 2.组件之间的嵌套关系为: 3.具体实现 (1)由于这种返回按钮在每个页面中的结构都是一样的,只是里面的数 ...
- Vue(小案例_vue+axios仿手机app)_首页(底部导航栏+轮播图+九宫格)
---恢复内容开始--- 一.前言 1.底部导航(两种做法) 2.轮播图 ...
- Vue(小案例_vue+axios仿手机app)_图文列表实现
一.前言 1.导航滑动实现 2.渲染列表信息时让一开始加载的时候就请求数据 3.根据路由的改变,加载图文的改变(实现用户访问网站时可能执行的三个操作) 二.主要内容 1.导航滑动实现: (1)演示 ...
随机推荐
- Dynamics 365-关于Solution的那些事(三)
这一篇的内容,是关于Solution的使用建议的,如果大家有什么实用的建议,欢迎留言讨论. 一. 版本控制 Solution是有版本号的,率性的人可能在新建一个solution的时候,直接赋值1.0, ...
- Webpack4教程 - 第三部分,如何使用插件
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.原文出处:https://wanago.io/2018/07/23/webpack-4-course-part ...
- Numpy库的学习(三)
今天我们继续学习一下Numpy库的学习 废话不多说 ,开始讲 比如我们现在想创建一个0-14这样一个15位的数组 可以直接写,但是很麻烦,Numpy中就给我们了一个方便创建的方法 numpy中有一个a ...
- 【English】十二、英语句子种类,陈述句、疑问句、祈使句、感叹句
一.英语句子按照用途可以分为4类 种类.用途.例句 陈述句 用于说明事实或说话人的看法(事实不就是别人发起并被同意的看法) My name is Jennt Green. I like him bec ...
- 轻松学习UML之类图,状态图
本文主要讲解UML图中的类图与状态图相关内容,如有不足之处,还请指正. 概述 统一建模语言(UML,UnifiedModelingLanguage)是面向对象软件的标准化建模语言,UML因其简单.统一 ...
- SQL大全基本语法
一.基础 1.说明:创建数据库 CREATE DATABASE database-name 2.说明:删除数据库 drop database dbname 3.说明:备份sql server --- ...
- ZooKeeper 之快速入门
-----------------破镜重圆,坚持不懈! 1. 概述 Zookeeper是Hadoop的一个子项目,它是分布式系统中的协调系统,可提供的服务主要有:配置服务.名字服务.分布式同步.组服务 ...
- Neutron Vlan Network 学习
vlan network 是带 tag 的网络,是实际应用最广泛的网络类型. 下图是 vlan100 网络的示例. 1. 三个 instance 通过 TAP 设备连接到名为 brqXXXX ...
- 通过keepalived实现多主集群方案
一. 环境说明:1.服务器列表:proxy01: eth0: 192.168.56.11 eth2: 192.168.156.11 proxy02: eth0: 192.168.56.12 eth2: ...
- h5手机查看
1.装个node:2.全局装个anywhere的npm包.(npm i -g anywhere)3.大功告成,现在到任意目录下用命令行执行anywhere就可以:(-p 参数可以设置启动端口) 补充: ...