小兔鲜--项目总结 2

news2024/12/24 11:00:23

目录

登录-表单校验实现

表单如何进行校验

 表单校验步骤

 自定义校验规则

 整个表单的内容验证

 登录-基础登录业务实现

登录业务流程

 Pinia管理用户数据

如何使用Pinia管理数据

 关键代码总结

登录-Pinia用户数据持久化

持久化用户数据说明

​编辑关键步骤总结和插件运行机制

登录-请求拦截器携带Token

为什么要在请求拦截器携带Token

如何配置

登录-退出登录功能实现

退出登录业务实现

购物车功能实现

购物车业务逻辑梳理拆解

 本地购物车 - 加入购物车实现

1. 添加购物车

2. 删除功能实现

3列表购物车-单选功能实现 

4.列表购物车-全选功能实现

5.列表购物车-统计数据功能实现

总结


登录-表单校验实现

为什么需要校验

作用:前端提前校验可以省去一些错误的请求提交,为后端节省接口压力

表单如何进行校验

ElementPlus表单组件内置了表单校验功能,只需要按照组件要求配置必要参数即可(直接看文档)

思想:当功能很复杂时,通过多个组件各自负责某个小功能,再组合成一个大功能是组件设计中的常用方法 高级软件人才培训

 表单校验步骤

 自定义校验规则

ElementPlus表单组件内置了初始的校验配置,应付简单的校验只需要通过配置即可,如果想要定制一些特殊的校验需 求,可以使用自定义校验规则,格式如下:

校验逻辑:如果勾选了协议框,通过校验,如果没有勾选,不通过校验

 整个表单的内容验证

思考:每个表单域都有自己的校验触发事件,如果用户一上来就点击登录怎么办呢? 答:在点击登录时需要对所有需要校验的表单进行统一校验

<script setup>
import { ref } from 'vue'
// 表单数据对象
const userInfo = ref({
  account: '1311111111',
  password: '123456',
  agree: true
})

// 规则数据对象
const rules = {
  account: [
    { required: true, message: '用户名不能为空' }
  ],
  password: [
    { required: true, message: '密码不能为空' },
    { min: 6, max: 24, message: '密码长度要求6-14个字符' }
  ],
  agree: [
    {
      validator: (rule, val, callback) => {
        return val ? callback() : new Error('请先同意协议')
      }
    }
  ]
}


</script>


<template>
    <div class="form">
      <el-form ref="formRef" :model="userInfo" :rules="rules" status-icon>
        <el-form-item prop="account" label="账户">
          <el-input v-model="userInfo.account" />
        </el-form-item>
        <el-form-item prop="password" label="密码">
          <el-input v-model="userInfo.password" />
        </el-form-item>
        <el-form-item prop="agree" label-width="22px">
          <el-checkbox v-model="userInfo.agree" size="large">
            我已同意隐私条款和服务条款
          </el-checkbox>
        </el-form-item>
        <el-button size="large" class="subBtn" >点击登录</el-button>
      </el-form>
    </div>
</template>

 登录-基础登录业务实现

登录业务流程

 Pinia管理用户数据

基本思想:Pinia负责用户数据相关的state和action,组件中只负责触发action函数并传递参数

如何使用Pinia管理数据

遵循理念:和数据相关的所有操作(state + action)都放到Pinia中,组件只负责触发action函数

 关键代码总结

import { defineStore } from 'pinia'
import { ref } from 'vue'
import { loginAPI } from '@/apis/login'
import { mergeCartAPI } from '@/apis/cart'
import { useCartStore } from '@/stores/cart'


export const useUserStore = defineStore('user', () => {
    // 1. 定义管理用户数据的state
    const userInfo = ref({})
    const CartStore = useCartStore()
    // 2. 定义获取接口数据的action函数
    const getUserInfo = async ({ account, password }) => {
        const res = await loginAPI({ account, password })
        userInfo.value = res.result
    // 合并购物车
        let result = CartStore.cartList.map(item => {
            return {
                skuId: item.skuId,
                selected: item.selected,
                count: item.count
            }
        })
        mergeCartAPI(result)
      //重新获取数据
        CartStore.updateNewList()
    }
    // 退出时清除用户信息
    const clearUserInfo = () => {
        userInfo.value = {}
        CartStore.clearCart()
    }
    // 3. 以对象的格式把state和action return
    return {
        getUserInfo,
        userInfo,
        clearUserInfo
    }
}, {
    persist: true,
})

登录-Pinia用户数据持久化

持久化用户数据说明

1. 用户数据中有一个关键的数据叫做Token (用来标识当前用户是否登录),而Token持续一段时间才会过期 2. Pinia的存储是基于内存的,刷新就丢失,为了保持登录状态就要做到刷新不丢失,需要配合持久化进行存储

目的:保持token不丢失,保持登录状态 最终效果:操作state时会自动把用户数据在本地的localStorage也存一份,刷新的时候会从localStorage中先取

关键步骤总结和插件运行机制

//下载
npm i pinia-plugin-persistedstate

//注册
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)


//使用
import { defineStore } from 'pinia'
export const useStore = defineStore(
  'main',
  () => {
    const someState = ref('你好 pinia')
    return { someState }
  },
  {
    persist: true,
  }
)

登录-请求拦截器携带Token

为什么要在请求拦截器携带Token

Token作为用户标识,在很多个接口中都需要携带Token才可以正确获取数据,所以需要在接口调用时携带Token。另 外,为了统一控制采取请求拦截器携带的方案

如何配置

Axios请求拦截器可以在接口正式发起之前对请求参数做一些事情,通常Token数据会被注入到请求header中,格式按 照后端要求的格式进行拼接处理

// axios请求拦截器
http.interceptors.request.use(config => {
    const UserStore = useUserStore();
    const token = UserStore.userInfo.token
    if (token) {
        config.headers.Authorization = `Bearer ${token}`
    }
    return config
}, e => Promise.reject(e))

登录-退出登录功能实现

退出登录业务实现

基础思想:

  1. 清除用户信息
  2. 跳转到登录页

1- 新增清除用户信息action

 // 退出时清除用户信息
  const clearUserInfo = () => {
    userInfo.value = {}
  }

2- 组件中执行业务逻辑

<script setup>
import { useUserStore } from '@/stores/userStore'
import { useRouter } from 'vue-router'
const userStore = useUserStore()
const router = useRouter()
const confirm = () => {
  console.log('用户要退出登录了')
  // 退出登录业务逻辑实现
  // 1.清除用户信息 触发action
  userStore.clearUserInfo()
  // 2.跳转到登录页
  router.push('/login')
}
</script>

购物车功能实现

购物车业务逻辑梳理拆解

1. 整个购物车的实现分为俩个大分支,本地购物车操作和接口购物车操作
2. 由于购物车数据的特殊性,采取Pinia管理购物车列表数据并添加持久化缓存

 本地购物车 - 加入购物车实现

1. 添加购物车

基础思想:如果已经添加过相同的商品,就在其数量count上加一,如果没有添加过,就直接push到购物车列表中

// 封装购物车模块

import { defineStore } from 'pinia'
import { ref } from 'vue'


export const useCartStore = defineStore('cart', () => {
  // 1. 定义state - cartList
  const cartList = ref([])
  // 2. 定义action - addCart
  const addCart = (goods) => {
    console.log('添加', goods)
    // 添加购物车操作
    // 已添加过 - count + 1
    // 没有添加过 - 直接push
    // 思路:通过匹配传递过来的商品对象中的skuId能不能在cartList中找到,找到了就是添加过
    const item = cartList.value.find((item) => goods.skuId === item.skuId)
    if (item) {
      // 找到了
      item.count++
    } else {
      // 没找到
      cartList.value.push(goods)
    }
  }
  return {
    cartList,
    addCart
  }
}, {
  persist: true,
})

2. 删除功能实现

1- 添加删除action函数


  // 删除购物车
  const delCart = async (skuId) => {
      // 思路:
      // 1. 找到要删除项的下标值 - splice
      // 2. 使用数组的过滤方法 - filter
      const idx = cartList.value.findIndex((item) => skuId === item.skuId)
      cartList.value.splice(idx, 1)
  }

2- 组件触发action函数并传递参数 

 <i class="iconfont icon-close-new" @click="cartStore.delCart(i.skuId)"></i>

3。列表购物车-单选功能实现 

基本思想:通过skuId找到要进行单选操作的商品,把控制是否选中的selected字段修改为当前单选框的状态

1- 添加单选action

// 单选功能
const singleCheck = (skuId, selected) => {
  // 通过skuId找到要修改的那一项 然后把它的selected修改为传过来的selected
  const item = cartList.value.find((item) => item.skuId === skuId)
  item.selected = selected
}

2- 触发action函数

<script setup>
// 单选回调
const singleCheck = (i, selected) => {
  console.log(i, selected)
  // store cartList 数组 无法知道要修改谁的选中状态?
  // 除了selected补充一个用来筛选的参数 - skuId
  cartStore.singleCheck(i.skuId, selected)
}
</script>

  
<template>
  <td>
    <!-- 单选框 -->
    <el-checkbox :model-value="i.selected" @change="(selected) => singleCheck(i, selected)" />
  </td>
</template>

4.列表购物车-全选功能实现

基础思想:

  1. 全选状态决定单选框状态 - 遍历cartList把每一项的selected都设置为何全选框状态一致
  2. 单选框状态决定全选状态 - 只有所有单选框的selected都为true, 全选框才为true

1- store中定义action和计算属性

// 全选功能action
const allCheck = (selected) => {
  // 把cartList中的每一项的selected都设置为当前的全选框状态
  cartList.value.forEach(item => item.selected = selected)
}


// 是否全选计算属性
const isAll = computed(() => cartList.value.every((item) => item.selected))

2- 组件中触发aciton和使用计算属性 、

<script setup>
const allCheck = (selected) => {
  cartStore.allCheck(selected)
}

</script>

  
<template>
  <!-- 全选框 -->
  <el-checkbox :model-value="cartStore.isAll" @change="allCheck" />
</template>

5.列表购物车-统计数据功能实现

// 3. 已选择数量
const selectedCount = computed(() => cartList.value.filter(item => item.selected).reduce((a, c) => a + c.count, 0))
// 4. 已选择商品价钱合计
const selectedPrice = computed(() => cartList.value.filter(item => item.selected).reduce((a, c) => a + c.count * c.price, 0))

总结

本地购物车与线上购物车合并

判断有没有token 如果有就走线上 如果没有就走本地

退出登录时清除本地购物车

登录时 跟购物车合并并重新查询


import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import { useUserStore } from '@/stores/user'
import { insertCartAPI, findNewChartList, delCartAPI } from '@/apis/cart'


export const useCartStore = defineStore('cart', () => {
    const userStore = useUserStore()
    const isLogin = computed(() => userStore.userInfo.token)
    // 1. 定义state - cartList
    const cartList = ref([])
    // 获取购物车列表
    const updateNewList = async () => {
        let res = await findNewChartList()
        console.log(res, '购物车列表');
        cartList.value = res.result
    }
    // 2. 定义action - addCart
    const addCart = async (goods) => {
        const { skuId, count } = goods
        console.log('添加', goods)
        if (isLogin.value) {
            // 登录之后的加入购车逻辑
            await insertCartAPI({ skuId, count })
            updateNewList()
        } else {
            // 添加购物车操作
            // 已添加过 - count + 1
            // 没有添加过 - 直接push
            // 思路:通过匹配传递过来的商品对象中的skuId能不能在cartList中找到,找到了就是添加过
            const item = cartList.value.find((item) => goods.skuId === item.skuId)
            if (item) {
                // 找到了
                item.count++
            } else {
                // 没找到
                cartList.value.push(goods)
            }

        }

    }
    // 删除购物车
    const delCart = async (skuId) => {
        if (isLogin.value) {
            // 调用接口实现接口购物车中的删除功能
            await delCartAPI([skuId])
            updateNewList()
        } else {
            // 思路:
            // 1. 找到要删除项的下标值 - splice
            // 2. 使用数组的过滤方法 - filter
            const idx = cartList.value.findIndex((item) => skuId === item.skuId)
            cartList.value.splice(idx, 1)
        }

    };

    // 单选功能
    const singleCheck = (skuId, selected) => {
        // 通过skuId找到要修改的那一项 然后把它的selected修改为传过来的selected
        const item = cartList.value.find((item) => item.skuId === skuId)
        item.selected = selected
    }
    // 全选功能action
    const allCheck = (selected) => {
        // 把cartList中的每一项的selected都设置为当前的全选框状态
        cartList.value.forEach(item => item.selected = selected)
    }
    // 清除购物车
    const clearCart = () => {
        cartList.value = []
    }

    const allCount = computed(() => cartList.value.reduce((acc, item) => acc + item.count, 0))
    const allPrice = computed(() => cartList.value.reduce((acc, item) => acc + item.price * item.count, 0))
    const isAll = computed(() => cartList.value.every((item) => item.selected))
    // 3. 已选择数量
    const selectedCount = computed(() => cartList.value.filter(item => item.selected).reduce((a, c) => a + c.count, 0))
    // 4. 已选择商品价钱合计
    const selectedPrice = computed(() => cartList.value.filter(item => item.selected).reduce((a, c) => a + c.count * c.price, 0))
    return {
        cartList,
        addCart,
        delCart,
        allCount,
        allPrice,
        singleCheck,
        allCheck,
        isAll,
        selectedCount,
        selectedPrice,
        clearCart,
        updateNewList
    }
}, {
    persist: true,
})

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

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

相关文章

基于 JMeter 实现 WEB 项目性能测试,环境搭建与测试用例编写

目录 前言&#xff1a; 一、JDK 安装 二、Tomcat 安装 三、Redis 安装 四、数据库安装 五、WEB 项目搭建 六、性能测试项目搭建 七、总结 前言&#xff1a; 性能测试是软件开发中必不可少的一环&#xff0c;它可以帮助开发者提高程序的稳定性&#xff0c;优化性能&…

【产品经理】产品体验报告的思路

&#xff08;一&#xff09;产品概述 &#xff08;1&#xff09;体验环境 对于app来说&#xff0c;无非就是体验产品所用的机型&#xff0c;系统&#xff0c;然后app版本&#xff0c;体验时间&#xff0c;体验人等方面的信息。 &#xff08;2&#xff09;产品的概括或简介说…

基于LLMs的多模态大模型(Visual ChatGPT,PICa,MM-REACT,MAGIC)

当LLMs已经拥有了极强的对话能力后&#xff0c;如何使其拥有视觉和语音等多模态能力是紧接而来的热点&#xff08;虽然GPT4已经有了&#xff09;&#xff0c;这个系列将不定期更新一些利用LLMs做多模态任务的文章。 直觉上&#xff0c;如果直接训练一个类似chatgpt架构的多模态…

7种PCB走线方式

01电源布局布线相关 数字电路很多时候需要的电流是不连续的&#xff0c;所以对一些高速器件就会产生浪涌电流。 如果电源走线很长&#xff0c;则由于浪涌电流的存在进而会导致高频噪声&#xff0c;而此高频噪声会引入到其他信号中去。 而在高速电路中必然会存在寄生电感和寄…

SSM 如何使用 ShardingSphere 实现数据库分库分表

SSM 如何使用 ShardingSphere 实现数据库分库分表 简介 在大规模数据应用场景下&#xff0c;单一数据库可能无法承载高并发的读写操作。为了解决这个问题&#xff0c;一种常见的方式是使用数据库分库分表技术。ShardingSphere 是一个支持多种关系型数据库的分布式数据库中间件…

带您看懂全国产串口服务器!如何使用一看便知

不可否认&#xff0c;目前工业现场仍然会有很多串口设备的存在&#xff0c;对于这些串口设备&#xff0c;如果想要联网&#xff0c;就必须要转换成网络接口&#xff0c;这时候就会用到全国产串口服务器。 全国产串口服务器提供串口转网络功能&#xff0c;能够将RS-232/485/422串…

盐城北大青鸟“北大青鸟杯”IT精英挑战赛设中心评审隆重开赛

为积极响应北大青鸟总部开展第十届“北大青鸟杯”全国IT精英挑战赛的号召&#xff0c;成就学员们的IT梦想&#xff0c;“北大青鸟杯”IT精英挑战赛&#xff08;设计组&#xff09;盐城卓晨中心评审于2023年5月25日下午1:00在人才大厦306教室正式开赛&#xff01; ​ 赛前&a…

【状态估计】基于随机方法优化PMU优化配置(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

报表控件FastReport使用指南——使用NuGet包创建PDF文档

FastReport 是功能齐全的报表控件&#xff0c;可以帮助开发者可以快速并高效地为.NET&#xff0c;VCL&#xff0c;COM&#xff0c;ActiveX应用程序添加报表支持&#xff0c;由于其独特的编程原则&#xff0c;现在已经成为了Delphi平台最优秀的报表控件&#xff0c;支持将编程开…

低代码平台简介(10家国产化低代码平台详细介绍)

低代码平台&#xff1a;一个号称能在几分钟的时间里开发一套企业内部都可使用的系统开发工具。 本人曾做过一个测试&#xff0c;2人&#xff0c;历时8小时&#xff0c;用低代码平台成功搭建出一套“客户管理系统”。该系统所需要的15个子模块与40个界面。同样的功能如果用传统编…

什么是 sudo,为什么它如此重要?

在当今的技术世界中&#xff0c;Linux 操作系统广泛应用于各种环境&#xff0c;包括个人计算机、服务器和嵌入式设备。作为一种强大的开源操作系统&#xff0c;Linux 提供了丰富的安全功能&#xff0c;以保护系统和用户的数据安全。在 Linux 安全领域中&#xff0c;sudo 是一项…

接口测试的请求和响应

接口测试的请求和响应 在软件开发中&#xff0c;接口测试是必不可少的一环节。接口测试主要涉及到测试请求和响应的过程。请求是指客户端向服务器发送的一些指令或数据&#xff0c;而响应则是服务器对这些请求做出的回应。 请求通常包括请求方法、请求头以及请求体。请求方法有…

信息安全服务资质认证CCRC证书‖中国网络安全审查技术与认证中心

随着CCRC信息安全服务资质持证企业的增加&#xff0c;很多企业看着自己的同行纷纷获的CCRC证书&#xff0c;自身也想进行申报&#xff0c;但由于之前没有做过了解&#xff0c;像个无头苍蝇一样&#xff0c;所以对该资质申报的条件要求、申报的好处又不是那么清楚&#xff0c;接…

重塑DeFi:深入了解Solaris Network

Solaris Network已经在充满活力的去中心化金融&#xff08;DeFi&#xff09;领域崭露头角&#xff0c;成为一家颠覆性的平台&#xff0c;使用户能够创造和交易合成资产。凭借其致力于多链集成、创新功能和以社区为中心的方法&#xff0c;Solaris Network正在改变DeFi的格局&…

【PXIE301-203】基于PXIE总线的4路Cameralink Base图像模拟源

产品概述 PXIE301-203一款基于PXI Express总线的高性能4路CameraLink Base图像模拟源&#xff0c;板卡采用Xilinx的高性能Kintex-7系列FPGA作为主控制器&#xff0c;实现PCI Express总线接口的转换&#xff0c;图像数据的缓存&#xff0c;以及CameraLink图像时序的控制。该板卡…

记录::opencv编译,cmake编译vs动态库

环境&#xff1a;window7&#xff0c;cmake-gui&#xff0c;vs2013 opencv&#xff1a;3.4.4 opencv_contrib&#xff1a;3.4.4&#xff08;nonfree模块&#xff0c;主要为了用sift&#xff09; 链接&#xff1a;https://pan.baidu.com/s/1OXg2IRaxTLTVqM2PVR2ZFA 提取码&a…

2023年企业网盘推荐:实测好用的工具

在数字化时代&#xff0c;企业内部的文件、资料等信息量越来越大。如何高效地管理和共享这些数据&#xff0c;成为企业管理者亟需解决的问题。企业网盘作为一种新型的信息技术工具&#xff0c;以其方便快捷、安全可靠等优点&#xff0c;受到越来越多企业的青睐。 企业网盘工具的…

非计算机专业 3 年外包闭关 180 天入职字节,鬼知道我是怎么过来的

面试 大家都知道&#xff0c;现在的测试面试是越来越难了&#xff01;主要原因无非是两个&#xff1a; 随着测试这个行业的兴起&#xff0c;不管是在家待业的、对自己现在工作不满意的、大学选错专业的、缺钱的、想自己学的等等这些人绝大部分都是选择了去学习测试&#xff01…

Linux:root登陆显示bash-4.2问题处理

情况描述&#xff1a; root账号登陆服务器后&#xff0c;显示如下。 正常显示应该是下面这样&#xff1a; userhostname 原因&#xff1a; 是因为/root目录下没有配置文件 解决&#xff1a; 使用root用户登录&#xff0c;再home目录中创建用户对应的文件夹&#xff0c;mkdi…

【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答(二)

【写在前面】 之前和大家分享过一下HarmonyOS应用开发相关问题&#xff0c;今天继续和大家分享&#xff01; 【前提简介】 本文档主要总结HarmonyOS开发过程中可能遇到的一些问题解答&#xff0c;主要围绕HarmonyOS展开&#xff0c;包括但不限于不同API版本HarmonyOS开发、UI…