vue+node 購物車列表功能實現
二.路由
購物車列表頁面的路由在src/router/index.js裡定義
{ path: '/cart', name: 'Cart', component:Cart },
引入這個cart 這個元件 import Cart from '@/views/Cart'
一.元件的複用
1.要先引入元件
import NavHeader from './../components/NavHeader'
2.要註冊這個元件
components:{ NavHeader, NavFooter, NavBread, Modal },
3.要在template裡引入這個元件名
<template> <div> <nav-header></nav-header>
</template>
接下來要寫server端的cart介面,在server/routes/users.js裡書寫
鍵盤事件的書寫 @keyup.enter="login"
一、渲染購物車列表頁面
新建src/views/Cart.vue
獲取cartList購物車列表資料就可以在頁面中渲染出該使用者的購物車列表資料
-
data(){
-
return {
-
cartList:[] // 購物車商品列表
-
}
-
},
-
mounted:function(){
-
this.init();
-
},
-
methods:{
-
init(){ // 初始化商品資料
-
axios.get('/users/cartList').then((response)=>{
-
let res = response.data;
-
this.cartList = res.result;
-
})
-
}
-
}
購物車介面:server/routes/users.js
-
// 查詢當前使用者的購物車資料
-
router.get('/cartList',function(req,res,next){
-
var userId = req.cookies.userId;
-
User.findOne({userId:userId},function(err,doc){
-
if(err){
-
res.json({
-
status:'1',
-
msg:err.message,
-
result:''
-
});
-
}else{
-
if(doc){
-
res.json({
-
status:'0',
-
msg:'',
-
result:doc.cartList
-
})
-
}
-
}
-
})
-
})
建立路由src/router/index.js
-
import Cart from '@/views/Cart' // 購物車列表
-
export default new Router({
-
routes: [
-
{
-
path: '/cart', // 購物車列表路由
-
name: 'Cart',
-
component: Cart
-
}
-
]
-
})
二、購物車商品刪除功能
購物車刪除介面:server/routes/users.js
-
// 購物車刪除功能
-
router.post('/cartDel',function(req,res,next){
-
var userId = req.cookies.userId,productId = req.body.productId;
-
User.update({
-
userId:userId
-
},{
-
$pull:{
-
'cartList':{
-
'productId':productId
-
}
-
}
-
},function(err,doc){
-
if(err){
-
res.json({
-
status:'1',
-
msg:err.message,
-
result:''
-
});
-
}else{
-
res.json({
-
status:'0',
-
msg:'',
-
result:'suc'
-
});
-
}
-
})
-
});
src/views/Cart.vue
-
點選刪除圖示模態框出現(匯入模態Modal.vue子元件)
-
<!-- 刪除圖示 -->
-
<a href="javascript:;" class="item-edit-btn" @click="delCartConfirm(item.productId)">
-
<svg class="icon icon-del">
-
<use xlink:href="#icon-del"></use>
-
</svg>
-
</a>
-
<!-- 模態框 -->
-
<Modal :mdShow="modalConfirm" @close="closeModal">
-
<p slot="message">你確認要刪除此條資料嗎?</p>
-
<div slot="btnGroup">
-
<a href="javascript:;" class="btn btn--m" @click="delCart">確認</a>
-
<a href="javascript:;" class="btn btn--m" @click="modalConfirm = false">關閉</a>
-
</div>
-
</Modal>
-
import Modal from '@/components/Modal.vue' // 模態框
-
export default {
-
data(){
-
return {
-
productId:'',
-
modalConfirm:false // 模態框是否顯示
-
}
-
},
-
components:{
-
Modal
-
},
-
methods:{
-
delCartConfirm(productId){ // 點選刪除圖示
-
this.productId = productId;
-
this.modalConfirm = true; // 模態框顯示
-
},
-
closeModal(){ // 關閉模態框
-
this.modalConfirm = false;
-
},
-
delCart(){ // 確認刪除此商品
-
axios.post('/users/cartDel',{
-
productId:this.productId
-
}).then((response) => {
-
let res = response.data;
-
if(res.status = '0'){
-
this.modalConfirm = false; // 關閉模態框
-
this.init(); // 重新初始化購物車資料
-
}
-
})
-
}
-
}
-
}
**** 在這裡發現一個bug,在商品列表頁點選"加入購物車",購物車頁面新新增的商品數量和總價格是未定義。mongoose新增屬性問題
這是後端介面處理的問題,在server/routes/goods.js的加入到購物車介面中,是從mongodb的資料庫dumall的goods表根據商品id獲取對應資料,再對此商品資料新增productNum和checked屬性,之後再插入到users表的購物車列表中的。
屬性沒有新增成功,在Goods模型中新增屬性,要去models/goods.js的Schema新增這兩個屬性。
-
server/models/goods.js
-
// 定義一個Schema
-
var produtSchema = new Schema({
-
'productId':String,
-
'productName':String,
-
'salePrice':Number,
-
'productImage':String,
-
// 新增的屬性
-
"checked":String,
-
"productNum":Number
-
})
-
module.exports = mongoose.model('good',produtSchema);
重新啟動express(node server/bin/www)
三、購物車商品修改功能
商品加減和商品勾選
server/routes/users.js
-
//修改商品數量介面
-
router.post("/cartEdit",function(req,res,next){
-
var userId = req.cookies.userId,
-
productId = req.body.productId,
-
productNum = req.body.productNum,
-
checked = req.body.checked;
-
User.update({ // 查詢條件
-
"userId":userId,
-
"cartList.productId":productId
-
},{ // 修改的資料
-
"cartList.$.productNum":productNum,
-
"cartList.$.checked":checked
-
},function(err,doc){
-
if(err){
-
res.json({
-
status:'1',
-
msg:err.message,
-
result:''
-
});
-
}else{
-
res.json({
-
status:'0',
-
msg:'',
-
result:'suc'
-
});
-
}
-
});
-
})
src/views/Cart.vue
-
<!--選中圖示-->
-
<a href="javascipt:;" class="checkbox-btn item-check-btn" v-bind:class="{'check':item.checked=='1'}" @click="editCart('checked',item)">
-
<svg class="icon icon-ok">
-
<use xlink:href="#icon-ok"></use>
-
</svg>
-
</a>
-
<!--加減圖示-->
-
<a class="input-sub" @click="editCart('minu',item)">-</a>
-
<a class="input-add" @click="editCart('add',item)">+</a>
-
methods:{
-
editCart(flag,item){
-
if(flag == 'add'){ // 新增商品數量
-
item.productNum++;
-
}else if(flag = 'minu'){ // 減少商品數量
-
if(item.productNum <= 1){
-
return;
-
}
-
item.productNum--;
-
}else{ // 商品控制選中
-
item.checked = (item.checked=='1') ? '0' : '1';
-
}
-
axios.post('/users/cartEdit',{
-
productId:item.productId,
-
productNum:item.productNum,
-
checked:item.checked
-
}).then((response)=>{
-
let res = response.data;
-
})
-
}
-
}
購物車全選和商品實時計算功能
- 全選和取消全選
server/routes/users.js
-
//全選和取消全選
-
router.post('/editCheckAll',function(req,res,next){
-
var userId = req.cookies.userId,
-
checkAll = req.body.checkAll?'1':'0';
-
User.findOne({userId:userId},function(err,user){
-
if(err){
-
res.json({
-
status:'1',
-
msg:err.message,
-
result:''
-
});
-
}else{
-
if(user){
-
user.cartList.forEach((item)=>{
-
item.checked = checkAll;
-
})
-
user.save(function (err1,doc) {
-
if(err1){
-
res.json({
-
status:'1',
-
msg:err1,message,
-
result:''
-
});
-
}else{
-
res.json({
-
status:'0',
-
msg:'',
-
result:'suc'
-
});
-
}
-
})
-
}
-
}
-
})
-
})
src/views/Cart.vue
-
<a href="javascipt:;" @click="toggleCheckAll">
-
<span class="checkbox-btn item-check-btn" v-bind:class="{'check':checkAllFlag}">
-
<svg class="icon icon-ok"><use xlink:href="#icon-ok"/></svg>
-
</span>
-
<span>Select all</span>
-
</a>
-
export default {
-
data(){
-
return {
-
checkAllFlag:false // 控制全選
-
}
-
},
-
methods:{
-
toggleCheckAll(){ // 全選和取消全選
-
this.checkAllFlag = !this.checkAllFlag; // 取反
-
this.cartList.forEach((item)=>{
-
item.checked = this.checkAllFlag;
-
})
-
axios.post('/users/editCheckAll',{
-
checkAll:this.checkAllFlag
-
}).then((response)=>{
-
let res = response.data;
-
if(res.status=='0'){
-
console.log("update suc");
-
}
-
})
-
}
-
}
-
}
這裡出現一個問題,在點選select All全選之後,顯示正常,但是重新整理頁面之後全選的圖示沒有顯示全選,因為全選的資訊沒有儲存到資料庫儲存,所以重新整理之後就沒有了。
【解決的辦法】
用到了實時計算的computed功能,實時計算的是屬性,只不過是函式的寫法,data裡面就不用在聲明瞭。
src/views/Cart.vue
-
export default {
-
data(){
-
return {
-
// checkAllFlag:false // 控制全選
-
}
-
},
-
computed:{ // 實時計算的是屬性,只不過是函式的寫法,data裡面就不用在聲明瞭
-
checkAllFlag(){ // 是否全選屬性
-
return this.checkedCount == this.cartList.length; // 勾選的商品種數=購物車商品列表的商品種數時,返回true代表全選。
-
},
-
checkedCount(){ // 獲取已勾選的商品種數(幾種商品已勾選)
-
var i = 0;
-
this.cartList.forEach((item)=>{
-
if(item.checked=='1')i++;
-
});
-
return i;
-
}
-
},
-
methods:{
-
toggleCheckAll(){ // 全選和取消全選
-
// this.checkAllFlag = !this.checkAllFlag;
-
// 不能使用這種寫法了,checkAllFlag是實時計算的屬性,如果true取反變成false之後,還沒來得及執行下面的所有商品取消勾選,就實時計算了檢測到勾選的商品種數=購物車商品列表的商品種數,就又變成全選了。
-
var flag = !this.checkAllFlag; // 宣告變數取代
-
this.cartList.forEach((item)=>{
-
item.checked = flag ?'1':'0';
-
})
-
axios.post('/users/editCheckAll',{
-
checkAll:flag
-
}).then((response)=>{
-
let res = response.data;
-
if(res.status=='0'){
-
console.log("update suc");
-
}
-
})
-
}
-
}
-
}
頁面一重新整理就實時計算了
- 商品實時計算功能實現
這裡也要用到computed計算屬性
-
<div class="item-total">
-
Item total: <span class="total-price">{{totalPrice}}</span>
-
</div>
-
computed:{
-
totalPrice(){ // 總價格屬性
-
var money = 0;
-
this.cartList.forEach((item)=>{
-
if(item.checked=='1'){
-
money += parseFloat(item.salePrice)*parseInt(item.productNum);
-
}
-
});
-
return money;
-
}
-
}
接下來要對價格進行格式化,vuex官網github有一個對購物車將格式化的函式https://github.com/vuejs/vuex/blob/dev/examples/shopping-cart/currency.js 可以拿過來對價格格式化,在src/util/currency.js
格式化要用到過濾器:可以在src/views/Cart.vue匯入使用區域性過濾器,也可以在main.js使用全域性過濾器
-
<span class="total-price">{{totalPrice | currency('$')}}</span>
-
// 區域性過濾器
-
import {currency} from '@/util/currency.js'
-
filters:{
-
currency:currency // currency.js傳過來的本就是函式
-
},
-
// 全域性過濾器 在main.js裡定義
-
import {currency} from './util/currency'
-
Vue.filter("currency",currency);