程序员的资源宝库

网站首页 > gitee 正文

vue3项目-小兔鲜儿笔记-登录页02和购物车01

sanyeah 2024-04-13 16:21:05 gitee 4 ℃ 0 评论

1. 登录-消息提示组件封装

组件功能分析:

  • 固定顶部显示,有三种类型:成功、错误、警告

  • 显示消息提示时需要动画从上滑入

  • 组件使用的方式不够便利,封装成工具函数的方式

<template>
  <div class="xtx-message" :style="style[type]">
    <!-- 上面绑定的是样式 -->
    <!-- 不同提示图标会变 -->
    <i class="iconfont" :class="[style[type].icon]"></i>
    <span class="text">{{text}}</span>
  </div>
</template>
<script setup>
import {ref, onMounted} from "vue";

defineProps({
  text: {
    type: String,
    default: ''
  },
  type: {
    type: String,
    // warn 警告, success 成功, error 错误
    default: 'warn'
  }
})

// 定义一个包含三种情况样式的对象
const style = {
  warn: {
    icon: 'icon-warning',
    color: '#e6a23c',
    backgroundColor: 'rgb(253, 246, 236)',
    borderColor: 'rgb(250, 236, 216)'
  },
  error: {
    icon: 'icon-shanchu',
    color: '#F56C6C',
    backgroundColor: 'rgb(254, 240, 240)',
    borderColor: 'rgb(253, 226, 226)'
  },
  success: {
    icon: 'icon-queren2',
    color: '#67C23A',
    backgroundColor: 'rgb(240, 249, 235)',
    borderColor: 'rgb(225, 243, 216)'
  }
}

// 控制消息组件的显示和隐藏
const visible = ref(false)
onMounted(() => {
  visible.value = true
})
</script>
实现从上滑入的效果
.down-enter-from, .down-leave-to {
  transform: translateY(-75px);
  opacity: 0;
}

.down-enter-to, .down-leave-from {
  transform: none;
  opacity: 1;
}

.down-enter-active, .down-leave-active {
  transition: all 0.3s ease;
}
Message.js:封装成工具函数
// 封装消息组件并挂载到app.config.globalProperties中,使得组件可直接调用消息组件
import { createVNode, render } from 'vue'
import XtxMessage from '@/components/library/xtx-message'

// 先创建一个div容器,让消息组件挂载到这个容器上
const div = document.createElement('div')
div.classList.add('xtx-message-container')
document.body.appendChild(div)

// 定时器标识
let timer = null

export default function ({ type, text }) {
  // 将消息组件渲染成vnode
  const vnode = createVNode(XtxMessage, { type, text })
  // 将vnode挂到上述定义的div容器中
  render(vnode, div)
  // 定时器作用:消息显示一段时间后删除
  clearTimeout(timer)
  timer = setTimeout(() => {
    render(null, div)
  }, 3000)
}

main.js
// 挂载消息组件到全局vue对象上
app.config.globalProperties.$message = Message

显示效果:

 

 

2. 购物车功能分析

  • 购物车的各种状态都会有登录和未登录两种状态的区分,这种区分逻辑封装在pinia的actions中,组件不需区分。

  • pinia的actions中的区分:

    • 未登录:直接修改cart模块中的数据即可

    • 已登录:通过api接口去后端操作,响应成功后再修改cart模块中的数据

  • 不管何种状态最后均返回一个promise,让组件去判断操作是否成功。

  • 登录后,需要合并本地购物车到服务端,退出后,清空cart模块的数据也会同步清空本地数据。

 

3. 加入购物车-本地

完成商品详情的添加购物车操作,支持未登录状态

首先约定本地存储的字段信息:

id skuId name attrsText picture price nowPrice selected stock count isEffective

    // 加入购物车
    insertCart(payload) {
      // 约定加入购物车字段必须和后端一致:payload的字段
      // 它们是:id skuId name attrsText picture price nowPrice selected stock count isEffective
      // 插入数据的规则:
      // 1.先找是否有相同的商品
      // 如果有:找到这个商品的数量,累加到payload上,然后删除原来的商品,将payload保存到最新的位置上
      // 如果没有:直接将payload保存到最新的位置上
      const sameIndex = this.list.findIndex(
        (goods) => goods.skuId === payload.skuId
      )
      if (sameIndex > -1) {
        const count = this.list[sameIndex].count
        payload.count += count
        this.list.splice(sameIndex, 1)
      }
      this.list.unshift(payload)
    },

    // 判断登录还是未登录逻辑的加入购物车
    asyncInsertCart(payload) {
      const userStore = useUserStore()
      return new Promise((resolve, reject) => {
        if (userStore.profile.token) {
          // 已登录
        } else {
          // 未登录
          this.insertCart(payload)
          resolve()
        }
      })
    },
商品详情页:
// 加入购物车
const insertCart = () => {
  // 判断是否选择完整的sku
  if (currSku.value && currSku.value.skuId) {
    // 约定字段:id skuId name attrsText picture price nowPrice selected stock count isEffective
    const {skuId, price, inventory: stock, specsText: attrsText} = currSku.value
    const {id, name, mainPictures} = goods.value
    cartStore.asyncInsertCart({
      id,
      skuId,
      name,
      attrsText,
      picture: mainPictures[0],
      price,
      nowPrice: price,
      selected: true,
      stock,
      count: num.value,
      isEffective: true
    }).then(() => {
      Message({type: 'success', text: '成功加入购物车'})
    })
  } else {
    Message({text: '请选择完整的规格信息'})
  }
}

 

4. 头部购物车-商品列表-本地

目的:当进入首页时,根据本地存储的商品获取最新的库存价格和有效状态,更新商品列表信息

    // 一次性获取购物车列表的所有商品并更新商品信息
    findCartList() {
      return new Promise((resolve, reject) => {
        const userStore = useUserStore()
        if (userStore.profile.token) {
          // 已登录
        } else {
          // 未登录
          // Promise.all() 一次发送多个请求,等所有请求完毕后调用then
          // 传参是promise数组
          const promiseList = this.list.map((goods) => {
            // 发送请求返回的是promise
            return getNewCartGoods(goods.skuId)
          })

          Promise.all(promiseList).then((dataList) => {
            // dataList结果和promiseList一一对应
            dataList.forEach((data, i) => {
              this.updateGoods({ skuId: this.list[i].skuId, ...data.result })
            })
          })

          resolve()
        }
      })
    },
// 头部栏组件
<script setup>
import useCartStore from "@/store/cart";
import Message from "@/components/library/Message";

const cartStore = useCartStore()
// 一开始进入首页时就应该更新购物车列表的商品信息
cartStore.findCartList().then(() => {
  Message({type: 'success', text: '更新购物车列表成功'})
})
。。。

 

5. 头部购物车-删除操作-本地

目的:完成头部购物车删除操作,支持未登录状态

    // 通过skuId删除购物车中的商品
    deleteCart(skuId) {
      const index = this.list.findIndex((goods) => goods.skuId === skuId)
      this.list.splice(index, 1)
    },

    //结合登录逻辑和未登录逻辑的删除购物车中的商品
    asyncDeleteCart(skuId) {
      return new Promise((resolve, reject) => {
        const userStore = useUserStore()
        if (userStore.profile.token) {
          // 已登录
        } else {
          // 未登录
          this.deleteCart(skuId)
          resolve()
        }
      })
    }
// 头部栏组件
// 删除购物车中的商品
const deleteCart = (skuId) => {
  cartStore.asyncDeleteCart(skuId).then(() => {
    Message({type: 'success', text: '成功删除商品'})
  }).catch(error => {
    Message({type: 'error', text: '删除商品失败'})
  })
}

 

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表