vue之仿网易严选详解

vue之仿网易严选详解_第1张图片


文章目录

  • 前言
    • 1、使用到的技术
    • 2、使用到的软件
    • 3、使用代码步骤
  • 一、首页
  • 二、分类列表
  • 三、商品列表
  • 四、商品详情
  • 五、加入购物车
  • 六、购物车列表


前言

1、使用到的技术

1-vant ui框架
2-less-css预编译语言
3-vue
4-vue router
5-vuex
6-axios
7-javascript
8-html5
9-css3
10-vue-cli
11-webpack

2、使用到的软件

1.visua lstudio code,官网https://code.visualstudio.com/
2.navicat


3、使用代码步骤

代码地址 链接:https://pan.baidu.com/s/1xVw9nVNf-Hg-N3S_3IoNvA 提取码:f786

1、用navicat创建一个新的数据库

vue之仿网易严选详解_第2张图片

导入数据库,运行SQL文件vue之仿网易严选详解_第3张图片
vue之仿网易严选详解_第4张图片
导入mall-server文件下的nideshop.sql文件vue之仿网易严选详解_第5张图片
导入成功后就会出现这些表(可能要等待一点时间)vue之仿网易严选详解_第6张图片
** 2、使用visua lstudio code打开文件**

在这里插入图片描述

打开红线路径的文件,database是数据库名称、user,password是数据库账户和密码(需要改),prefix是表的前缀
vue之仿网易严选详解_第7张图片
把文件mall和mall-server,分别在终端打开。打开后输入npm install安装两个文件都要安装。(还需要安装vue依赖才能使用)vue之仿网易严选详解_第8张图片
安装成功后,在mall-server输入npm start,在mall输入npm run serve(要是报错看下面)
在这里插入图片描述

在这里插入图片描述

如果报错Module build failed (from ./node_modules/babel-loader/lib/index.js)
意思是babel版本冲突
安装npm install @babel/core @babel/preset-env

再用浏览器打开这个地址

vue之仿网易严选详解_第9张图片

浏览器打开后

vue之仿网易严选详解_第10张图片

打开文件mall\src\views的Home.vue

<script>
//导入接口
import axios from 'axios'
import api from '../assets/config/api'
</script>
接口地址,mall/src/assets/config/api.js

vue之仿网易严选详解_第11张图片

ajax请求,返回后端数据库的数据

vue之仿网易严选详解_第12张图片

vue之仿网易严选详解_第13张图片
vue之仿网易严选详解_第14张图片

一、首页

1、Search 搜索

打开vant框架网站,基本用法
https://vant-contrib.gitee.io/vant/#/zh-CN/search
vue之仿网易严选详解_第15张图片
mall\src\views的Home.vue

//Home.vue
<van-search placeholder="商品搜索 共239万款好物" input-align='center' v-model="searchData" />
//导入vant
import Vue from 'vue';
import {
      Lazyload } from 'vant';
Vue.use(Lazyload);
export default {
     
  name: 'home',
 data:function(){
     
    return {
     
    //设置v-mode的值
     searchData:"", 
     data:{
     },    
    }
  },
 }

2、Swipe 轮播

https://vant-contrib.gitee.io/vant/#/zh-CN/swipe
vue之仿网易严选详解_第16张图片

//Home.vue
	<van-swipe :autoplay="3000" :width="375" :height="200">
      <van-swipe-item v-for="(image, index) in images" :key="index">
        <img class="swiperimg" v-lazy="image.image_url" />
      </van-swipe-item>
    </van-swipe>

 <scrpet>
 export default {
     
 data:function(){
     
    return {
     
      searchData:"",
      data:{
     },
    }
  },
  computed: {
     
    images:function(){
     
//判断ajax的data.banner是否为数组,是的话返回数组,不是的话返回空数组
      if(typeof this.data.banner=='object'){
     
        return this.data.banner
      }else{
     
        return []
      }
    },
   }
   }
 </scrpet>
 <style lang="less">
  #home{
     
    .swiperimg{
     
      width: 375px;
      height: 200px;
    }
  	}
<style>

vue之仿网易严选详解_第17张图片
3、5个图标

Grid 宫格https://vant-contrib.gitee.io/vant/#/zh-CN/grid

<van-grid :column-num='5'>
      <van-grid-item v-for="(item,index) in channel"  :key="index" :icon="item.icon_url" :text="item.name" />
    </van-grid>
     <script>
  export default {
     
     name: 'home',
  data:function(){
     
    return {
     
      searchData:"",
      data:{
     },
    }
    computed: {
     
    channel:function(){
     
    //判断ajax的data.channel是否为数组,是的话返回数组,不是的话返回空数组
      if(typeof this.data.channel=='object'){
     
        return this.data.channel
      }else{
     
        return []
      }
    },
    }
   }
    </script>

vue之仿网易严选详解_第18张图片
4、品牌制造商直供

图片懒加载
https://vant-contrib.gitee.io/vant/#/zh-CN/swipe

懒加载是一种网页性能优化的方式,它能极大的提升用户体验。就比如说图片,图片一直是影响网页性能的主要元凶,现在一张图片超过几兆已经是很经常的事了。如果每次进入页面就请求所有的图片资源,那么可能等图片加载出来用户也早就走了。所以,我们需要懒加载,进入页面的时候,只请求可视区域的图片资源。

vue之仿网易严选详解_第19张图片

<div class="brandlist">
      <!-- Panel 面板 -->
      <van-panel title="品牌制造商直供">
        <van-grid  :column-num="2">
          <van-grid-item v-for="(item1,index1) in brandList" :key="index1">
            <van-image fit="cover" lazy-load :src="item1.new_pic_url" />
            <h4 class="title">{
     {
     item1.name}}</h4>
            <p class="price">{
     {
     item1.floor_price}}元起</p>
          </van-grid-item>
        </van-grid>
      </van-panel>
    </div>
    <script>
  export default {
     
     name: 'home',
  data:function(){
     
    return {
     
      searchData:"",
      data:{
     },
    }
    computed: {
     
    brandList:function(){
     
    //判断ajax的data.brandList是否为数组,是的话返回数组,不是的话返回空数组
      if(typeof this.data.brandList=='object'){
     
        return this.data.brandList
      }else{
         
        return []
      }
    },
    }
   }
    </script>
    <style lang="less">
    .brandlist{
     
      .van-grid-item__content{
     
        padding: 0;
      }
      .van-image{
     
        border: 1px solid #fff;
      }
      .title{
     
        position: absolute;
        top: 20px;
        left: 10px;
       
      }
      .price{
     
        position: absolute;
        top: 40px;
        left: 10px;
        font-size: 14px;
        color:#999 ;
      }
      </style>

vue之仿网易严选详解_第20张图片5、新品首发

   <!-- 新品首发 -->
    <div class="newlist">
      <van-panel title="品牌制造商直供">
        <van-grid  :column-num="2">
          <van-grid-item v-for="(item2,index2) in newGoodsList" :key="index2">
            <van-image fit="cover" lazy-load :src="item2.list_pic_url" />
            <h4 class="title" >{
     {
     item2.name}}</h4>
            <p class="price">{
     {
     item2.retail_price}}元起</p>
           </van-grid-item>
          
       </van-grid>
      </van-panel>
    </div>
 <script>
   export default {
     
     name: 'home',
  data:function(){
     
    return {
     
      searchData:"",
      data:{
     },
    }
    computed: {
     
    newGoodsList:function(){
     
        //判断ajax的data.newGoodsList是否为数组,是的话返回数组,不是的话返回空数组
      if(typeof this.data.newGoodsList=='object'){
     
        return this.data.newGoodsList
      }else{
     
        return []
      }
    },
    }
   }
   <script>
   <style>
    .newlist{
     
      
      .title{
     
        
        width: 90%;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
      }
      .price{
     
        
        font-size: 14px;
        color:#999 ;
      }
    }![在这里插入图片描述](https://img-blog.csdnimg.cn/20200911153220161.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpYW9qdWFqdW4=,size_16,color_FFFFFF,t_70#pic_center)

   </style>

vue之仿网易严选详解_第21张图片
6、人气面板
Card 卡片
https://vant-contrib.gitee.io/vant/#/zh-CN/card
vue之仿网易严选详解_第22张图片

<div class="hotlist">
      <van-panel title="人气推荐">
        <van-card v-for="(item3,index3) in hotGoodsList" :key="index3"
          :price="item3.retail_price"
          :desc="item3.goods_brief"
          :title="item3.name"
          :thumb="item3.list_pic_url"
        />
      </van-panel>
    </div>
    <script>
   export default {
     
     name: 'home',
  data:function(){
     
    return {
     
      searchData:"",
      data:{
     },
    }
    computed: {
     
   hotGoodsList:function(){
     
   //判断ajax的data.hotGoodsList=是否为数组,是的话返回数组,不是的话返回空数组
      if(typeof this.data.hotGoodsList=='object'){
     
        return this.data.hotGoodsList
      }else{
     
        return []
      }
    },
  },
    }
   }
   <script>
   <style>
    .hotlist{
     
      .van-card__content{
     
        justify-content: center;
        text-align:left;
      }
      .van-card__title{
     
        font-weight: 900;
        color: #333;
        font-size: 14px;
        padding: 5px 0;
      }
      .van-card__price{
     
        color: red;
      }
    }
 </style>

vue之仿网易严选详解_第23张图片

二、分类列表

1、创建分类列表的页面

//mall/src/router/index.js
{
     
    path:'/category',
    name:'category',
    component:Category

  },
在mall/scr/views,创建一个文件category.vue 然后导入页面
//mall/src/router/index.js
import Category from '@/views/category'

2、创建Tabbar 标签栏

Tabbar 标签栏https://vant-contrib.gitee.io/vant/#/zh-CN/tabbar#dai-ma-yan-shi

vue之仿网易严选详解_第24张图片
在mall/src/components,创建新的文件tabBtn.vue

//mall/src/components/tabBtn.vue
<template>
<van-tabbar v-model="tabActive">
      <van-tabbar-item icon="home-o">首页</van-tabbar-item>
      <van-tabbar-item icon="medal-o">专题</van-tabbar-item>
      <van-tabbar-item icon="qr">分类</van-tabbar-item>
      <van-tabbar-item icon="shopping-cart-o">购物车</van-tabbar-item>
      <van-tabbar-item icon="manager-o" badge="20">我的</van-tabbar-item>
    </van-tabbar>
 </template>
    <script>
   export default {
     
     name: 'home',
  data:function(){
     
    return {
     
      tabActive:"0",
    }
  }
  </script>
引入tabBtn文件
//mall/src/views/Home.vue
import tabBtn from '@/components/tabBtn.vue'
把tabBar显示在页面上
//mall/src/views/Home.vue
 <tab-btn></tab-btn>
设置id为category,导入tabBa
//mall/src/views/category.vue
<template>
//设置id为category
    <div id="category">
  //  把tabBar放进来
     <tab-btn></tab-btn>
   </div>
</template>
 <script>
 //导入tabBar
 import tabBtn from '@/components/tabBtn.vue'
export default {
     
    data(){
     
        return {
               
        }
     }//导入
      components:{
     
        tabBtn
    }
  }   
     </script>
to跳转到分类页面category
//mall/src/components/tabBtn.vue
      <van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
      <van-tabbar-item icon="qr" to="/category">分类</van-tabbar-item>
监听路由变化,改变v-model的tabActive值
 mounted() {
     
        // 监听路由
        switch(this.$route.path){
     
                
                case "/category":
                    this.tabActive = 2;
                    break;
                case '/':
                    this.tabActive = 0;
                    break;
                default:
                    this.tabActive = 0;
            }
    },

当点击分类时页面就会发生改变
vue之仿网易严选详解_第25张图片

3、分类选择

TreeSelect 分类选择
https://vant-contrib.gitee.io/vant/#/zh-CN/tree-select
vue之仿网易严选详解_第26张图片

vue之仿网易严选详解_第27张图片

4、ajax请求获取数据

//mall/src/views/category.vue
<template>
    <div id="category">
        <van-search placeholder="商品搜索 共239万款好物" input-align='center' v-model="searchData" />
        <tab-btn></tab-btn>
        </div>
</template>
<script>
import tabBtn from '@/components/tabBtn.vue'
import axios from 'axios'
import api from '@/assets/config/api.js'
export default {
     
    data(){
     
        return {
     
            searchData:"",
            
        }
    },
    async created() {
     
   // ajax请求
        let res = await axios.get(api.CatalogList)
        let data = res.data;
        console.log(data)
    },
     components:{
     
        tabBtn
    }
 }
</script>

data里面的categoryList数据

vue之仿网易严选详解_第28张图片

还有里面的子数据subCategoryList

vue之仿网易严选详解_第29张图片

5、获取数组categoryList,并进行循环

//mall/src/views/category.vue
<template>
    <div id="category">
        <van-search placeholder="商品搜索 共239万款好物" input-align='center' v-model="searchData" /> <van-tree-select
        :items="items"
        :main-active-index.sync="activeIndex"
        >
        //子内容,九宫格
        <template slot="content">            

            </template>
        <tab-btn></tab-btn>
        </div>
</template>
<script>
import tabBtn from '@/components/tabBtn.vue'
import axios from 'axios'
import api from '@/assets/config/api.js'
export default {
     
    data(){
     
        return {
     
            searchData:"",
            activeIndex: 0,
            data:{
     },
        }
    },
    async created() {
     
   // ajax请求
        let res = await axios.get(api.CatalogList)
        let data = res.data;
         this.data = data.data;
    },
     computed: {
     
        items:function(){
     
            //判断ajax的data.categoryList是否undefined,是的话返回空数组,不是的话返回数组进行循环
            if(this.data.categoryList==undefined){
     
                return []
            }else{
     
                // console.log(123)
                let arr = []
                 //对数据进行循环
                this.data.categoryList.forEach((item,index)=>{
     
                    // console.log(index)
                    item.text = item.name
                      // 修改数据
                    arr.push(item)
                })
                // 返回数组arr
                return arr;
            }
     components:{
     
        tabBtn
    }
 }

vue之仿网易严选详解_第30张图片

6、修改分类选择的高度

//mall/src/views/category.vue
        <van-tree-select
        :items="items"
        :main-active-index.sync="activeIndex"
        //减去搜索框和标签栏高度
        height='calc(100vh - 104px)'
        @click-nav='changeRight'
        >

vue之仿网易严选详解_第31张图片

7、添加点击事件@click-nav,获取回调参数index
vue之仿网易严选详解_第32张图片

//mall/src/views/category.vue
<van-tree-select
        :items="items"
        :main-active-index.sync="activeIndex"
        height='calc(100vh - 104px)'
        @click-nav='changeRight'
        >

        <script>
        methods: {
     
        //点击事件获取activeIndex索引值
        changeRight:function(index){
     
            console.log(index)  
        }
    },
        </script>

vue之仿网易严选详解_第33张图片

8、监听activeIndex改变,获取categoryList的id

//mall/src/views/category.vue
     // 监听activeIndex改变
 watch: {
     
        activeIndex:async function(){
     
            //判断有没有数据,没有的话返回空数组
            if(this.items.length!==0){
     
                //通过当前的id去请求数据
                let id = this.items[this.activeIndex].id
                console.log(id)
            }else{
     
                this.subCategoryList =  []
            }
            
        }
    },

vue之仿网易严选详解_第34张图片

vue之仿网易严选详解_第35张图片

    watch: {
     
        activeIndex:async function(){
     
            //判断有没有数据,没有的话返回空数组
            if(this.items.length!==0){
     
                //通过当前的id去请求数据
                let id = this.items[this.activeIndex].id
                // console.log(id)
                //通过id请求数据
                //传参需要里面写对象params
                let res = await axios.get(api.CatalogCurrent,{
     params:{
     id}})
                console.log(res)
                this.subCategoryList = res.data.data.currentCategory.subCategoryList
                //获取九宫格上面的图片地址
                this.bannerImg = this.data.currentCategory.img_url;
            }else{
     
                this.subCategoryList =  []
            }
            
        }
    },

vue之仿网易严选详解_第36张图片

9、将数据渲染在九宫格上

//自定义内容,九宫格
            <template slot="content">
                <div class='imgbanner'>
                 <img width="100%" :src="bannerImg" alt="">
                </div>
               
                <van-grid :column-num="3">
                    <van-grid-item v-for="(item,index) in subCategoryList" :key="index" :icon="item.wap_banner_url" :text="item.name" :to="'/categoryList/'+item.id" >

                        
                    </van-grid-item>
                    
                </van-grid>

            </template>

vue之仿网易严选详解_第37张图片

三、商品列表

1、标签页

Tab 标签页,标签栏滚动
https://vant-contrib.gitee.io/vant/#/zh-CN/tab

vue之仿网易严选详解_第38张图片

在mall/src/views创建新的页面categorylist.vue
//mall/src/views/categorylist.vue
<template>
    <div id="categoryList">
<van-tabs v-model="active">
  <van-tab title="标签 1">内容 1</van-tab>
  <van-tab title="标签 2">内容 2</van-tab>
  <van-tab title="标签 3">内容 3</van-tab>
  <van-tab title="标签 4">内容 4</van-tab>
</van-tabs>
	</div>
</template>

<script>
export default {
     
//设定props
    props:['id'],
    data(){
     
        return {
     
            tabActive:0,
        }
    },
    created() {
     
            console.log(this.id)
    }
 }
</script>

2、在index.js设置props

//mall/src/router/index.js
import categorylist from '@/views/categorylist'
{
     
    path:'/categorylist/:id',
    name:'categorylist',
    component: categorylist,
    //通过props传进来,props改为true
    props:true
  }

vue之仿网易严选详解_第39张图片

**3、在main.js进行全局导入**
//mall/src/main.js
import api from '@/assets/config/api.js'
new Vue({
     
  data:{
     
    api:api
  }
})
<script>
//mall/src/views/categorylist.vue
import axios from 'axios'
export default {
     
//设定props
    props:['id'],
    data(){
     
        return {
     
            tabActive:0,
        }
    },
    created() {
     
            console.log(this.$root.api)
    }
 }
</script>
然后就能得到数据

vue之仿网易严选详解_第40张图片
vue之仿网易严选详解_第41张图片

**4、获取数据**
<script>
//mall/src/views/categorylist.vue
import axios from 'axios'
export default {
     
//设定props
    props:['id'],
    data(){
     
        return {
     
            tabActive:0,
        }
/    },
    created() {
     
        let res = await axios.get(this.$root.api.GoodsCategory,{
     params:{
     id:this.id}})
        console.log(res.data)
    }
 }
</script>

vue之仿网易严选详解_第42张图片

5、将数据渲染在页面上

<template>
    <div id="categoryList">
<van-tabs v-model="tabActive">
            <van-tab v-for="(item,index) in clist" 
            :key="index"
            :title="item.name">
            <h3>{
     {
     item.name}}</h3>
            <p>{
     {
     item.front_name}}</p>
            </van-tab>
</van-tabs>
	</div>
</template>

<script>
import axios from 'axios'
export default {
     
//设定props
    props:['id'],
    data(){
     
        return {
     
            tabActive:0,
            clist:[]
        }
    },
  async created() {
     
            // console.log(this.id)
            // console.log(this.$root.api)
            let res = await axios.get(this.$root.api.GoodsCategory,{
     params:{
     id:this.id}})
            this.clist = res.data.data.brotherCategory
               console.log(res.data)
    }, 
 }
</script>

vue之仿网易严选详解_第43张图片

6、获取商品列表数据

async created() {
     
        //ajax请求
        let res = await axios.get(this.$root.api.GoodsCategory,{
     params:{
     id:this.id}})
        console.log(res.data)
        this.clist = res.data.data.brotherCategory
        let id = this.clist[0].id;
      
        this.clist.forEach(async (item,index)=>{
     
        //获取函数getlist的数据
            item.plist= await this.getlist(item.id,1)
            console.log(item.plist)
            this.$forceUpdate()
        })

    },
    methods: {
     
        async getlist(cid,page){
     
            let res = await axios.get(`${
     this.$root.api.GoodsList}?categoryId=${
     cid}&page=${
     page}&size=20`)
            console.log(res)
            return res.data.data
        },
        
    },
**7、将商品列表的数据渲染在页面上**
//mall/src/views/categorylist.vue
<template>
    <div id="categoryList">
    //Tab 标签页
        <van-tabs v-model="tabActive">
            <van-tab v-for="(item,index) in clist" 
            :key="index"
            :title="item.name">
            <h3>{
     {
     item.name}}</h3>
            <p>{
     {
     item.front_name}}</p>
            <div v-if="item.plist">
            //九宫格
            <van-grid :border="true" :column-num="2">         
                    <van-grid-item   v-for="(item1,index1) in item.plist.data" :to="'/product/'+item1.id" :key="index1">
                        <van-image
                            width="100"
                            height="100"
                            :src="item1.list_pic_url"
                            />
                        <h4 class="van-ellipsis">{
     {
     item1.name}}</h4>
                        <p class="price">¥{
     {
     item1.retail_price}}</p>
                        
                    </van-grid-item>
                
                
            </van-grid>
            </div>
            
            </van-tab>
            
        </van-tabs>

    </div>
</template>

<script>
import axios from 'axios'
export default {
     
//设定props
    props:['id'],
    data(){
     
        return {
     
            tabActive:0,
            clist:[]
        }
    },
    async created() {
     
        //ajax请求
        let res = await axios.get(this.$root.api.GoodsCategory,{
     params:{
     id:this.id}})
        console.log(res.data)
        this.clist = res.data.data.brotherCategory
        let id = this.clist[0].id;
      
        this.clist.forEach(async (item,index)=>{
     
        //获取函数getlist的数据
            item.plist= await this.getlist(item.id,1)
            console.log(item.plist)
            this.$forceUpdate()
        })

    },
    methods: {
     
        async getlist(cid,page){
     
            let res = await axios.get(`${
     this.$root.api.GoodsList}?categoryId=${
     cid}&page=${
     page}&size=20`)
            console.log(res)
            return res.data.data
        },
        
    },
    
}
</script>


<style lang="less">
    #categoryList{
     
        .van-ellipsis{
     
            width: 100%;
            font-size: 14px;
            font-weight: 500;
            padding: 0 10px;
        }
        .van-grid-item{
     
            overflow: hidden;
            box-sizing: border-box;
        }
        .price{
     
            color:red;
        }
    }
</style>

vue之仿网易严选详解_第44张图片

四、商品详情

1、新建页面

在mall/src/views新建一个页面product.vue

//mall/src/views/product.vue
<template>
		<div>
		<h1>product</h1>
		</div>
</template>
在categorylist.vue ,设置路由入口to
//mall/src/views/categorylist.vue    
                    <van-grid-item   v-for="(item1,index1) in item.plist.data" :to="'/product/'+item1.id" :key="index1">                      

vue之仿网易严选详解_第45张图片
2、NavBar 导航栏
https://vant-contrib.gitee.io/vant/#/zh-CN/nav-barvue之仿网易严选详解_第46张图片

van-nav-bar
            title="商品"
            left-text="返回"
            left-arrow
            @click-left="onClickLeft"
            />
    methods:{
     
        onClickLeft:function(){
     
            this.$router.go(-1);
        },

vue之仿网易严选详解_第47张图片

3、轮播


<van-swipe :autoplay="3000">
            <van-swipe-item v-for="(image, index) in images" :key="index">
                <img class="swipeImg" v-lazy="image.img_url" />
            </van-swipe-item>
        </van-swipe>
        
 computed:{
     
        images:function(){
     
            if(this.data.gallery==undefined){
     
                return []
            }else{
     
                return this.data.gallery;
            }
        }
    },
 export default {
     
        props:['id'],
  data() {
     
    return {
     
    data:{
     },
    }
async created(){
     
        this.$store.dispatch('AjaxCart');
       let res = await axios.get(this.$root.api.GoodsDetail,{
     params:{
     id:this.id}})
       console.log(res.data)
       this.data = res.data.data;
     }
     }

.swipeImg{
     
            width: 100%;
        }

vue之仿网易严选详解_第48张图片
4、0天无忧退货

 <div class='info'>
            <span>30天无忧退货</span>
            <span>48小时快速退款</span>
            <span>88元免邮费</span>
        </div>



 		.info{
     
            display: flex;
            justify-content: space-around;
            font-size: 12px;
            color: #999;
            height: 24px;
            line-height: 24px;
            background: #efefef;
            span{
     
                position: relative;
            }
            span::before{
     
                content:"";
                display: block;
                position: absolute;
                left: -10px;
                top: 8px;
                width: 4px;
                height: 4px;
                border-radius: 2px;
                border: 1px solid red;

            }

vue之仿网易严选详解_第49张图片

5、商品参数


<van-cell title="请选择规格数量" is-link />

   <div class="proParams">
            <h3>商品参数</h3>
            <div class="proItem" v-for="(item1,index1) in attribute" :key="index1">
                <span class="title">{
     {
     item1.name}}</span>
                <span class="value">{
     {
     item1.value}}</span>
            </div>
        </div>
        
  async created(){
     
       this.attribute = this.data.attribute;
       }
       
 .van-cell__title{
     
            text-align: left;
        }

.proItem{
     
                width: 90%;
                margin: 0 auto;
                display: flex;
                justify-content: space-between;
                height: 24px;
                color: #999;
                font-size: 12px;
                background: #efefef;
                line-height: 24px;
                text-align: left;
                span.title{
     
                    width:45px;
                    padding: 0 10px;
                    border-right: 1px solid #ccc;
                }
                span.value{
     
                    width: 260px;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    white-space: nowrap;
                }
            }

vue之仿网易严选详解_第50张图片
6、商品详情

//引入html
<div class="proDetail" v-html="info.goods_desc">

        </div>



.proDetail{
     
            width: 100%;
            img{
     
                width: 100%
            }
            p{
     
                margin: 0;
                padding: 0;
                display: flex;
            }
        }

vue之仿网易严选详解_第51张图片

五、加入购物车

1、GoodsAction 商品导航
https://vant-contrib.gitee.io/vant/#/zh-CN/goods-action

vue之仿网易严选详解_第52张图片
Sku 商品规格
https://vant-contrib.gitee.io/vant/#/zh-CN/sku
vue之仿网易严选详解_第53张图片

2、修改加入购物车时的商品规格sku

vue之仿网易严选详解_第54张图片

			//商品导航
    <van-goods-action>
            <van-goods-action-icon to="/buycart" :info="$store.state.cartTotal.goodsCount==0?'':$store.state.cartTotal.goodsCount" icon="cart-o" text="购物车" />
            <van-goods-action-icon icon="star-o" text="已收藏"  />
            <van-goods-action-button @click="chooseSku" type="warning" text="加入购物车" />
            <van-goods-action-button type="danger" text="立即购买" />
        </van-goods-action>
        
		//选择商品规格
 <<van-sku
            v-model="showSku"
            :sku="sku"
            :goods="goods"
            @buy-clicked="onBuyClicked"
            @add-cart="onAddCartClicked"
            />
    </div>


export default {
     
    props:['id'],
    data(){
     
        return {
     
            data:{
     },
            info:{
     },
            attribute:[],
            showSku:false,
            sku:sku,   
            goods: {
     
                // 商品标题
                title: '测试商品',
                // 默认商品 sku 缩略图
                picture: 'https://img.yzcdn.cn/1.jpg'   
          }
            }  
            
        let sku = {
     
  // 所有sku规格类目与其值的从属关系,比如商品有颜色和尺码两大类规格,颜色下面又有红色和蓝色两个规格值。
  // 可以理解为一个商品可以有多个规格类目,一个规格类目下可以有多个规格值。
  tree: [
    {
     
      k: '颜色', // skuKeyName:规格类目名称
      v: [
        {
     
          id: '30349', // skuValueId:规格值 id
          name: '红色', // skuValueName:规格值名称
          
        },
        {
     
          id: '1215',
          name: '蓝色',
          
        }
      ],
      k_s: 's1' // skuKeyStr:sku 组合列表(下方 list)中当前类目对应的 key 值,value 值会是从属于当前类目的一个规格值 id
    }
  ],
  // 所有 sku 的组合列表,比如红色、M 码为一个 sku 组合,红色、S 码为另一个组合
  list: [
    {
     
      id: 2259, // skuId,下单时后端需要
      price: 100, // 价格(单位分)
      's-1': '2', // 规格类目 k_s 为 s1 的对应规格值 id
      's-2': '3', // 规格类目 k_s 为 s2 的对应规格值 id
      stock_num: 110 // 当前 sku 组合对应的库存
    },
    {
     
      id: 2259, // skuId,下单时后端需要
      price: 100, // 价格(单位分)
      's-1': '1', // 规格类目 k_s 为 s1 的对应规格值 id
      's-2': '4', // 规格类目 k_s 为 s2 的对应规格值 id
      stock_num: 130 // 当前 sku 组合对应的库存
    }
  ],
  price: '1.00', // 默认价格(单位元)
  stock_num: 227, // 商品总库存
  }
  


}
添加点击事件,控制商品规格的显示,showSku为true弹出,,showSku为flase收起
 methods:{
         
 		//商品规格按钮 
        onBuyClicked:function(){
     
            // 将当前的内容提交到订单页
            this.showSku = false;
        },
        //购物车按钮
         chooseSku(){
     
            this.showSku = true;
        }
把数据库数据商品图片和价格放进去sku里面
async created(){
          
		//把商品图片名字从数据放在页面上
       this.goods.picture = this.info.primary_pic_url;
       this.goods.title = this.info.name;
       //修改价格
        this.sku.price = this.info.retail_price;
        //修改剩余数
        this.sku.stock_num = this.info.goods_number;
     
	}

vue之仿网易严选详解_第55张图片

从specificationList拿到数据后,进行循环再赋值给sku(其他商品specificationList可能是空,这会导致sku无效,) 下图的商品specificationList有数据

vue之仿网易严选详解_第56张图片

vue之仿网易严选详解_第57张图片

  async created(){
     
        this.$store.dispatch('AjaxCart');
       let res = await axios.get(this.$root.api.GoodsDetail,{
     params:{
     id:this.id}})
       console.log(res.data)
       this.data = res.data.data;
       this.info = this.data.info;
       this.attribute = this.data.attribute;
       this.goods.picture = this.info.primary_pic_url;
       this.goods.title = this.info.name;
       this.sku.price = this.info.retail_price;
       this.sku.stock_num = this.info.goods_number;

//将数据进行循环
       let tree = []
       this.data.specificationList.forEach((item,index)=>{
     
           let arr = []
           item.valueList.forEach((product,i)=>{
     
               arr.push({
     
                    id: product.id, // skuValueId:规格值 id
                    name: product.value, // skuValueName:规格值名称
                })
           })
//	把数据放到sku里面
           tree.push({
     
                k: item.name, // skuKeyName:规格类目名称
                v: arr,
                k_s: "s-"+item.specification_id // skuKeyStr:sku 组合列表(下方 list)中当前类目对应的 key 值,value 值会是从属于当前类目的一个规格值 id
            })
       })
       this.sku.tree = tree;
       
    }
}
然后再添加list数组,不然商品规格无法选取,变成灰色
let sku ={
     
list: [
    {
     
      id: 2259, // skuId
      s1: '1', // 规格类目 k_s 为 s1 的对应规格值 id
      s2: '1', // 规格类目 k_s 为 s2 的对应规格值 id
      price: 100, // 价格(单位分)
      stock_num: 110 // 当前 sku 组合对应的库存
    },
    {
     
      id: 2259, // skuId
      's-1': '2', // 规格类目 k_s 为 s1 的对应规格值 id
      's-2': '3', // 规格类目 k_s 为 s2 的对应规格值 id
      price: 110, // 价格(单位分)
      stock_num: 110 // 当前 sku 组合对应的库存
    },
    {
     
      id: 2259, // skuId
      's-1': '1', // 规格类目 k_s 为 s1 的对应规格值 id
      's-2': '4', // 规格类目 k_s 为 s2 的对应规格值 id
      price: 110, // 价格(单位分)
      stock_num: 130 // 当前 sku 组合对应的库存
    }
  ],
  }

vue之仿网易严选详解_第58张图片

3、把商品数据放进购物车
点击事件获取回调函数,获取商品规格
vue之仿网易严选详解_第59张图片
vue之仿网易严选详解_第60张图片

onAddCartClicked(skuData){
     
                console.log(skuData)
         }

vue之仿网易严选详解_第61张图片

获取goods_id、goods_number…

onAddCartClicked(skuData){
     
            this.showSku = false;
            // console.log(skuData)
            let chooseContent = skuData.selectedSkuComb['s-1']+'_'+skuData.selectedSkuComb['s-2']
            console.log(chooseContent)
            let resultPro = this.data.productList.filter((item,index)=>{
     
                    //判断选择的内容是否等于服务器内容。
                if(item.goods_specification_ids==chooseContent){
     
                    return true
                }else{
     
                    return false
                }
            })
            console.log(resultPro)
        
    },

vue之仿网易严选详解_第62张图片

加入购物车的数量selectedNum
vue之仿网易严选详解_第63张图片

把商品规格数据通过ajax发到后端(如果用户发送数据,会显示用户的特定值)

let cartRes = await axios.post(this.$root.api.CartAdd,{
      goodsId:resultPro[0].goods_id , number: skuData.selectedNum, productId: resultPro[0].id })
apj接口

vue之仿网易严选详解_第64张图片

let cartRes = await axios.post(this.$root.api.CartAdd,{
      goodsId:resultPro[0].goods_id , number: skuData.selectedNum, productId: resultPro[0].id })
            let data = cartRes.data.data
            console.log(cartRes.data)
商品的数量,总价格

vue之仿网易严选详解_第65张图片

将购物车数据放到全局使用

//mall/scr/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

import api from '@/assets/config/api.js'
import axios from 'axios'
export default new Vuex.Store({
     
  state: {
     
    cartTotal:{
     
      goodsCount: 0,
      goodsAmount: 0,
      checkedGoodsCount: 0,
      checkedGoodsAmount: 0
    },
    cartList:[],

  },
  mutations: {
     
    setCarList:function(state,cartList){
     
      state.cartList = cartList
    },
    setCartTotal:function(state,cartTotal){
     
      state.cartTotal = cartTotal
    }
  },
  actions: {
     
    AjaxCart:async function(content){
     
      let cartRes = await axios.get(api.CartList)
      // console.log(cartRes.data)
      content.commit('setCarList',cartRes.data.data.cartList)
      content.commit('setCartTotal',cartRes.data.data.cartTotal)
    }
  },
  modules: {
     
  }
})

		//三元运算
            <van-goods-action-icon :info="$store.state.cartTotal.goodsCount==0?'':$store.state.cartTotal.goodsCount" icon="cart-o" text="购物车" />


export default {
     
  methods:{
     
  async onAddCartClicked(skuData){
     
     // 修改提交
            let data = cartRes.data.data
            this.$store.commit('setCarList',data.cartList),
            this.$store.commit('setCartTotal',data.cartTotal)
            }

async created(){
     
  			 // 调用mall/scr/store/index.js的AjaxCart
        this.$store.dispatch('AjaxCart');
  			}
          }
            
这是点击加入购物车,选择商品规格,这样就能将总商品数显示在购物车上

vue之仿网易严选详解_第66张图片

六、购物车列表

1、新建购物车页面

//mall/src//views/buycart.vue
<template>
    <div id="buycart">
        <div class='info'>
            <span>30天无忧退货</span>
            <span>48小时快速退款</span>
            <span>88元免邮费</span>
        </div>
       
    </div>
</template>

style lang="less">
//mall/src//views/product.vue
    #buycart{
     
        .info{
     
            display: flex;
            justify-content: space-around;
            font-size: 12px;
            color: #999;
            height: 24px;
            line-height: 24px;
            background: #efefef;
            span{
     
                position: relative;
            }
            span::before{
     
                content:"";
                display: block;
                position: absolute;
                left: -10px;
                top: 8px;
                width: 4px;
                height: 4px;
                border-radius: 2px;
                border: 1px solid red;

            }
        }
    }

</style>
在product.vue页面商品导航加上进入购物车页面的入口
//mall/src//views/product.vue
<van-goods-action-icon to="/buycart" :info="$store.state.cartTotal.goodsCount==0?'':$store.state.cartTotal.goodsCount" icon="cart-o" text="购物车" />
设置路由
//mall/src/router/index.js

import buycart from '@/views/buycart'

{
     
    path:"/buycart",
    name:"buycart",
    component:buycart
  }

2、获取购物车的商品规格数据

//mall/src//views/buycart.vue
//导入//mall/scr/store/index.js数据
import {
     mapState} from 'vuex';
import axios from 'axios'

//导入mall/scr/store/index.js里面的'cartTotal','cartList'
let mapStateObj = mapState(['cartTotal','cartList'])
export default {
     
    data(){
     
        return {
     
        }
    },
     computed:{
     
     
     //解析构造
        ...mapStateObj,
    },
    created(){
     
    //获取mall/scr/store/index.js里面的'AjaxCart'数据
        this.$store.dispatch('AjaxCart')
    },
    mounted(){
     
    	//输出mall/scr/store/index.js的cartList
        console.log(this.cartList)
    },

}

输出this.cartList(点击购物车才会出来,刷新页面没有反应)

vue之仿网易严选详解_第67张图片

3、列表

Card 卡片 https://vant-contrib.gitee.io/vant/#/zh-CN/card

vue之仿网易严选详解_第68张图片

Checkbox 复选框
https://vant-contrib.gitee.io/vant/#/zh-CN/checkbox
vue之仿网易严选详解_第69张图片

把cartList的数据渲染到页面上

  <div class="list">
            <div class="cartItem" v-for="(item,index) in cartList" :key="index">
           // 复选框
                <van-checkbox v-model="item.checked"></van-checkbox>
                //fit='cover'填充模式
                <van-image
                    fit='cover'
                    width="70"
                    height="70"
                    :src="item.list_pic_url"
                />
                <div class="proBrief">
                    <div class="title">
                        
                        <span>{
     {
     item.goods_name}}</span> 
                        <span class="num">X{
     {
     item.number}}</span>
                        </div>
                    <p class="brief">{
     {
     item.goods_specifition_name_value}}</p>
                    <p class="price">{
     {
     item.retail_price}}</p>
                </div>
            </div>
        </div>

<style lang="less">
    #buycart{
     
        .info{
     
            display: flex;
            justify-content: space-around;
            font-size: 12px;
            color: #999;
            height: 24px;
            line-height: 24px;
            background: #efefef;
            span{
     
                position: relative;
            }
            span::before{
     
                content:"";
                display: block;
                position: absolute;
                left: -10px;
                top: 8px;
                width: 4px;
                height: 4px;
                border-radius: 2px;
                border: 1px solid red;

            }
        }
        .cartItem{
     
            padding: 0 5px;
            display: flex;
            align-items: center;
            padding: 10px 5px;
            .van-checkbox{
     
                margin: 0 5px;
            }
            .van-image{
     
                background: #efefef;
            }
            .proBrief{
     
                flex: 1;
                display: flex;
                flex-direction: column;
                justify-content: space-between;
                align-items: flex-start;
                height: 70px;
                padding: 0 10px;
                .title{
     
                    width: 100%;
                    font-size: 14px;
                    display: flex;
                    justify-content: space-between;
                }
                .brief{
     
                    color: #999;
                    font-size: 12px;
                }
            }

        }
    }

</style>

vue之仿网易严选详解_第70张图片

把数据渲染到提交订单上
  <van-submit-bar
            :price="cartTotal.checkedGoodsAmount*100"
            button-text="提交订单"
            @submit="onSubmit"
            >
            <van-checkbox v-model="checkedAll">全选</van-checkbox>
            
        </van-submit-bar>

vue之仿网易严选详解_第71张图片

SubmitBar 提交订单栏
https://vant-contrib.gitee.io/vant/#/zh-CN/submit-bar

vue之仿网易严选详解_第72张图片


<van-checkbox v-model="checkedAll">全选</van-checkbox>


  computed:{
     
        checkedAll:{
     
            set(val){
     
                console.log(val,'设置全选')
            },
            get(){
     
            //选中的数量goodsCount是否==checkedGoodsCount
                if(this.cartTotal.goodsCount==this.cartTotal.checkedGoodsCount){
     
                    return true;
                }else{
     
                    return false;
                }
            }
        },
        ...mapStateObj,
    },

vue之仿网易严选详解_第73张图片
vue之仿网易严选详解_第74张图片

给复选框添加点击事件
<van-checkbox @change="checkEvent($event)" v-model="item.checked"></van-checkbox>


checkEvent:async function(event){
     
            console.log(event)

        }

vue之仿网易严选详解_第75张图片

在@change="checkEvent"里传入参数

               <van-checkbox @change="checkEvent($event,item)" v-model="item.checked"></van-checkbox>

checkEvent:async function(event,item){
     
            console.log(event)
			console.log(item)
        }
输出item,其中checked为true,表示选中

vue之仿网易严选详解_第76张图片

修改发送并储存isChecked,productIds值。isChecked改为1或者0
   checkEvent:async function(event,item){
     
            // console.log(event)
            // console.log(item)
            let res = await axios.post(this.$root.api.CartChecked,{
     isChecked: Number(event),productIds: item.product_id})
            let data = res.data.data
            console.log(data)
            this.$store.commit('setCarList',data.cartList),
            this.$store.commit('setCartTotal',data.cartTotal)
        }
这样页面刷新复选框的选择也不会改变

vue之仿网易严选详解_第77张图片

选择的商品总价也会自动更新

vue之仿网易严选详解_第78张图片

你可能感兴趣的:(html,less,vue)