购物车
Cart

目标 Objective

综合运用 Vue 框架处理购物车相关业务
  1. 模板语法
  2. 响应式数据
  3. 事件处理
  4. 双向绑定
  5. 生命周期函数
  6. 数据侦听
  7. 编程式导航/声明式导航
  8. 状态管理

内容 Content

  1. 购物车仓库设计 cart.js
  2. 购物车组件设计 Cart.vue
  3. 购物车组件运用

回顾 Review

. 略

引言 Introduction

. 略

过程 Procedure

. 遵循先静态后动态的开发原则
. 略

前置 Prerequisite

  1. {{}}、v-bind / :
  2. ref() / reactive()
  3. v-on / @
  4. .stop
  5. v-model
  6. onMounted()
  7. watch() / watchEffect()
  8. <RouterLink>
  9. useRouter()
  10. pinia

购物车仓库 cart.js

. 为避免重复操作添加数据,可以预先准备一些测试数据,如下

. 详情请查看 状态管理应用 - 购物车

const lists = ref([
  { id: 0, title: "coffee", price: 9, discount: 0.2, stock: 10, quantity: 1 },
  { id: 1, title: "cup", price: 5, discount: 0.4, stock: 10, quantity: 1 },
  { id: 2, title: "code", price: 16, discount: 0.1, stock: 10, quantity: 1 },
  { id: 3, title: "cookie", price: 8, discount: 0.5, stock: 10, quantity: 1 },
]);

购物车组件 Cart.vue

操作购物车数据:数据加载、全选、数量调整、结算、下单等

. 详情请查看 侦听综合运用 - 购物车

  1. 购物车UI设计
  2. . 封装为一个组件

    . 固定定位在菜单视图底部;弹性盒子方向调整为垂直;折叠时,由 footer 撑开;展开时,背景设为透明黑

    . header 和 main 折叠时不显示 - 条件渲染

    . header 设置外边距上为自动,撑开购物车

    . main 指定最大高度;信息区可以设置单点溢出...

    . footer 折叠时,背景为空;展开时设置白色

    <div class="cart" :class="{ 'show-cart': isShow }">
      <header v-show="isShow">
        // ...
      </header>
      <main v-show="isShow">
        <div class="cart-item" v-for="item in 4">
          // ...
        </div>
      </main>
      <footer>
        <div class="cont">
          // ...
        </div>
      </footer>
    </div>
    购物车静态折叠效果
    购物车静态展开效果
  3. 数据准备
  4. 全选和单选
  5. 数量增加/减少
  6. 商品价格总数
  7. 商品折扣价格总数
  8. 商品数量总数
  9. 去结算

详情页 DetailsView.vue

内容:加载商品数据并清洗 → 渲染 → 生成购物车数据
  1. 购物车静态效果
  2. . 购物车固定定位在详情页底部;没有封装

    . 基本结构,无数据绑定、无事件

    . 高度同主导航;网格布局;平分空间;保持间隔;其它价格部分单独设置结束对齐

    . 按钮的初始化和相关变量均来自主样式文件 app.css

    <div class="cart">
      <span class="price">¥<span>99</span>起</span>
      <div class="opers">
        <button class="oper dec">-</button>
        <div class="quantity">1</div>
        <button class="oper inc">+</button>
      </div>
      <button class="btn btn-cart">加入购物车</button>
      <button class="btn btn-buy">立即购买</button>
    </div>
    购物车静态效果
  3. 准备数据及清洗
  4. . 在 onMounted 中已经获取到了商品数据;需要准备添加到购物车的数据;

    . 利用解构处理/清洗数据,格式同原始数据保持一致;同名字段,后面的会覆盖前面的

    . 增加一个数量字段 quantity,默认是1

    . 处理规格 specification;规格组件的封装,请参考 组件双向绑定 defineModel - 规格组件 specification

    . 这里没有处理甜点 dessert 和推荐商品 recommend

    // 商品仓库 goods.js 中读取的数据
    const good = ref({})
    // 清洗后的数据,加入到购物车仓库 cart.js 中
    const goodSelected = ref({})
    onMounted(async () => {
      let res = await goodsStore.getById(route.params.id)
      good.value = res
    
      goodSelected.value = {
        ...res,
        quantity: 1,
        specification: [
          { name: 'cup', value: res.specification[0].options.find(item => item.sel == true).label },
          { name: 'ther', value: res.specification[1].options.find(item => item.sel == true).label },
          { name: 'sugar', value: res.specification[2].options.find(item => item.sel == true).label, }],
        dessert: [],
        recommend: []
      }
    })
  5. 增加/减少数量
  6. . 为数量按钮绑定单击事件

    . 利用按钮的 disabled 处理越界;只判断下边界

    . 这里使用内联事件实现

    <div class="opers">
      <button class="oper dec" @click="goodSelected.quantity--" :disabled="goodSelected.quantity <= 1">-</button>
      <div class="quantity">{{ goodSelected.quantity }}</div>
      <button class="oper inc" @click="goodSelected.quantity++">+</button>
    </div>
  7. 处理总价
  8. . 使用计算 computed()处理;也可以使用监听 watch()

    const total = computed(() => {
      return (goodSelected.value.price * (1 - goodSelected.value.discount) * goodSelected.value.quantity).toFixed(2)
    })
  9. 添加到购物车
  10. . 为加入购物车指定事件 addToCarts

    . 创建购物车数据项:增加新字段 productId,保存商品 id;商品原 id 字段可以保留,也可以移除

    . 数据项的 id 可以在这里指定;也可以在购物车仓库中指定;这里由购物车仓库指定

    . 跳转到菜单视图;这里使用替换 replace 模式

    const addToCarts = () => {
      const cartItem = {
        ...goodSelected.value,
        productId: goodSelected.value.id,
      };
      cartStore.addToCarts(cartItem)
      router.replace('/menu')
      alert('Added to carts')
    }
  11. 立即购买
  12. . 略

  13. 更新视图
  14. . 使用响应式数据替换掉上述静态结构中的数据

    . 完善详情页中其它部分

    . 更多效果自行完成

小结 Summary

  1. 组件的运用
  2. 数据的运用
  3. 状态的运用
  4. 样式的运用

作业 Homework

  1. 完善细节设计和逻辑处理
  2. 进一步组件化

参考 Reference

  1. 模板语法 Template Syntax
  2. 响应式数据 Reactivity
  3. 事件 Events
  4. 事件修饰符 Events Modifiers
  5. 生命周期函数 Lifecycle Hooks
  6. 父传子 defineProps()
  7. 组件双向绑定 defineModel
  8. 路由 Router
  9. 路由信息 useRoute()
  10. 编程式导航 useRouter()
  11. 状态管理 Pinia
  12. 弹性盒子布局 Flex
  13. 网格布局 Flex