• 07.智慧商城——商品详情页、加入购物车、拦截器封装token


    01. 商品详情 - 静态布局

    在这里插入图片描述

    静态结构 和 样式

    <template>
      <div class="prodetail">
        <van-nav-bar fixed title="商品详情页" left-arrow @click-left="$router.go(-1)" />
    
        <van-swipe :autoplay="3000" @change="onChange">
          <van-swipe-item v-for="(image, index) in images" :key="index">
            <img :src="image" />
          van-swipe-item>
    
          <template #indicator>
            <div class="custom-indicator">{{ current + 1 }} / {{ images.length }}div>
          template>
        van-swipe>
    
        
        <div class="info">
          <div class="title">
            <div class="price">
              <span class="now">¥0.01span>
              <span class="oldprice">¥6699.00span>
            div>
            <div class="sellcount">已售1001件div>
          div>
          <div class="msg text-ellipsis-2">
            三星手机 SAMSUNG Galaxy S23 8GB+256GB 超视觉夜拍系统 超清夜景 悠雾紫 5G手机 游戏拍照旗舰机s23
          div>
    
          <div class="service">
            <div class="left-words">
              <span><van-icon name="passed" />七天无理由退货span>
              <span><van-icon name="passed" />48小时发货span>
            div>
            <div class="right-icon">
              <van-icon name="arrow" />
            div>
          div>
        div>
    
        
        <div class="comment">
          <div class="comment-title">
            <div class="left">商品评价 (5条)div>
            <div class="right">查看更多 <van-icon name="arrow" /> div>
          div>
          <div class="comment-list">
            <div class="comment-item" v-for="item in 3" :key="item">
              <div class="top">
                <img src="http://cba.itlike.com/public/uploads/10001/20230321/a0db9adb2e666a65bc8dd133fbed7834.png" alt="">
                <div class="name">神雕大侠div>
                <van-rate :size="16" :value="5" color="#ffd21e" void-icon="star" void-color="#eee"/>
              div>
              <div class="content">
                质量很不错 挺喜欢的
              div>
              <div class="time">
                2023-03-21 15:01:35
              div>
            div>
          div>
        div>
    
        
        <div class="desc">
          <img src="https://uimgproxy.suning.cn/uimg1/sop/commodity/kHgx21fZMWwqirkMhawkAw.jpg" alt="">
          <img src="https://uimgproxy.suning.cn/uimg1/sop/commodity/0rRMmncfF0kGjuK5cvLolg.jpg" alt="">
          <img src="https://uimgproxy.suning.cn/uimg1/sop/commodity/2P04A4Jn0HKxbKYSHc17kw.jpg" alt="">
          <img src="https://uimgproxy.suning.cn/uimg1/sop/commodity/MT4k-mPd0veQXWPPO5yTIw.jpg" alt="">
        div>
    
        
        <div class="footer">
          <div class="icon-home">
            <van-icon name="wap-home-o" />
            <span>首页span>
          div>
          <div class="icon-cart">
            <van-icon name="shopping-cart-o" />
            <span>购物车span>
          div>
          <div class="btn-add">加入购物车div>
          <div class="btn-buy">立刻购买div>
        div>
      div>
    template>
    
    <script>
    export default {
      name: 'ProDetail',
      data () {
        return {
          images: [
            'https://img01.yzcdn.cn/vant/apple-1.jpg',
            'https://img01.yzcdn.cn/vant/apple-2.jpg'
          ],
          current: 0
        }
      },
      methods: {
        onChange (index) {
          this.current = index
        }
      }
    }
    script>
    
    <style lang="less" scoped>
    .prodetail {
      padding-top: 46px;
      ::v-deep .van-icon-arrow-left {
        color: #333;
      }
      img {
        display: block;
        width: 100%;
      }
      .custom-indicator {
        position: absolute;
        right: 10px;
        bottom: 10px;
        padding: 5px 10px;
        font-size: 12px;
        background: rgba(0, 0, 0, 0.1);
        border-radius: 15px;
      }
      .desc {
        width: 100%;
        overflow: scroll;
        ::v-deep img {
          display: block;
          width: 100%!important;
        }
      }
      .info {
        padding: 10px;
      }
      .title {
        display: flex;
        justify-content: space-between;
        .now {
          color: #fa2209;
          font-size: 20px;
        }
        .oldprice {
          color: #959595;
          font-size: 16px;
          text-decoration: line-through;
          margin-left: 5px;
        }
        .sellcount {
          color: #959595;
          font-size: 16px;
          position: relative;
          top: 4px;
        }
      }
      .msg {
        font-size: 16px;
        line-height: 24px;
        margin-top: 5px;
      }
      .service {
        display: flex;
        justify-content: space-between;
        line-height: 40px;
        margin-top: 10px;
        font-size: 16px;
        background-color: #fafafa;
        .left-words {
          span {
            margin-right: 10px;
          }
          .van-icon {
            margin-right: 4px;
            color: #fa2209;
          }
        }
      }
    
      .comment {
        padding: 10px;
      }
      .comment-title {
        display: flex;
        justify-content: space-between;
        .right {
          color: #959595;
        }
      }
    
      .comment-item {
        font-size: 16px;
        line-height: 30px;
        .top {
          height: 30px;
          display: flex;
          align-items: center;
          margin-top: 20px;
          img {
            width: 20px;
            height: 20px;
          }
          .name {
            margin: 0 10px;
          }
        }
        .time {
          color: #999;
        }
      }
    
      .footer {
        position: fixed;
        left: 0;
        bottom: 0;
        width: 100%;
        height: 55px;
        background-color: #fff;
        border-top: 1px solid #ccc;
        display: flex;
        justify-content: space-evenly;
        align-items: center;
        .icon-home, .icon-cart {
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          font-size: 14px;
          .van-icon {
            font-size: 24px;
          }
        }
        .btn-add,
        .btn-buy {
          height: 36px;
          line-height: 36px;
          width: 120px;
          border-radius: 18px;
          background-color: #ffa900;
          text-align: center;
          color: #fff;
          font-size: 14px;
        }
        .btn-buy {
          background-color: #fe5630;
        }
      }
    }
        
    .tips {
      padding: 10px;
    }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252

    LazyloadVue 指令,使用前需要对指令进行注册。

    import { Lazyload } from 'vant'
    Vue.use(Lazyload)
    
    • 1
    • 2

    02. 商品详情 - 动态渲染介绍

    1. 动态路由参数,获取商品 id
    computed: {
      goodsId () {
        return this.$route.params.id
      }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 封装 api 接口 api/product.js
    // 获取商品详情数据
    export const getProDetail = (goodsId) => {
      return request.get('/goods/detail', {
        params: {
          goodsId
        }
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 一进入页面发送请求,获取商品详情数据
    data () {
      return {
        images: [
          'https://img01.yzcdn.cn/vant/apple-1.jpg',
          'https://img01.yzcdn.cn/vant/apple-2.jpg'
        ],
        current: 0,
        detail: {},
      }
    },
    
    async created () {
      this.getDetail()
    },
    
    methods: {
      ...
      async getDetail () {
        const { data: { detail } } = await getProDetail(this.goodsId)
        this.detail = detail
        this.images = detail.goods_images
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    1. 动态渲染
    <div class="prodetail" v-if="detail.goods_name">
    
    <van-swipe :autoplay="3000" @change="onChange">
      <van-swipe-item v-for="(image, index) in images" :key="index">
        <img v-lazy="image.external_url" />
      van-swipe-item>
    
      <template #indicator>
        <div class="custom-indicator">{{ current + 1 }} / {{ images.length }}div>
      template>
    van-swipe>
    
    
    <div class="info">
      <div class="title">
        <div class="price">
          <span class="now">¥{{ detail.goods_price_min }}span>
          <span class="oldprice">¥{{ detail.goods_price_max }}span>
        div>
        <div class="sellcount">已售{{ detail.goods_sales }}件div>
      div>
      <div class="msg text-ellipsis-2">
        {{ detail.goods_name }}
      div>
    
      <div class="service">
        <div class="left-words">
          <span><van-icon name="passed" />七天无理由退货span>
          <span><van-icon name="passed" />48小时发货span>
        div>
        <div class="right-icon">
          <van-icon name="arrow" />
        div>
      div>
    div>
    
    
    <div class="tips">商品描述div>
    <div class="desc" v-html="detail.content">div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    03. 商品详情 - 动态渲染评价

    1. 封装接口 api/product.js
    // 获取商品评价
    export const getProComments = (goodsId, limit) => {
      return request.get('/comment/listRows', {
        params: {
          goodsId,
          limit
        }
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 页面调用获取数据
    import defaultImg from '@/assets/default-avatar.png'
    
    data () {
      return {
        ...
        total: 0,
        commentList: [],
        defaultImg
    },
    
    async created () {
      ...
      this.getComments()
    },
        
    async getComments () {
      const { data: { list, total } } = await getProComments(this.goodsId, 3)
      this.commentList = list
      this.total = total
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 动态渲染评价
    <!-- 商品评价 -->
    <div class="comment" v-if="total > 0">
      <div class="comment-title">
        <div class="left">商品评价 ({{ total }})</div>
        <div class="right">查看更多 <van-icon name="arrow" /> </div>
      </div>
      <div class="comment-list">
        <div class="comment-item" v-for="item in commentList" :key="item.comment_id">
          <div class="top">
            <img :src="item.user.avatar_url || defaultImg" alt="">
            <div class="name">{{ item.user.nick_name }}</div>
            <van-rate :size="16" :value="item.score / 2" color="#ffd21e" void-icon="star" void-color="#eee"/>
          </div>
          <div class="content">
            {{ item.content }}
          </div>
          <div class="time">
            {{ item.create_time }}
          </div>
        </div> 
      </div>
    </div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    04. 加入购物车 - 唤起弹窗

    在这里插入图片描述

    1. 按需导入 van-action-sheet
    import { ActionSheet } from 'vant'
    Vue.use(ActionSheet)
    
    • 1
    • 2
    1. 准备 van-action-sheet 基本结构
    <van-action-sheet v-model="showPannel" :title="mode === 'cart' ? '加入购物车' : '立刻购买'">
    	弹窗
    van-action-sheet>
        
    data () {
      return {
        ...
        mode: 'cart'
        showPannel: false
      }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 注册点击事件,点击时唤起弹窗
    <div class="btn-add" @click="addFn">加入购物车</div>
    <div class="btn-buy" @click="buyFn">立刻购买</div>
    
    addFn () {
      this.mode = 'cart'
      this.showPannel = true
    },
    buyFn () {
      this.mode = 'buyNow'
      this.showPannel = true
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 完善结构
    <van-action-sheet v-model="showPannel" :title="mode === 'cart' ? '加入购物车' : '立刻购买'">
      <div class="product">
        <div class="product-title">
          <div class="left">
            <img src="http://cba.itlike.com/public/uploads/10001/20230321/8f505c6c437fc3d4b4310b57b1567544.jpg" alt="">
          div>
          <div class="right">
            <div class="price">
              <span>¥span>
              <span class="nowprice">9.99span>
            div>
            <div class="count">
              <span>库存span>
              <span>55span>
            div>
          div>
        div>
        <div class="num-box">
          <span>数量span>
          数字框占位
        div>
        <div class="showbtn" v-if="true">
          <div class="btn" v-if="true">加入购物车div>
          <div class="btn now" v-else>立刻购买div>
        div>
        <div class="btn-none" v-else>该商品已抢完div>
      div>
    van-action-sheet>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    .product {
      .product-title {
        display: flex;
        .left {
          img {
            width: 90px;
            height: 90px;
          }
          margin: 10px;
        }
        .right {
          flex: 1;
          padding: 10px;
          .price {
            font-size: 14px;
            color: #fe560a;
            .nowprice {
              font-size: 24px;
              margin: 0 5px;
            }
          }
        }
      }
    
      .num-box {
        display: flex;
        justify-content: space-between;
        padding: 10px;
        align-items: center;
      }
    
      .btn, .btn-none {
        height: 40px;
        line-height: 40px;
        margin: 20px;
        border-radius: 20px;
        text-align: center;
        color: rgb(255, 255, 255);
        background-color: rgb(255, 148, 2);
      }
      .btn.now {
        background-color: #fe5630;
      }
      .btn-none {
        background-color: #cccccc;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    1. 动态渲染
    <van-action-sheet v-model="showPannel" :title="mode === 'cart' ? '加入购物车' : '立刻购买'">
      <div class="product">
        <div class="product-title">
          <div class="left">
            <img :src="detail.goods_image" alt="">
          div>
          <div class="right">
            <div class="price">
              <span>¥span>
              <span class="nowprice">{{ detail.goods_price_min }}span>
            div>
            <div class="count">
              <span>库存span>
              <span>{{ detail.stock_total }}span>
            div>
          div>
        div>
        <div class="num-box">
          <span>数量span>
          数字框组件
        div>
        <div class="showbtn" v-if="detail.stock_total > 0">
          <div class="btn" v-if="mode === 'cart'">加入购物车div>
          <div class="btn now" v-if="mode === 'buyNow'">立刻购买div>
        div>
        <div class="btn-none" v-else>该商品已抢完div>
      div>
    van-action-sheet>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    05. 加入购物车 - 封装数字框组件

    在这里插入图片描述

    1. 封装组件 components/CountBox.vue
    <template>
      <div class="count-box">
        <button @click="handleSub" class="minus">-button>
        <input :value="value" @change="handleChange" class="inp" type="text">
        <button @click="handleAdd" class="add">+button>
      div>
    template>
    
        
    <script>
    export default {
      props: {
        value: {
          type: Number,
          default: 1
        }
      },
      methods: {
        handleSub () {
          if (this.value <= 1) {
            return
          }
          this.$emit('input', this.value - 1)
        },
        handleAdd () {
          this.$emit('input', this.value + 1)
        },
        handleChange (e) {
          // console.log(e.target.value)
          const num = +e.target.value // 转数字处理 (1) 数字 (2) NaN
    
          // 输入了不合法的文本 或 输入了负值,回退成原来的 value 值
          if (isNaN(num) || num < 1) {
            e.target.value = this.value
            return
          }
    
          this.$emit('input', num)
        }
      }
    }
    script>
        
    
    <style lang="less" scoped>
    .count-box {
      width: 110px;
      display: flex;
      .add, .minus {
        width: 30px;
        height: 30px;
        outline: none;
        border: none;
        background-color: #efefef;
      }
      .inp {
        width: 40px;
        height: 30px;
        outline: none;
        border: none;
        margin: 0 5px;
        background-color: #efefef;
        text-align: center;
      }
    }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    1. 使用组件
    import CountBox from '@/components/CountBox.vue'
    
    export default {
      name: 'ProDetail',
      components: {
        CountBox
      },
      data () {
        return {
          addCount: 1
          ...
        }
      },
    }
    
    <div class="num-box">
      <span>数量</span>
      <CountBox v-model="addCount"></CountBox>
    </div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    06. 加入购物车 - 判断 token 登录提示

    说明:加入购物车,是一个登录后的用户才能进行的操作,所以需要进行鉴权判断,判断用户 token 是否存在

    1. 若存在:继续加入购物车操作
    2. 不存在:提示用户未登录,引导到登录页

    1. 按需注册 dialog 组件
    import { Dialog } from 'vant'
    Vue.use(Dialog)
    
    • 1
    • 2
    1. 按钮注册点击事件
    <div class="btn" v-if="mode === 'cart'" @click="addCart">加入购物车div>
    
    • 1
    1. 添加 token 鉴权判断,跳转携带回跳地址
    async addCart () {
      // 判断用户是否有登录,因为这里要根据用户的选择判断是否要跳转到登录页面,我们之前在路由前置守卫中设置过拦截路径为:['/pay', '/myorder'],
      //这里是用户访问这两个页面直接拦截前往登录页面,并没有询问用户的意见,所以不能直接在前置守卫中进行拦截。
      if (!this.$store.getters.token) {
        this.$dialog.confirm({
          title: '温馨提示',
          message: '此时需要先登录才能继续操作哦',
          confirmButtonText: '去登录',
          cancelButtonText: '再逛逛'
        })
          .then(() => {
            //注意这里使用replace而不是使用push,push会新增浏览记录,而replace则是替换浏览记录,A页面replaceB时,A的浏览纪律会被B替换
            this.$router.replace({
              path: '/login',
              query: {
                backUrl: this.$route.fullPath
              }
            })
          })
          .catch(() => {})
        return
      }
      console.log('进行加入购物车操作')
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. 登录后,若有回跳地址,则回跳页面
    // 判断有无回跳地址
    const url = this.$route.query.backUrl || '/'
    this.$router.replace(url)
    
    • 1
    • 2
    • 3

    07. 加入购物车 - 封装接口进行请求

    在这里插入图片描述

    1. 封装接口 api/cart.js
    // 加入购物车,goodSkulId为商品的规格,例如颜色,大小
    export const addCart = (goodsId, goodsNum, goodsSkuId) => {
      return request.post('/cart/add', {
        goodsId,
        goodsNum,
        goodsSkuId
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 页面中调用请求
    data () {
      return {
          cartTotal: 0
      }  
    },
    
    async addCart () {
      ...
      const { data } = await addCart(this.goodsId, this.addCount, this.detail.skuList[0].goods_sku_id)
      this.cartTotal = data.cartTotal
      this.$toast('加入购物车成功')
      this.showPannel = false
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    1. 请求拦截器中,统一携带 token
    // 自定义配置 - 请求/响应 拦截器
    // 添加请求拦截器
    instance.interceptors.request.use(function (config) {
      ...
      const token = store.getters.token
      if (token) {
        config.headers['Access-Token'] = token
        config.headers.platform = 'H5'
      }
      return config
    }, function (error) {
      // 对请求错误做些什么
      return Promise.reject(error)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 准备小图标
    <div class="icon-cart">
      <span v-if="cartTotal > 0" class="num">{{ cartTotal }}span>
      <van-icon name="shopping-cart-o" />
      <span>购物车span>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 定制样式
    .footer .icon-cart {
      position: relative;
      padding: 0 6px;
      .num {
        z-index: 999;
        position: absolute;
        top: -2px;
        right: 0;
        min-width: 16px;
        padding: 0 4px;
        color: #fff;
        text-align: center;
        background-color: #ee0a24;
        border-radius: 50%;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    如何在数据库中存储小数:FLOAT、DECIMAL还是BIGINT?
    数商云S2B2C商城积分商城功能如何实现家用电器企业营销价值最大化?
    博客后台模块
    【漏洞复现】Nacos系列多漏洞复现
    Apahce虚拟主机配置演示
    SpringBoot-打印请求的入参和出参
    OpenShift 4 - 应用不间断,集群滚动升级
    Unity 头顶信息的优化
    Linux | 磁盘结构 | 文件系统认识 | inode | 软硬链接
    分享一个python基于数据可视化的智慧社区服务平台源码
  • 原文地址:https://blog.csdn.net/m0_53951384/article/details/134462752