vue.js实战,最后一个项目(电商系统),总结

一、项目目录:
在这里插入图片描述

二、基础配置:

(1)、项目全局引入jq和bootstrap

<1>、npm/cnpm install jQuery 安装jq
<2>、修改webpack.base.conf.js文件:
在头部加上var webpack = require('webpack');
在node属性的plugins属性里加上
plugins: [
    new webpack.optimize.CommonsChunkPlugin('common.js'),
    new webpack.ProvidePlugin({
      jQuery: "jquery",
      $: "jquery",
      "windows.jQuery": "jquery"
    })
  ]

<3>、main.js里面导入:
import $ from ‘jquery’
import ‘…/static/bootstrap-3.3.7-dist/css/bootstrap.css’;
import ‘…/static/bootstrap-3.3.7-dist/js/bootstrap.min’;

(2)、配置路由:

<1>、router/index.js文件:
1、先引入Vue和VueRouter
2、再使用VueRouter:Vue.use(VueRouter);
3、然后配置路由列表:

path是路径,meta是路由的一些信息(用于路由跳转前或者后的时候钩子函数做一些处理)
component:(resolve)=>require(['./views/shopList.vue'],resolve):
代表懒加载,路由跳转后再加之目标文件

const RoutersPath=[
  {
    path:'/shopList',
    meta:{
      title:'商品列表'
    },
    component:(resolve)=>require(['./views/shopList.vue'],resolve)
  },{
    path:'/carList',
    name:'carList',
    meta:{
      title:'购物车'
    },
    component:(resolve)=>require(['./views/carList.vue'],resolve)
  },{
    path:'/productInfo',
    name:'productInfo',
    meta:{
      title:'详细信息'
    },
    component:(resolve)=>require(['./views/productInfo.vue'],resolve)
  }
]

4、配置路由属性:
routes:是路由列表属性
mode:路由路径方式,mode:'history’代表路由路径将以"/"的形式展现,根贴近习惯
base:根路径:base:__dirname代表路由根路径是当前文件所在的目录,

const routerConfig={
  routes:RoutersPath,
  mode:'history',
  base:__dirname
}

5、实例化一个router对象

const router=new VueRouter(routerConfig);

6、声明钩子函数

路由前将路由模板的meta信息赋值给title
路由后滚动条跳转到初始化状态

router.beforeEach((to,from,next)=>{
  window.document.title=to.meta.title;
  next();
});

router.afterEach((to,from,next)=>{
  window.scroll(0,0);
});

7、export default router;
导出路由(其他js文件可以导入)

8、main.js里面:

(1)、import router from ‘./router’:
引入路由配置文件
(2)、给vue实例配置路由属性:router

new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

9、项目中的路由跳转方式:

this.$router.push({
    name:'productInfo',
    params:{
      id:id
    }	
 })

(3)、VUEX配置:

1、按模块创建vuex仓库

Vuex/shopStore/index文件:
声明数据(state)\数据赋值操作(mutations)\数据逻辑操作(actions)\数据视图(getters)

import productData from '../../../static/product.js';

//声明取出重复方法
function getFilterArray(array) {
  const res= [];
  const json = {};
  array.forEach((item,index)=>{
    const _self=item;
    if (!json[_self]) {
      res.push(_self);
      json[_self]=1
    }
  })
  return res;
};


//声明数据
const state={
  productList:[],
  carList:[]
}

//声明赋值方法
const mutations={
    setProduct(state,data){
      state.productList=data;
    },
    setCar(state,id){
      const nowCar=state.carList.find((item)=>item.id===id);
      if (nowCar) {
        nowCar.count++;
      }else {
        state.carList.push({
          id:id,
          count:1
        })
      }
    },
    editCar(state,obj){
      let car=state.carList.find((item)=>item.id==obj.id);
      car.count+=obj.count;
    },
    deleteItems(state,obj){
      let index=state.carList.findIndex((item)=>item.id==obj.id);
      state.carList.splice(index,1);
    }
}

//有关于逻辑的方法
const actions={
  getProductList(concent){
    setTimeout(()=>{
      concent.commit('setProduct',productData)
    },300)
  }
}


const getters={
  colors:(state)=>{
    const colors=state.productList.map((item)=>{
      return item.color;
    });
    return getFilterArray(colors);
  },
  brands:(state)=>{
    const brands=state.productList.map(item=>item.brand);
    return getFilterArray(brands);
  }
}


export default {
  state,
  mutations,
  actions,
  getters
}

2、按模块创建vuex仓库

Vuex/moudle.js文件:vuex的总配置文件

用于集中所有分模块的vuex仓库

import Vue from 'vue';
import Vuex from 'Vuex';
Vue.use(Vuex);

import shopStore from './shopStore/index.js';

export default new Vuex.Store({
  modules:{
    shopStore:shopStore
  }
});

3、main.js文件

引入vuex总配置:import store from ‘./Vuex/moudles.js’;
设置vue实例的store属性:

new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

4、其他文件里面调用vuex数据和方法

<1>数据:

this.$store.state.shopStore.productList

<2>数据赋值方法:

this.$store.commit('setCar',id);

<3>含逻辑的方法:

this.$store.dispatch('getProductList');

<4>数据视图:

this.$store.getters.colors;

三、其他文件:

(1)、组件:components/product.vue

<template>
    <div class="product-main" @click="routerClick(info.id)">
      <div class="product-item" :style="{backgroundColor:colors[info.color]}">
        <div class="product-img"><img :src="require('@/assets/img/'+info.image)"></div>
        <div class="product-name">{{info.name}}</div>
        <div class="product-cost">{{info.cost}}</div>
        <div class="product-cost">¥{{info.sales}}</div>
        <div class="product-add" @click.stop="addClick(info.id)">加入购物车</div>
      </div>
    </div>
</template>

<script>
    export default {
        name: "product",
        data(){
          return{
            colors:{
              '粉色':'#FFB6C1',
              '红色':'#DC143C',
              '金色':'#FFD700',
              '绿色':'#90EE90',
              '青色':'#00CED1',
            }
          }
        },
        props:{
          info:{
            type:Object
          }
        },
      methods:{
        addClick(id){
          this.$store.commit('setCar',id);
        },
        routerClick(id){
          this.$router.push({
            name:'productInfo',
            params:{
              id:id
            }
          })
        }
      }
    }
</script>

<style scoped>
  .product-main{
    display: flex;
    flex-flow:row  wrap;
    justify-content: center;
    align-items:center;
    cursor: pointer;
  }


  .product-add{
    background-color: #9acfea;
    position:absolute;
    top: 1px;
    right: 2px;
    display: none;
  }

  .product-main :hover .product-add{
    display: inline-block;
  }


  .product-item{
    width: 150px;
    height: 230px;
    margin: 2px 2px;
    display: flex;
    flex-flow:column wrap;
    justify-content: center;
    align-items:center;
    position:relative;
  }

  .product-img{
    width: 100px;
    height: 100px;
  }

  .product-img img{
    width: 100px;
    height: 100px;
  }

  .product-name{
    text-align: center;
    height: 50px;
    margin-top: 10px;
  }

  .product-cost{
    text-align: center;
    height: 10px;
    margin-top: 5px;
  }

</style>

(2)、路由视图:router/shopList.vue

<template>
  <div id="shopList-main">
    <div class="shopList-contorle">
      <div class="shopList-contorle-item">
        <div class="shopList-contorle-itemSpan-lable">排序:</div>
        <div @click="handleClick('cost')" class="shopList-contorle-itemSpan" :class="{on:nowSort.indexOf('cost')>-1}">按销量
          <template v-if="nowSort=='costAsc'">↑</template>
          <template v-if="nowSort=='costDesc'">↓</template>
        </div>
        <div @click="handleClick('sales')" class="shopList-contorle-itemSpan" :class="{on:nowSort.indexOf('sales')>-1}">按价格
          <template v-if="nowSort=='salesAsc'">↑</template>
          <template v-if="nowSort=='salesDesc'">↓</template>
        </div>
      </div>


      <div class="shopList-contorle-item">
        <div class="shopList-contorle-itemSpan-lable">按颜色筛选:</div>
        <div @click="handleSeahColor(item)" :class="{on:nowColor.indexOf(item)>-1}" class="shopList-contorle-itemSpan" v-for="item in colors"> {{item}}
        </div>
      </div>

      <div class="shopList-contorle-item">
        <div class="shopList-contorle-itemSpan-lable">按品牌筛选:</div>
        <div @click="handleSeahBrand(item)" :class="{on:nowBrand==item}" class="shopList-contorle-itemSpan" v-for="item in brands"> {{item}}
        </div>
      </div>

    </div>
    <div class="shopList-product">
      <product class="shopList-Item" v-for="item in filterList" :info="item" :key="item.id"></product>
    </div>
  </div>

</template>

<script>

  import product from '../../components/product.vue';

    export default {
        name: "shopList",
        components:{product},
        data(){
          return{
            nowSort:'',
            nowColor:[],
            nowBrand:''
          }
        },
        methods:{
          handleClick(type){
            //按销量
            if (type=='cost'){
              if (this.nowSort=='costAsc') {
                this.nowSort='costDesc'
              }else {
                this.nowSort='costAsc'
              }
            };

            //按价格
            if (type=='sales'){
              if (this.nowSort=='salesAsc') {
                this.nowSort='salesDesc'
              }else {
                this.nowSort='salesAsc'
              }
            };
          },
          //按颜色筛选
          handleSeahColor(item){
            var index=this.nowColor.findIndex((value)=>value==item);
            if (index>-1){
              this.nowColor.splice(index,1);
            }else {
              this.nowColor.push(item);
            }
            // this.nowColor=this.nowColor==''?item:(this.nowColor==item?'':item);
          },
          //按品牌筛选
          handleSeahBrand(item){
            this.nowBrand=this.nowBrand==''?item:(this.nowBrand==item?'':item);
          }
        },
        computed:{
          list(){
            return this.$store.state.shopStore.productList
          },
          colors(){
            return this.$store.getters.colors;
          },
          brands(){
            return this.$store.getters.brands;
          },
          filterList(){
            //复制list
            let list=[...this.list];
            //排序
            if (this.nowSort!=''){
              //按销量排序
              if (this.nowSort.indexOf('cost')>-1){
                  list= list.sort((a,b) =>{
                    return this.nowSort.indexOf('Asc')>-1?a.cost-b.cost:b.cost-a.cost
                  });
              }
              //按价格排序
              if (this.nowSort.indexOf('sales')>-1){
                list= list.sort((a,b) =>{
                  return  this.nowSort.indexOf('Asc')>-1?a.sales-b.sales:b.sales-a.sales
                });
              }
            };

            //按颜色筛选
            if (this.nowColor.length!=0){
              list=list.filter((item)=>{
                var obj=this.nowColor.find((value)=>value==item.color);
                if (obj!=undefined) return item;
              });
            }
            //按品牌筛选
            if (this.nowBrand!=''){
              list=list.filter((item,index)=>item.brand===this.nowBrand)
            }
            return list;
          }
        },
        mounted(){
          this.$store.dispatch('getProductList');
        }
    }
</script>

<style scoped>
  .shopList-main{
    display: flex;
    flex-flow:row  wrap;
    justify-content: center;
    align-items:center;
  }

  .shopList-product{
    display: flex;
    flex-flow:row  wrap;
    justify-content: flex-start;
    align-items:center;
  }


  .shopList-contorle{
    display: flex;
    flex-flow:column  wrap;
    justify-content: flex-start;
    align-items:flex-start;
  }


  .shopList-contorle-item{
    margin-top: 5px;
  }

  .shopList-contorle-itemSpan-lable{
    margin-left: 10px;
    float: left;
  }

  .shopList-contorle-itemSpan{
    background-color: #c4e3f3;
    color: #e38d13;
    border: 1px solid #c4e3f3;
    cursor: pointer;
    margin-left: 10px;
    float: left;
    width: 50px;
    height: 20px;
  }

  .on{
    background-color:#31b0d5;
  }


</style>

(3)、路由视图:router/productInfo.vue

<template>
  <div class="info-main">
    <div class="row">
      <div class="col-md-2"></div>
      <div class="col-md-4">
          <div class="info-item">
            <img :src="require('@/assets/img/'+productInfo.image)">
          </div>
      </div>
      <div class="col-md-4">
        <div class="info-text">
          <div>{{productInfo.name}}</div>
          <div>{{productInfo.cost}}</div>
          <div class="product-add" @click.stop="addClick(productInfo.id)">加入购物车</div>
        </div>
      </div>
      <div class="col-md-2"></div>
    </div>
  </div>

</template>

<script>
    export default {
        name: "productInfo",
        data() {
          return {
            id:this.$route.params.id,
            productInfo:null
          };
        },
        methods:{
          getProductInfo(){
            let list =this.$store.state.shopStore.productList
            this.productInfo=list.find((item)=>item.id===this.id);
          },addClick(id){
            this.$store.commit('setCar',id);
            this.$router.push({
              name:'carList'
            })
          },
        },
        mounted() {
          this.getProductInfo();
        }
    }
</script>

<style scoped>
  .info-main{
    height: 520px;
  }
  .info-item{
    height: 210px;
    margin: 155px auto;
    text-align: center;
  }

  .info-text{
    height: 100px;
    margin: 220px auto;
    text-align: center;
    display: flex;
    flex-flow:column   wrap;
    justify-content: center;
    align-items:center;
  }

  .product-add{
    background-color: #9acfea;
    display: inline-block;
    cursor: pointer;
  }

</style>

(4)、路由视图:router/carList.vue

<template>
    <div class="car-main">
          <div class="car-table row">
              <div class="col-md-1"></div>
              <div class="col-md-8">
                <table class="table table-striped">
                  <thead>
                    <th>商品信息</th>
                    <th>单价</th>
                    <th>数量</th>
                    <th>小计</th>
                    <th>删除</th>
                  </thead>
                  <tbody>
                      <tr v-for="(item,index) in carList">
                        <td>
                          <img :src="require('@/assets/img/'+productDic[item.id].image)">
                          <span>{{productDic[item.id].name}}</span>
                        </td>
                        <td>{{productDic[item.id].sales}}</td>
                        <td>
                          <button @click="handleAdd(1,item.id)">+</button>
                            {{item.count}}
                          <button @click="handleAdd(-1,item.id)">-</button>
                        </td>
                        <td>{{productDic[item.id].sales*item.count}}</td>
                        <td><span style="cursor: pointer" @click="deleteItems(item.id)">x</span></td>
                      </tr>
                  </tbody>
                </table>
              </div>
              <div class="col-md-1"></div>
          </div>
          <div class="car-cosp row">
            <div class="col-md-4" style="text-align: left">
              <span>优惠码:</span>
              <input type="text" v-model="youhuima" placeholder="输入优惠码">
              <button class="btn btn-default btn-sm" @click="youhuiclick">使用优惠码</button>
            </div>
            <div class="col-md-3"></div>
            <div class="col-md-2">
                <span style="display: block">总数量:{{costAll}}</span>
                <span style="display: block">原价:{{salesAll}}</span>
                <span style="display: block">优惠价:{{yousalesAll}}</span>
            </div>
          </div>
    </div>
</template>

<script>
    export default {
        name: "carList",
        data(){
          return{
            productData:this.$store.state.shopStore.productList,
            youhuima:'',
            youhuisales:0
          }
        },
        computed:{
          carList(){
              return this.$store.state.shopStore.carList
          },
          productDic(){
            const dict={};
            this.productData.forEach(item=>{
              dict[item.id]=item;
            });
            return dict;
          },
          costAll(){
            var allCount=0;
            this.carList.forEach((item)=>{
              allCount+=item.count;
            });
            return allCount;
          },
          salesAll(){
            var allSales=0;
            this.carList.forEach((item)=>{
              allSales+=this.productDic[item.id].sales*item.count;
            });
            return allSales;
          },
          yousalesAll(){
            return  this.salesAll-this.youhuisales;
          }
        },
        methods:{
          handleAdd(count,id){
            let carItem=this.carList.find((item)=>item.id==id);
            if (count<1 && carItem.count==1){return false};
            this.$store.commit('editCar',{
              count:count,
              id:id
            });
          },
          deleteItems(id){
            this.$store.commit('deleteItems',{
              id:id
            });
          },
          youhuiclick(){
            if (this.youhuima!='vue') {
              alert('优惠码错误!');
              return false;
            }

            if (this.youhuisales==500){
              alert('已经优惠过了!');
              return false;
            }
            this.youhuisales=500;
          }
        }
    }
</script>

<style scoped>
  .car-table,th,td{
    text-align: center;
    vertical-align: middle;
  }
  img{
    height: 50px;
  }

  .table tbody tr td{
    vertical-align: middle;
  }

</style>

(5)、主组件:App.vue

<template>
  <div id="app">
    <div class="app-main">
      <div class="row">
          <div class="col-md-2"></div>
          <div class="col-md-8" >
              <div class="app-controller">
                  <div class="app-router">
                    <ul class="nav nav-pills">
                      <li :class="{active:nowId=='listId'}" role="presentation" @click="handleClick"><router-link id="listId" to="/shopList">商品列表</router-link></li>
                      <li :class="{active:nowId=='carId'}" role="presentation" @click="handleClick"><router-link id="carId" to="/carList">购物车({{shopCarNum}})</router-link></li>
                    </ul>
                  </div>
              </div>
          </div>
          <div class="col-md-2"></div>
      </div>
      <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-8">
          <div class="app-view">
            <router-view/>
          </div>
        </div>
        <div class="col-md-2"></div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'App',
    data(){
      return{
        nowId:''
      }
    },
    computed:{
      shopCarNum(){
        var count=0;
        this.$store.state.shopStore.carList.forEach((item)=>{
          count+=item.count
        })
        return count;
      }
    },
    methods:{
      handleClick(event){
        this.nowId=event.target.id
      }
    }
  }
</script>

<style scoped>

  .app-main{
    margin: 0 auto;
  }

  .app-controller{
    margin: 0 auto;
    display: flex;
    flex-flow:row  wrap;
    justify-content: center;
    align-items:center;
  }


  .app-router{
    margin: 0 auto;
    text-align: center;
  }

  .app-view{
    margin: 0 auto;
    text-align: center;
  }

</style>

(6)、main.js程序入口

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import $ from 'jquery'
import '../static/bootstrap-3.3.7-dist/css/bootstrap.css';
import '../static/bootstrap-3.3.7-dist/js/bootstrap.min';
import store from './Vuex/moudles.js';
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

7、图片放在src\assets\img目录下,所有请求图片需要:

<img :src="require('@/assets/img/'+info.image)">

8、bootstrap和product.js等静态资源放在static文件下

9、product.js文件(静态数据)

export default [
  {
    id: 1,
    name: 'AirPods',
    brand: 'Apple',
    image: '1.png',
    sales: 10000,
    cost: 1288,
    color: '粉色'
  },
  {
    id: 2,
    name: 'BeatsX 入耳式耳机',
    brand: 'Beats',
    image: '2.png',
    sales: 11000,
    cost: 1188,
    color: '粉色'
  },
  {
    id: 3,
    name: 'Beats Solo3 Wireless 头戴式式耳机',
    brand: 'Beats',
    image: '3.png',
    sales: 5000,
    cost: 2288,
    color: '粉色'
  },
  {
    id: 4,
    name: 'Beats Pill+ 便携式扬声器',
    brand: 'Beats',
    image: '4.png',
    sales: 3000,
    cost: 1888,
    color: '红色'
  },
  {
    id: 5,
    name: 'Sonos PLAY:1 无线扬声器',
    brand: 'Sonos',
    image: '5.png',
    sales: 8000,
    cost: 1578,
    color: '红色'
  },
  {
    id: 6,
    name: 'Powerbeats3 by Dr. Dre Wireless 入耳式耳机',
    brand: 'Beats',
    image: '6.png',
    sales: 12000,
    cost: 1488,
    color: '红色'
  },
  {
    id: 7,
    name: 'Beats EP 头戴式耳机',
    brand: 'Beats',
    image: '7.png',
    sales: 25000,
    cost: 788,
    color: '金色'
  },
  {
    id: 8,
    name: 'B&O PLAY BeoPlay A1 便携式蓝牙扬声器',
    brand: 'B&O',
    image: '8.png',
    sales: 15000,
    cost: 1898,
    color: '金色'
  },
  {
    id: 9,
    name: 'Bose® QuietComfort® 35 无线耳机',
    brand: 'Bose',
    image: '9.png',
    sales: 14000,
    cost: 2878,
    color: '青色'
  },
  {
    id: 10,
    name: 'B&O PLAY Beoplay H4 无线头戴式耳机',
    brand: 'B&O',
    image: '10.png',
    sales: 9000,
    cost: 2298,
    color: '绿色'
  }
]
  • 1
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值