智慧商城(continue)

news2024/12/23 23:56:51

文章目录

    • 1.静态页面结构准备和动态渲染
    • 2.搜索 - 历史记录管理
        • 1. 写好基础静态页面,可以先往里面加一点假数据
        • 2. 上面基本的渲染直接利用history渲染就可以了
        • 3. 搜索历史基本渲染结束了,开始点击搜索添加历史
        • 4. vant内用v-model=" ",可以快速拿到搜索框的值
        • 5. 往历史记录里面追加,追加到最前面,要用到`onshift`
        • 6. 清空数组,就是把它变成一个空数组,只需要在垃圾桶图标的地方注册一个点击事件,然后在methods中写方法
        • 7. 完成搜索历史的持久化,往storage模块里封装方法就可以了
    • 3.搜索列表 - 静态布局 & 渲染
        • 1.现在这个手机是写死的,不管搜什么都是手机(基于搜索关键字渲染)
        • 2.基于分类页进行渲染
    • 4.商品详情 - 静态布局 & 渲染
        • 1.图片部分
        • 2.商品评价部分(获取接口)
    • 5.加入购物车 - 唤起弹层
    • 6.加入购物车 - 封装数字组件
    • 7.加入购物车 - 判断token登录提示
        • 1.封装接口 api/cart.js
        • 2.页面中调用请求
        • 3.请求拦截器中,统一携带 token
    • 8.构建 vuex cart模块,获取数据存储
    • 10. 购物车 - 封装 getters - 动态计算展示
    • 11. 购物车 - 全选反选功能
    • 12. 购物车 - 数字框修改数量
    • 13. 购物车 - 编辑、删除、空购物车处理
    • 14. 订单结算台

1.静态页面结构准备和动态渲染

van-search是搜索框

van-swipe & van-swipe-item是轮播图

van-grid & van-grid-item是grid布局

2.搜索 - 历史记录管理

目标:构建搜索页面的静态布局,完成历史记录的管理

历史管理的需求:
1.搜索历史基本渲染(展示之前搜索过的标签

2.点击搜索(添加历史)

点击 搜索按钮 或 底下历史记录, 都能进行搜索
①若之前 没有 相同搜索关键字,则直接追加到最前面
②若之前 已有 相同搜索关键字, 将该原有关键字移除,再追加

3.清空历史:添加清空图标, 可以清空历史记录

4.持久化:搜索历史需要持久化,刷新历史不丢失

搜索部分在views/search/index.vue里面写

1. 写好基础静态页面,可以先往里面加一点假数据
<script>
export default {
  name: 'SearchIndex',
  data () {
    return {
      history: ['手机', '白酒', '电视']
    }
  }
}
</script>
2. 上面基本的渲染直接利用history渲染就可以了

在这里插入图片描述

<!-- 搜索历史 -->
    <div class="search-history" v-if="history.length > 0">
    <!-- 上面的 v-if 是,历史的长度大于0,有历史,才去渲染下面的东西 -->
      <div class="title">
        <span>最近搜索</span>
        <van-icon name="delete-o" size="16" />
      </div>
      <div class="list">
        <!-- 内容用v-for循环 -->
        <div v-for ="item in history" :key="item" class="list-item" @click="$router.push('/searchlist')">{{ item }}</div>
      </div>
    </div>
3. 搜索历史基本渲染结束了,开始点击搜索添加历史

给搜索和最近搜索标签添加点击事件,goSearch,在下面添加方法
在这里插入图片描述

<div @click="goSearch">搜索</div>
<div v-for ="item in history" :key="item" class="list-item" @click="goSearch">{{ item }}</div>

methods: {
    goSearch () {
      console.log('进行了搜索')
    }
  }
4. vant内用v-model=" ",可以快速拿到搜索框的值
<van-search v-model="search" show-action placeholder="请输入搜索关键词" clearable>
<div @click="goSearch(search)">搜索</div> <!--把search传过去-->
<div v-for ="item in history" :key="item" class="list-item" @click="goSearch(item)">{{ item }}</div>

data () {
    return {
      search: '',
    }
 },
 methods: {
    goSearch (key) {
      console.log('进行了搜索')
    }
  }
5. 往历史记录里面追加,追加到最前面,要用到onshift
  methods: {
    goSearch (key) {
      // console.log('进行了搜索')
      const index = this.history.indexOf(key) // indexOf的作用是用来查找当前这个key在history里的下标,如果将来真的找到了,便于删除
      if (index !== -1) {
        // 存在相同的项,将原有的关键字移除
        // splice (从哪开始,删除几个,项1,项2)
        this.history.splice(index, 1)
      }
      this.history.unshift(key)
    }
  }
6. 清空数组,就是把它变成一个空数组,只需要在垃圾桶图标的地方注册一个点击事件,然后在methods中写方法
<van-icon @click="clear" name="delete-o" size="16" />

clear () {
    this.history = []
 }
7. 完成搜索历史的持久化,往storage模块里封装方法就可以了

先在storage里面写

const HISTORY_KEY = 'zxy_history_list'

// 获取搜索历史
export const getHiatoryList = () => {
  const result = localStorage.getItem(HISTORY_KEY)
  return result ? JSON.parse(result) : []
}

// 设置搜索历史
export const setHiatoryList = (arr) => {
  localStorage.setItem(HISTORY_KEY, JSON.stringify(arr))
}

然后历史记录应该优先从本地去读,直接调用(看有"👈"的行)

import { getHistoryList, setHistoryList } from '@/utils/storage'👈
export default {
  name: 'SearchIndex',
  data () {
    return {
      search: '',
      history: getHistoryList()// 从本地读取👈
    }
  },
  methods: {
    goSearch (key) {
      // console.log('进行了搜索')
      const index = this.history.indexOf(key) // indexOf的作用是用来查找当前这个key在history里的下标,如果将来真的找到了,便于删除
      if (index !== -1) {
        // 存在相同的项,将原有的关键字移除
        // splice (从哪开始,删除几个,项1,项2)
        this.history.splice(index, 1)
      }
      this.history.unshift(key)

      setHistoryList(this.history)// 本地存👈

	
	// 跳转到搜索列表页
      this.$router.push(`/searchlist?search=${key}`)👈
    },
    clear () {
      this.history = []
      setHistoryList([])👈
    }
  }
}

3.搜索列表 - 静态布局 & 渲染

在这里插入图片描述

1.现在这个手机是写死的,不管搜什么都是手机(基于搜索关键字渲染)

在这里插入图片描述

要去找接口文档

在这里插入图片描述

api/product.js

import requset from '@/utils/request'

// 获取搜索商品列表的数据
export const getProList = (obj) => {
  const { categoryId, goodsName, page } = obj
  return requset.get('/goods/list', {
    params: {
      categoryId,
      goodsName,
      page
    }
  })
}

计算属性,query拿地址栏参数

export default {
  name: 'SearchIndex',
  components: {
    GoodsItem
  },
  computed: {
    // 获取地址栏的搜索关键字
    querySearch () {
      return this.$route.query.search
    }
  }
}

在created里面发请求,拿数据然后渲染

data () {
    return {
      page: 1,
      proList: []
    }
  },
  async created () {
    const { data: { list } } = await getProList({
      goodsName: this.querySearch,
      page: this.page
    })
    this.proList = list.data
  }

list.vue把item传进去,用Goodsitem.vue解析

list.vue

<div class="goods-list">
      <GoodsItem v-for="item in proList" :key="item.goods_id" :item="item"></GoodsItem>
</div>

在这里插入图片描述

2.基于分类页进行渲染

新建api/category.js

import request from '@/utils/request'

// 获取分类数据
export const getCategoryData = () => {
  return request.get('/category/list')
}

list.vue

async created () {
    const { data: { list } } = await getProList({
      categoryId: this.$route.query.categoryId,👈
      goodsName: this.querySearch,
      page: this.page
    })
    this.proList = list.data
  }

4.商品详情 - 静态布局 & 渲染

在这里插入图片描述

1.图片部分
product.js

// 获取商品详情数据
export const getProDetail = (goodsId) => {
  return requset.get('/goods/detail', {
    params: {
      goodsId
    }
  })
}
prodetail/index.vue

<van-swipe :autoplay="3000" @change="onChange">
      <van-swipe-item v-for="(image, index) in images" :key="index">
        <img :src="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>


data () {
    return {
      images: [],
      current: 0,
      detail: {}
    }
  },
  computed: {
    goodsId () {
      return this.$route.params.id
    }
  },
  created () {
    this.getDetail()
  },
  methods: {
    onChange (index) {
      this.current = index
    },
    async getDetail () {
      const { data: { detail } } = await getProDetail(this.goodsId)
      this.detail = detail
      this.images = detail.goods_images
      console.log(this.images)
    }
  }

在这里插入图片描述

商品描述部分不能用{{ }},因为里面包含p标签

<!-- 商品描述 -->
    <div class="desc" v-html="detail.content">
    </div>
2.商品评价部分(获取接口)
product.js

// 获取商品评价
export const getProComments = (goodsId, limit) => {
  return request.get('/comment/listRows', {
    params: {
      goodsId,
      limit
    }
  })
}
index.vue

<!-- 商品评价 -->
    <div class="comment">
      <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>

import defaultImg from '@/assets/default-avatar.png'

export default {
  name: 'ProDetail',
  data () {
    return {
      images: [],
      current: 0,
      detail: {},
      total: 0, // 评价总数👈
      commentList: [], // 评价列表👈
      defaultImg👈
    }
  },
  computed: {
    goodsId () {
      return this.$route.params.id
    }
  },
  created () {
    this.getDetail()
    this.getComments()👈
  },
  methods: {
    onChange (index) {
      this.current = index
    },
    async getDetail () {
      const { data: { detail } } = await getProDetail(this.goodsId)
      this.detail = detail
      this.images = detail.goods_images
      console.log(this.images)
    },
    async getComments () {👈
      const { data: { list, total } } = await getProComments(this.goodsId, 3)👈
      this.commentList = list👈
      this.total = total👈
    }👈
  }
}
</script>

在这里插入图片描述

5.加入购物车 - 唤起弹层

在这里插入图片描述
弹层用的是vant中的反馈组件

import { ActionSheet } from 'vant';

Vue.use(ActionSheet);

自定义面板

通过插槽可以自定义面板的展示内容,同时可以使用title属性展示标题栏

<van-action-sheet v-model="show" title="标题">
  <div class="content">内容</div>
</van-action-sheet>

<style>
  .content {
    padding: 16px 16px 160px;
  }
</style>
index.vue

 <!-- 加入购物车的弹层 -->
    <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-else>立刻购买</div>
        </div>
        <div class="btn-none" v-else>该商品已抢完</div>
      </div>
    </van-action-sheet>

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

在这里插入图片描述

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>

prodeatil/index.js

import CountBox from '@/components/CountBox.vue'👈

export default {
  name: 'ProDetail',
  components: {
    CountBox
  },
  data () {
    return {
      images: [],
      current: 0,
      detail: {},
      total: 0, // 评价总数
      commentList: [], // 评价列表
      defaultImg,
      showPannel: false, // 控制弹层的显示隐藏
      mode: 'cart', // 标记弹层状态
      addCount: 1 // 数字框绑定的数据👈
    }
  },

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

在这里插入图片描述

1.封装接口 api/cart.js

// 加入购物车

export const addCart = (goodsId, goodsNum, goodsSkuId) => {
  return request.post('/cart/add', {
    goodsId,
    goodsNum,
    goodsSkuId
  })
}
2.页面中调用请求
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
},
3.请求拦截器中,统一携带 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)
})

8.构建 vuex cart模块,获取数据存储

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

新建 modules/cart.js 模块

export default {
  namespaced: true,
  state () {
    return {
      cartList: []
    }
  },
  mutations: {
  },
  actions: {
  },
  getters: {
  }
}

挂载到 store 上面

import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import cart from './modules/cart'

Vue.use(Vuex)

export default new Vuex.Store({
  getters: {
    token: state => state.user.userInfo.token
  },
  modules: {
    user,
    cart
  }
})

封装 API 接口 api/cart.js

// 获取购物车列表数据
export const getCartList = () => {
  return request.get('/cart/list')
}
封装 action 和 mutation
mutations: {
  setCartList (state, newList) {
    state.cartList = newList
  },
},
actions: {
  async getCartAction (context) {
    const { data } = await getCartList()
    data.list.forEach(item => {
      item.isChecked = true
    })
    context.commit('setCartList', data.list)
  }
},

页面中 dispatch 调用

computed: {
  isLogin () {
    return this.$store.getters.token
  }
},
created () {
  if (this.isLogin) {
    this.$store.dispatch('cart/getCartAction')
  }
},
  1. 购物车 - mapState - 渲染购物车列表
    将数据映射到页面
import { mapState } from 'vuex'

computed: {
  ...mapState('cart', ['cartList'])
}

动态渲染

<!-- 购物车列表 -->
<div class="cart-list">
  <div class="cart-item" v-for="item in cartList" :key="item.goods_id">
    <van-checkbox icon-size="18" :value="item.isChecked"></van-checkbox>
    <div class="show" @click="$router.push(`/prodetail/${item.goods_id}`)">
      <img :src="item.goods.goods_image" alt="">
    </div>
    <div class="info">
      <span class="tit text-ellipsis-2">{{ item.goods.goods_name }}</span>
      <span class="bottom">
        <div class="price">¥ <span>{{ item.goods.goods_price_min }}</span></div>
        <CountBox :value="item.goods_num"></CountBox>
      </span>
    </div>
  </div>
</div>

10. 购物车 - 封装 getters - 动态计算展示

封装 getters:商品总数 / 选中的商品列表 / 选中的商品总数 / 选中的商品总价

getters: {
  cartTotal (state) {
    return state.cartList.reduce((sum, item, index) => sum + item.goods_num, 0)
  },
  selCartList (state) {
    return state.cartList.filter(item => item.isChecked)
  },
  selCount (state, getters) {
    return getters.selCartList.reduce((sum, item, index) => sum + item.goods_num, 0)
  },
  selPrice (state, getters) {
    return getters.selCartList.reduce((sum, item, index) => {
      return sum + item.goods_num * item.goods.goods_price_min
    }, 0).toFixed(2)
  }
}

页面中 mapGetters 映射使用

computed: {
  ...mapGetters('cart', ['cartTotal', 'selCount', 'selPrice']),
},
    
<!-- 购物车开头 -->
<div class="cart-title">
  <span class="all">共<i>{{ cartTotal || 0 }}</i>件商品</span>
  <span class="edit">
    <van-icon name="edit"  />
    编辑
  </span>
</div>


<div class="footer-fixed">
  <div  class="all-check">
    <van-checkbox  icon-size="18"></van-checkbox>
    全选
  </div>
  <div class="all-total">
    <div class="price">
      <span>合计:</span>
      <span>¥ <i class="totalPrice">{{ selPrice }}</i></span>
    </div>
    <div v-if="true" :class="{ disabled: selCount === 0 }" class="goPay">
      结算({{ selCount }})
    </div>
    <div v-else  :class="{ disabled: selCount === 0 }" class="delete">
      删除({{ selCount }})
    </div>
  </div>
</div>

11. 购物车 - 全选反选功能

全选 getters
getters: {
  isAllChecked (state) {
    return state.cartList.every(item => item.isChecked)
  }
}
    
...mapGetters('cart', ['isAllChecked']),

<div class="all-check">
  <van-checkbox :value="isAllChecked" icon-size="18"></van-checkbox>
  全选
</div>

点击小选,修改状态

<van-checkbox @click="toggleCheck(item.goods_id)" ...></van-checkbox>
    
toggleCheck (goodsId) {
  this.$store.commit('cart/toggleCheck', goodsId)
},
    
mutations: {
  toggleCheck (state, goodsId) {
    const goods = state.cartList.find(item => item.goods_id === goodsId)
    goods.isChecked = !goods.isChecked
  },
}

点击全选,重置状态

<div @click="toggleAllCheck" class="all-check">
  <van-checkbox :value="isAllChecked" icon-size="18"></van-checkbox>
  全选
</div>

toggleAllCheck () {
  this.$store.commit('cart/toggleAllCheck', !this.isAllChecked)
},

mutations: {
  toggleAllCheck (state, flag) {
    state.cartList.forEach(item => {
      item.isChecked = flag
    })
  },
}

12. 购物车 - 数字框修改数量

封装 api 接口

// 更新购物车商品数量
export const changeCount = (goodsId, goodsNum, goodsSkuId) => {
  return request.post('/cart/update', {
    goodsId,
    goodsNum,
    goodsSkuId
  })
}

页面中注册点击事件,传递数据

<CountBox :value="item.goods_num" @input="value => changeCount(value, item.goods_id, item.goods_sku_id)"></CountBox>

changeCount (value, goodsId, skuId) {
  this.$store.dispatch('cart/changeCountAction', {
    value,
    goodsId,
    skuId
  })
},

提供 action 发送请求, commit mutation

mutations: {
  changeCount (state, { goodsId, value }) {
    const obj = state.cartList.find(item => item.goods_id === goodsId)
    obj.goods_num = value
  }
},
actions: {
  async changeCountAction (context, obj) {
    const { goodsId, value, skuId } = obj
    context.commit('changeCount', {
      goodsId,
      value
    })
    await changeCount(goodsId, value, skuId)
  },
}

13. 购物车 - 编辑、删除、空购物车处理

data 提供数据, 定义是否在编辑删除的状态

data () {
  return {
    isEdit: false
  }
},

注册点击事件,修改状态

<span class="edit" @click="isEdit = !isEdit">
  <van-icon name="edit"  />
  编辑
</span>

底下按钮根据状态变化

<div v-if="!isEdit" :class="{ disabled: selCount === 0 }" class="goPay">
    去结算({{ selCount }})
</div>
<div v-else :class="{ disabled: selCount === 0 }" class="delete">删除</div>

监视编辑状态,动态控制复选框状态

watch: {
  isEdit (value) {
    if (value) {
      this.$store.commit('cart/toggleAllCheck', false)
    } else {
      this.$store.commit('cart/toggleAllCheck', true)
    }
  }
}

购物车 - 删除功能完成
查看接口,封装 API ( 注意:此处 id 为获取回来的购物车数据的 id )

// 删除购物车
export const delSelect = (cartIds) => {
  return request.post('/cart/clear', {
    cartIds
  })
}

注册删除点击事件

<div v-else :class="{ disabled: selCount === 0 }" @click="handleDel" class="delete">
  删除({{ selCount }})
</div>

async handleDel () {
  if (this.selCount === 0) return
  await this.$store.dispatch('cart/delSelect')
  this.isEdit = false
},

提供 actions

actions: {
    // 删除购物车数据
    async delSelect (context) {
      const selCartList = context.getters.selCartList
      const cartIds = selCartList.map(item => item.id)
      await delSelect(cartIds)
      Toast('删除成功')

      // 重新拉取最新的购物车数据 (重新渲染)
      context.dispatch('getCartAction')
    }
},

购物车 - 空购物车处理
外面包个大盒子,添加 v-if 判断

<div class="cart-box" v-if="isLogin && cartList.length > 0">
  <!-- 购物车开头 -->
  <div class="cart-title">
    ...
  </div>
  <!-- 购物车列表 -->
  <div class="cart-list">
    ...
  </div>
  <div class="footer-fixed">
    ...
  </div>
</div>

<div class="empty-cart" v-else>
  <img src="@/assets/empty.png" alt="">
  <div class="tips">
    您的购物车是空的, 快去逛逛吧
  </div>
  <div class="btn" @click="$router.push('/')">去逛逛</div>
</div>

14. 订单结算台

所谓的 “立即结算”,本质就是跳转到订单结算台,并且跳转的同时,需要携带上对应的订单参数。

而具体需要哪些参数,就需要基于 【订单结算台】 的需求来定。

(1) 静态布局

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2) 获取收货地址列表

  1. 封装获取地址的接口
import request from '@/utils/request'
// 获取地址列表
export const getAddressList = () => {
  return request.get('/address/list')
}
  1. 页面中 - 调用获取地址
data () {
  return {
    addressList: []
  }
},
computed: {
  selectAddress () {
    // 这里地址管理不是主线业务,直接获取默认第一条地址
    return this.addressList[0] 
  }
},
async created () {
  this.getAddressList()
},
methods: {
  async getAddressList () {
    const { data: { list } } = await getAddressList()
    this.addressList = list
  }
}
  1. 页面中 - 进行渲染
computed: {
  longAddress () {
    const region = this.selectAddress.region
    return region.province + region.city + region.region + this.selectAddress.detail
  }
},

<div class="info" v-if="selectAddress?.address_id">
  <div class="info-content">
    <span class="name">{{ selectAddress.name }}</span>
    <span class="mobile">{{ selectAddress.phone }}</span>
  </div>
  <div class="info-address">
    {{ longAddress }}
  </div>
</div>

(3) 订单结算 - 封装通用接口

思路分析 : 这里的订单结算,有两种情况:

购物车结算,需要两个参数

① mode=“cart”

② cartIds=“cartId, cartId”

立即购买结算,需要三个参数

① mode=“buyNow”

② goodsId=“商品id”

③ goodsSkuId=“商品skuId”

都需要跳转时将参数传递过来

封装通用 API 接口 api/order

import request from '@/utils/request'

export const checkOrder = (mode, obj) => {
  return request.get('/checkout/order', {
    params: {
      mode,
      delivery: 0,
      couponId: 0,
      isUsePoints: 0,
      ...obj
    }
  })
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1428936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3.0(一):Vue的引入-options api-模板语法

Vue的引入方式 CDN方式进行引入 将以下 script标签引入即可 <script src"https://unpkg.com/vue3/dist/vue.global.js"></script><!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><met…

oracle19C 密码包含特殊字符@ 导致ORA-12154

oracle 19C 密码包含特殊字符 出现登录失败&#xff0c;针对此问题一次说个明白 ORA-12154: TNS:could not resolve the connect identifier specified Oracle 19c之前密码是可以包含特殊字符&#xff0c;但是如果包含特殊字符需要双引号 比如oracle11g 正常 如果密码包含特殊…

创新大赛专访丨金智维荣膺2023年度数字化创新服务卓越品牌:专注提供企业级RPA平台,重塑企业生产力,让员工更有价值

日前&#xff0c;2023第三届全国人力资源创新大赛颁奖典礼暨成果展圆满举行。自2023年10月份启动以来&#xff0c;大赛共吸引了457个案例报名参赛&#xff0c;经组委会专家团队评审严格审核&#xff0c;企业赛道共有103个案例获奖、72家企业、13位个人、7个产业园斩获荣誉。 珠…

帮管客CRM SQL注入漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

简单实践 java spring boot 自动配置模拟

1.概要 1.1 需求&#xff0c;自己写一个redis-spring-boot-starter模拟自动配置 自动配置就是在引入*-starter坐标后&#xff0c;可以已经spring框架的规则实现一些Bean的自动注入&#xff0c;并设置一些参数的默认值&#xff0c;且也可以在引入的工程中修改这些配置的值。这…

C#代码添加脚本头

目录 前言 代码展示 前言 创建脚本的时候添加脚本的介绍 代码展示 using System.IO;/// <summary> /// 创建脚本自动添加头注 /// </summary> public class CommentFirst : UnityEditor.AssetModificationProcessor {/// <summary>/// 在资源创建生成.me…

springboot整合RabbitMQ,RabbitMQ实现高级特性消息不丢失

1.生产者可靠性消息投递 简单操作参考---------打开主页上篇博客 https://blog.csdn.net/weixin_45810161/article/details/135906602?spm1001.2014.3001.5501 在使用RabbitMQ的时候,怎么保证保证消息不丢失,RabbitMQ提供了两种不同的方式来控制消息的可靠性投递 1.confirm…

js中的数据类型(存储上的差别)

文章目录 前言一、基本类型NumberUndefinedStringNullBooleanSymbol 二、引用类型ObjectArrayFunction其他引用类型 三、存储区别基本类型引用类型 小结 前言 在JavaScript中&#xff0c;我们可以分成两种类型&#xff1a; 基本类型复杂类型 两种类型的区别是&#xff1a;存…

老版本labelme如何不保存imagedata

我的版本是3.16&#xff0c;默认英文且不带取消保存imagedata的选项。 最简单粗暴的方法就是在json文件保存时把传递过来的imagedata数据设定为None&#xff0c;方法如下&#xff1a; 找到labelme的源文件&#xff0c;例如&#xff1a;D:\conda\envs\deeplab\Lib\site-packages…

jsp自助点餐管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 自助点餐管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0…

PCB笔记(二十三):allegro 标注长宽(一般用于测量板宽)时如何显示双单位

步骤&#xff1a;首先选择标注工具&#xff0c;然后右键→Parameters&#xff0c;在弹出来的窗口中√上如下图二所示选项 最终要达到显示单位的效果的话&#xff0c;需要在Text项键入%v%u。 今天就记录到这里啦O

Jmeter直连mysql数据库教程

mysql数据库能够通过Navicat等远程连接工具连接 下载驱动并加入jmeter 1.mysql驱动下载地址&#xff1a;MySQL :: Download MySQL Connector/J (Archived Versions) 找到对应的驱动下载&#xff1a;如下图&#xff1a; 把驱动jar包加入jmeter 配置jmeter连接mysql数据库…

正则表达式与文本处理工具

目录 引言 一、正则表达式基础 &#xff08;一&#xff09;字符匹配 1.基本字符 2.特殊字符 3.量词 4.边界匹配 &#xff08;二&#xff09;进阶用法 1.组与引用 2.选择 二、命令之-----grep &#xff08;一&#xff09;基础用法 &#xff08;二&#xff09;高级用…

数据结构——实验01-线性表的链式存储和操作

一、实验内容 二、算法思想与算法实现 1、解题思想 &#xff08;1&#xff09;逆序创建链表La就是使用头插法创建一个链表&#xff0c;所谓头插法就是在创建链表时始终将新元素插入到头结点之后&#xff0c;而正序创建链表Lb就是使用尾插法创建一个链表&#xff0c;所谓尾插法…

Spring Bean 生命周期常见错误

虽然说 Spring 容器上手简单&#xff0c;可以仅仅通过学习一些有限的注解&#xff0c;即可达到快速使用的目的。但在工程实践中&#xff0c;我们依然会从中发现一些常见的错误。尤其当你对 Spring 的生命周期还没有深入了解时&#xff0c;类初始化及销毁过程中潜在的约定就不会…

AJAX-URL查询参数

定义&#xff1a;浏览器提供给服务器的额外信息&#xff0c;让服务器返回浏览器想要的数据 http://xxxx.com/xxx/xxx?参数名1值1&参数名2值2 axios语法 使用axios提供的params选项 注意&#xff1a;axios在运行时把参数名和值&#xff0c;会拼接到url?参数名值 axios(…

第5课 使用FFmpeg将rtmp流再转推到rtmp服务器

本课对应源文件下载链接&#xff1a; https://download.csdn.net/download/XiBuQiuChong/88801992 通过前面的学习&#xff0c;我们已经可以正常播放网络rtmp流及本地mp4文件。这节课&#xff0c;我们将在前面的基础上实现一个常用的转推功能&#xff1a;读取rtmp流或mp4文件并…

matlab|【EI复现】日前日内多阶段多时间尺度源荷储协调调度

目录 一、模型 二、程序运行 三、下载链接 多阶段多时间尺度的协调调度的优势是考虑新能源出力的波动性与随机性&#xff0c;减少需求响应负荷的不确定性对电网制定的日前调度计划准确性造成的影响&#xff0c;也就是能够更加精准的进行调度和分析&#xff0c;优化结果的可用…

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~

目录 冒泡排序(BubbleSort)&#xff1a; 代码详解&#xff1a; 冒泡排序的优化&#xff1a; 选择排序(SelectSort)&#xff1a; 代码详解&#xff1a; 插入排序&#xff08;InsertSort&#xff09;&#xff1a; 代码详解&#xff1a; 希尔排序(ShellSort)&#xff1a; 法一…

【数据结构与算法】之排序系列-20240202

这里写目录标题 一、389. 找不同二、414. 第三大的数三、455. 分发饼干四、506. 相对名次五、561. 数组拆分六、594. 最长和谐子序列 一、389. 找不同 简单 给定两个字符串 s 和 t &#xff0c;它们只包含小写字母。 字符串 t 由字符串 s 随机重排&#xff0c;然后在随机位置添…