vue + vuex + element-ui 购物车

2,622 阅读2分钟

这个购物车功能相对简单,路由控制进入home页(商品列表页),添加商品后,cart页(购物车页)链接后会显示加入商品数量。进入cart页会列出添加的商品,可加减数量、删除单个商品、清空购物车。

项目截图

1. home页面

2. cart页面

项目src目录

src              
 ├─ components    '组件目录'
 │   ├─ cart.vue       '购物车组件'
 │   ├─ goods.vue      '货物组件'
 │   ├─ info.vue       '信息组件'
 │   └─ layout.vue     '布局组件'
 ├─ page          '页面目录'
 │   ├─ cart.vue       '购物车页面'
 │   └─ home.vue       '主页面 - 购物页面'
 ├─ router        '路由目录'
 │   ├─ router.js      '路由设置'
 ├─ store         'vuex相关'
 │   ├─ action.js
 │   ├─ index.js
 │   └─ mutations.js
 ├─ main.js
 ├─ App.vue

router.js 路由引入../page/目录下页面

import App from '../App'
import Home from '../page/home'
import Cart from '../page/cart'

export default [{
  path: '/',
  component: App,
  children: [{
    path: '/',
    component: Home
  }, {
    path: '/cart',
    component: Cart
  }]
}]

layout.vue

<template>
  <div>
    <el-container>
      <el-aside width="200px">
        <el-menu router :default-active="$route.path" class="el-menu-vertical-demo nav">
          <el-menu-item index="/">
            <i class="el-icon-menu"></i>
            <span slot="title">home</span>
          </el-menu-item>
          <el-menu-item index="/cart">
            <i class="el-icon-goods"></i>
            <span slot="title">
              cart
              <el-badge class="mark" v-if="totalNum" :value="totalNum" />
            </span>
          </el-menu-item>
        </el-menu>
      </el-aside>
      <el-main>
        <el-tag v-if="page == 'home'">商品列表</el-tag>
        <el-tag v-if="page == 'cart'">购物车</el-tag>
        <Goods v-if="page == 'home'"></Goods>
        <Cart v-if="page == 'cart'"></Cart>
      </el-main>
    </el-container>
  </div>
</template>
<script>
  import { mapState, mapGetters, mapActions } from 'vuex'
  import Goods from './goods'
  import Cart from './cart'

  export default {
    name: 'layout',
    props:['page'],
    components: {
      Goods,
      Cart
    },
    computed:{
      ...mapGetters([
        'goodList','totalNum'
      ])
    },
    methods: {
      ...mapActions(['addToCart'])
    }
  }
</script>

<style scoped>
</style>
  • 通过 <el-menu> 组件的 router 和 :default-active="$route.path" 属性加上路由功能
  • 通过 <el-menu-item> 组件的 index 映射 url 地址
  • <el-main> 组件部分通过 layout 父组件的 page 参数值进行 v-if 条件渲染

goods.vue

<template>
  <el-table :data="goodList" style="width: 100%">
    <el-table-column prop="id" label="商品ID" width="180"></el-table-column>
    <el-table-column prop="name" label="商品名称" width="180"></el-table-column>
    <el-table-column prop="price" label="单价" width="180"></el-table-column>
    <el-table-column label="操作">
      <template slot-scope="scope">
        <el-button size="mini" type="primary" icon="el-icon-plus" @click="addToCart( scope.row )">加入购物车</el-button>
      </template>
    </el-table-column>
  </el-table>
</template>

<script>
  import { mapState, mapGetters, mapActions } from 'vuex'
  export default {
    name: 'goods',
    computed:{
      ...mapGetters([
        'goodList','totalNum'
      ])
    },
    methods: {
      ...mapActions(['addToCart'])
    }
  }
</script>

<style scoped>
</style>
  • 商品列表页面,加入购物车按钮的事件 addToCart 绑定的是 action.js 中 注册的 ADD_TO_CART 事件,这里传的参数 (scope.row) 是对应这一行的数据,包括这一条商品id,商品名称,单价
  • 这里的 <el-badge class="mark" v-if="totalNum" :value="totalNum" /> 绑定的是点击加入购物车按钮后的购物车商品数量总数

cart.vue

<template>
  <div>
    <el-table :data="cartProducts" style="width: 100%">
      <el-table-column prop="id" label="商品ID" width="180"></el-table-column>
      <el-table-column prop="name" label="商品名称" width="180"></el-table-column>
      <el-table-column label="数量" width="180">
        <template slot-scope="scope">
          <el-input-number size="mini" :min="1" :value="scope.row.num" v-on:input="handleBlur" @change="handleChange( scope.row )"></el-input-number>
        </template>
      </el-table-column>
      <el-table-column prop="price" label="单价" width="180"></el-table-column>
      <el-table-column prop="total_num" label="总价" width="180"></el-table-column>
      <el-table-column label="操作" width="180">
        <template slot-scope="scope">
          <el-button type="danger" plain icon="el-icon-delete" size="mini" @click="dialogVisibleTrue( scope.row )">删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <Info v-if="totalNum"></Info>

    <el-dialog title="注意" :visible.sync="dialogVisible" width="20%">
      <span>确定要删除这个商品吗?</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogSure">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
  import { mapState, mapGetters, mapActions } from 'vuex'
  import Info from './info'
	
  export default {
    name: 'cart',
    data() {
      return {
        dialogVisible : false,
        result : null,
        input_number_value:1
      }
    },
    computed:{
      ...mapGetters([
        'cartProducts','totalNum'
      ])
    },
    components: {
        Info
    },
    methods: {
      ...mapActions(['delProduct','numChange']),
      dialogVisibleTrue( data ){
        this.dialogVisible = true;
        this.result = data;
      },
      dialogSure(){
        this.delProduct( this.result );
        this.dialogVisible = false;
      },
      handleBlur(value){
        this.input_number_value = value
      },
      handleChange( data ) {
        data.value = this.input_number_value;
        this.numChange( data );
      }
    }
  }
</script>

<style scoped>
  .el-table th>.cell{text-align: center;}
  .el-table td>.cell{text-align: center;}
</style>

  • <el-input-number>是控制单个商品数量加减的组件, :value="scope.row.num" 属性绑定value值,v-on:input="handleBlur" 属性通过handleBlur方法将数量单独存储在 data 的 input_number_value 值中,@change="handleChange( scope.row )" 将change事件绑定handleChange方法,而 handleChange 方法里调用 action.js 中注册的 numChange 事件,给 numChange 传的参数中,data.value 是 data 中临时存放的 input_number_value 的值。
  • 删除按钮是删除对应的一条数据,实现上,类似加入购物车的功能,这里加了一个弹出层<el-dialog>,询问是否要删除
  • 这个页面引入一个组件 info.vue 用来显示购物车商品总数量、总价和清空购物车按钮

info.vue

<template>
  <div>
    <el-row :gutter="20">
      <el-col :span="6">总数:{{totalNum}}</el-col>
      <el-col :span="6">合计价格:{{totalPrice}}</el-col>
      <el-col :span="6">
        <el-button type="danger" size="medium" icon="el-icon-delete" @click="dialogDeleteAll">清空购物车</el-button>
      </el-col>
    <el-col :span="6"></el-col>
    </el-row>

    <el-dialog title="注意" :visible.sync="dialogVisible" width="20%">
      <span>要清空购物车吗?</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogSure">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
  import { mapState, mapGetters, mapActions } from 'vuex'
	
  export default {
    name: 'info',
    data() {
      return {
        dialogVisible : false
      }
    },
    computed:{
      ...mapGetters(['totalPrice','totalNum'])
    },
    methods: {
      ...mapActions(['clearAllCart']),
      dialogDeleteAll( data ){
        this.dialogVisible = true;
      },
      dialogSure(){
        this.clearAllCart();
        this.dialogVisible = false;
      }
    }
  }
</script>

<style scoped>
  .el-col-6{padding: 20px; text-align: center;}
</style>
  • 清空购物车按钮就是清空 vuex 中 added 数组

项目地址: gitee.com/sonicwater/…