react购物车Redux

news2024/9/22 7:39:31

入口index.js

import React from 'react'
import {createRoot} from 'react-dom/client'

import App from './App'
//注入store
import {Provider} from "react-redux";
import store from "./store";

const root = createRoot(document.getElementById('root'))
root.render(
    <Provider store={store}>
        <App/>
    </Provider>
)

srotre/modules/takeway.js

//编写store
import {createSlice} from "@reduxjs/toolkit";
import axios from "axios";

const foodsStore = createSlice({
    name: 'foods',
    initialState: {
        //商品列表
        foodsList: [],
        //菜单激活的下标值
        activeIndex: 0,
        // 购物车列表
        cartList: []
    },
    reducers: {
        // 更改商品列表
        setFoodsList(state, action) {
            state.foodsList = action.payload
        },
        // 更改activeIndex
        changActiveIndex(state, action) {
            state.activeIndex = action.payload
        },
        //添加购物车
        addCart(state, action) {
            //是否添加过  如果添加只更新count 没有添加过 直接push进去
            const data = JSON.parse(JSON.stringify(state.cartList))
            const item = data
                .find(item => item.id === action.payload.id)
            const index = data.findIndex(item => item.id === action.payload.id)
            console.log(item)
            if (item) {
                state.cartList[index].count++
            } else {
                state.cartList.push(action.payload)
            }

        },
        //count增加
        addCount(state, action) {
            const data = JSON.parse(JSON.stringify(state.cartList))
            const item = data
                .find(item => item.id === action.payload.id)
            const index = data.findIndex(item => item.id === action.payload.id)
            state.cartList[index].count++

        },
        //count--
        decreCount(state, action) {
            const data = JSON.parse(JSON.stringify(state.cartList))
            const item = data.find(item => item.id === action.payload.id)
            const index = data.findIndex(item => item.id === action.payload.id)
            if (state.cartList[index].count === 0) {
                return
            }
            state.cartList[index].count--

        },
        // 清除购物车
        clearCart (state) {
            state.cartList = []
        }
    }
})
//异步获取部分 结构出创建action对象
const { setFoodsList, changActiveIndex, addCart, addCount, decreCount, clearCart } = foodsStore.actions
console.log(foodsStore.actions, 11111111111)
const fetchChannelList = () => {
    console.log('123')
    return async (dispatch) => {
        console.log('编写异步逻辑')
        // 编写异步逻辑
        const res = await axios.get('http://localhost:3004/takeaway')
        // 调用dispatch函数提交action
        dispatch(setFoodsList(res.data))
    }
}
export { fetchChannelList, changActiveIndex, addCart, addCount, decreCount, clearCart }
//获取reducer函数
const reducer = foodsStore.reducer

export default reducer

srotre/modules/index.js

import foodsReducer from './modules/takeaway'
import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({
    reducer: {
        foods: foodsReducer
    }
})

export default store

Menus/index.js

import classNames from 'classnames'
import './index.scss'
import {useDispatch, useSelector} from "react-redux";
import {changActiveIndex} from "../../store/modules/takeaway";

const Menu = () => {
    // const foodsList = [
    //   {
    //     "tag": "318569657",
    //     "name": "一人套餐",
    //     "foods": [
    //       {
    //         "id": 8078956697,
    //         "name": "烤羊肉串(10串)",
    //         "like_ratio_desc": "好评度100%",
    //         "month_saled": 40,
    //         "unit": "10串",
    //         "food_tag_list": ["点评网友推荐"],
    //         "price": 90,
    //         "picture": "https://zqran.gitee.io/images/waimai/8078956697.jpg",
    //         "description": "",
    //         "tag": "318569657"
    //       },
    //       {
    //         "id": 7384994864,
    //         "name": "腊味煲仔饭",
    //         "like_ratio_desc": "好评度81%",
    //         "month_saled": 100,
    //         "unit": "1人份",
    //         "food_tag_list": [],
    //         "price": 39,
    //         "picture": "https://zqran.gitee.io/images/waimai/7384994864.jpg",
    //         "description": "",
    //         "tag": "318569657"
    //       },
    //       {
    //         "id": 2305772036,
    //         "name": "鸡腿胡萝卜焖饭",
    //         "like_ratio_desc": "好评度91%",
    //         "month_saled": 300,
    //         "unit": "1人份",
    //         "food_tag_list": [],
    //         "price": 34.32,
    //         "picture": "https://zqran.gitee.io/images/waimai/2305772036.jpg",
    //         "description": "主料:大米、鸡腿、菜心、胡萝卜",
    //         "tag": "318569657"
    //       },
    //       {
    //         "id": 2233861812,
    //         "name": "小份酸汤莜面鱼鱼+肉夹馍套餐",
    //         "like_ratio_desc": "好评度73%",
    //         "month_saled": 600,
    //         "unit": "1人份",
    //         "food_tag_list": ["“口味好,包装很好~点赞”"],
    //         "price": 34.32,
    //         "picture": "https://zqran.gitee.io/images/waimai/2233861812.jpg",
    //         "description": "酸汤莜面鱼鱼,主料:酸汤、莜面 肉夹馍,主料:白皮饼、猪肉",
    //         "tag": "318569657"
    //       }
    //     ]
    //   }
    // ]
    const {foodsList, activeIndex} = useSelector(state => state.foods)
    const menus = foodsList.map(item => ({tag: item.tag, name: item.name}))
    const dispath = useDispatch()
    return (
        <nav className="list-menu">
            {/* 添加active类名会变成激活状态 */}
            {menus.map((item, index) => {
                return (
                    <div
                        onClick={() => dispath(changActiveIndex(index))}
                        key={item.tag}
                        className={classNames(
                            'list-menu-item',
                            activeIndex === index && 'active'
                        )}
                    >
                        {item.name}
                    </div>
                )
            })}
        </nav>
    )
}

export default Menu

FoodsItem.js

import './index.scss'
import { useDispatch } from 'react-redux'
import {addCart} from "../../../store/modules/takeaway";
const Foods = ({
  id,
  picture,
  name,
  unit,
  description,
  food_tag_list,
  month_saled,
  like_ratio_desc,
  price,
  tag,
  count
}) => {
  const dispatch = useDispatch()
  return (
    <dd className="cate-goods">
      <div className="goods-img-wrap">
        <img src={picture} alt="" className="goods-img" />
      </div>
      <div className="goods-info">
        <div className="goods-desc">
          <div className="goods-title">{name}</div>
          <div className="goods-detail">
            <div className="goods-unit">{unit}</div>
            <div className="goods-detail-text">{description}</div>
          </div>
          <div className="goods-tag">{food_tag_list.join(' ')}</div>
          <div className="goods-sales-volume">
            <span className="goods-num">月售{month_saled}</span>
            <span className="goods-num">{like_ratio_desc}</span>
          </div>
        </div>
        <div className="goods-price-count">
          <div className="goods-price">
            <span className="goods-price-unit">¥</span>
            {price}
          </div>
          <div className="goods-count">
            <span className="plus" onClick={() => dispatch(addCart({
              id,
              picture,
              name,
              unit,
              description,
              food_tag_list,
              month_saled,
              like_ratio_desc,
              price,
              tag,
              count
            }))}>+</span>
          </div>
        </div>
      </div>
    </dd>
  )
}

export default Foods

Cart.js

import classNames from 'classnames'
import Count from '../Count'
import './index.scss'
import {useDispatch, useSelector} from "react-redux";
import {addCount, clearCart, decreCount} from "../../store/modules/takeaway";
import {useState} from "react";

const Cart = () => {
    const {cartList} = useSelector(state => state.foods)
    const totalPrice = cartList.reduce((a, c) => a + c.price * c.count, 0)
    const totalCount = cartList.reduce((a, c) => a + c.count, 0)
    const dispatch = useDispatch()
    //控制购物车打开关闭的状态
    const [visible, setVisible] = useState(false)
    const onshow = () => {
        if (cartList.length > 0) {
            setVisible(true)
        }
    }
    // const cart = []
    return (
        <div className="cartContainer">
            {/* 遮罩层 添加visible类名可以显示出来 */}
            <div onClick={() => setVisible(false)}
                 className={classNames('cartOverlay', visible && 'visible')}
            />
            <div className="cart">
                {/* fill 添加fill类名可以切换购物车状态*/}
                {/* 购物车数量 */}
                <div onClick={onshow} className={classNames('icon')}>
                    {cartList.length && <div className="cartCornerMark">{totalCount}</div>}
                </div>
                {/* 购物车价格 */}
                <div className="main">
                    <div className="price">
            <span className="payableAmount">
              <span className="payableAmountUnit">¥</span>
                {totalPrice.toFixed(2)}
            </span>
                    </div>
                    <span className="text">预估另需配送费 ¥5</span>
                </div>
                {/* 结算 or 起送 */}
                {/* 结算 or 起送 */}
                {cartList.length > 0 ? (
                    <div className="goToPreview">去结算</div>
                ) : (
                    <div className="minFee">1元起送</div>
                )}
            </div>
            {/* 添加visible类名 div会显示出来 */}
            <div className={classNames('cartPanel', visible && 'visible')}>
                <div className="header">
                    <span className="text">购物车</span>
                    <span className="clearCart" onClick={() => dispatch(clearCart())}>
            清空购物车
          </span>
                </div>

                {/* 购物车列表 */}
                <div className="scrollArea">
                    {cartList.map(item => {
                        return (
                            <div className="cartItem" key={item.id}>
                                <img className="shopPic" src={item.picture} alt=""/>
                                <div className="main">
                                    <div className="skuInfo">
                                        <div className="name">{item.name}</div>
                                    </div>
                                    <div className="payableAmount">
                                        <span className="yuan">¥</span>
                                        <span className="price">{item.price}</span>
                                    </div>
                                </div>
                                <div className="skuBtnWrapper btnGroup">
                                    <Count
                                        count={item.count}
                                        onPlus={() => dispatch(addCount({id: item.id}))}
                                        onMinus={() => dispatch(decreCount({id: item.id}))}
                                    />
                                </div>
                            </div>
                        )
                    })}
                </div>
            </div>
        </div>
    )
}

export default Cart

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

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

相关文章

Python系统教程003

变量的数据类型 将输入信息存入变量name中然后输出。 如果想通过键盘输 入信息再存入变量 中应该怎么办&#xff1f; 一、内容 input函数变量的数据类型变量的运算 &#xff08;一&#xff09;、input函数 1、input函数1 函数&#xff1a;用来完成某一个特定功能的代码 …

2.第二阶段x86游戏实战2-认识进制、理解数据宽度和位的概念

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

NXP,S32K1XX汽车通用微控制器开发笔记

文章目录 1. 概述2. 开发环境配置2.1 S32 Design Studio2.2 安装SDK2.3 新建demo工程2.4 字体配置2.5 按需求修改demo2.5.1 修改pin脚定义2.5.2 增加串口打印功能2.6 编译代码2.7 debuger 配置参考1. 概述 S32K1系列32位微控制器(MCU)提供基于Arm Cortex-M的MCU,以及基本的…

某PO手机市场竞争分析,巧用波特五力分析法找出核心竞争力!

某PO手机主要从事手机的生产与销售&#xff0c;最近推出了新款 Reno 系列 5G 手机。当前&#xff0c;苹果占据了高端市场&#xff0c;华为占据了中高端市场&#xff0c;而某 PO 手机则在剩余市场中与某 VO 和某米竞争。近年来&#xff0c;某 PO手机凭借中端 R 系列逐步取得市场…

【文献阅读】AdaLora: Adaptive Budget Allocation for Parameter-Efficient Fine-Tuning

目录 1. 前言2. 矩阵分解2.1 SVD分解2.2 特征值分解2.3 LU分解2.4 QR分解 3. AdaLora3.1 motivation3.2 改进 1. 前言 矩阵分解为什么可以加速推理 假设原始权重矩阵 W ∈ R ( m ∗ n ) {W∈R^{(m*n)}} W∈R(m∗n)&#xff0c;矩阵乘法中时间复杂度为mn&#xff0c;变形为 W …

Vue.js 组件化开发:父子组件通信与组件注册详解

Vue.js 组件化开发&#xff1a;父子组件通信与组件注册详解 简介&#xff1a; 在 Vue.js 的开发中&#xff0c;组件是构建应用的重要基础。掌握组件的创建与使用&#xff0c;尤其是父子组件的通信和组件的注册与命名&#xff0c;是开发中不可或缺的技能。本文将详细探讨这些内容…

Day-04-QFile打开文件的两种方式

一、UI界面设置两个按键&#xff0c;并直接转到槽函数 二、两种代码展示 #include <QFile> #include <QDebug>//此两种方式中调用函数&#xff0c;应包含的头文件void Widget::on_btnReadFile01_clicked()//第一种打开方式 {//1. 打开文件QFile file;file.setFile…

双指针(3)_快慢指针_快乐数问题

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 双指针(3)_快慢指针_快乐数问题 收录于专栏【经典算法练习】 本专栏旨在分享学习C的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1.…

天润融通解开售后维修的成本枷锁,提高维修服务效率

如今&#xff0c;企业客户服务在开展业务咨询和售后受理时&#xff0c;主要方式还是通过电话与在线方式进行。这种方式虽然方便&#xff0c;但是对于一些非常紧急的情况还是显得有些不够。 比如&#xff0c;虽然现在许多企业APP已经实现了一键咨询和一键报修&#xff0c;但当客…

[Deepin] 简单使用 RustDesk 实现远程访问Deepin

本教程假设你学会了看官方文档&#xff0c;且拥有基本的IT常识 本教程仅提供可用的方法&#xff0c;并讲述局限性和更优但更复杂的方法&#xff0c;不是一个手把手教程 目标&#xff1a;实现远程访问Deepin 依托 樱花frpRustDesk的“允许通过ip访问” 概述 在RustDesk打开…

谷歌seo网址如何快速被收录?

想让你的网站快速被搜索引擎收录&#xff0c;可以采取几种不同的策略。首先&#xff0c;确保你的网站内容丰富、有价值&#xff0c;搜索引擎更喜欢收录内容质量高的网站。同时&#xff0c;增强网站的外链建设&#xff0c;做好这些站内优化&#xff0c;接下来就是通过谷歌搜索控…

AMV格式转换,试试这五种转换方式

AMV格式转换&#xff0c;AMV格式是一种专为MP4播放器而开发的视频格式&#xff0c;具有小巧、易于传输和较好的视频质量等特点。然而&#xff0c;AMV格式并不是普遍支持的格式&#xff0c;这导致了许多用户在使用MP4播放器时无法播放AMV格式的视频。为了帮助大家解决这一问题&a…

动态化-鸿蒙跨端方案介绍

一、背景 &#x1f449; 华为在2023.9.25官方发布会上宣布&#xff0c;新的鸿蒙系统将不再兼容安卓应用&#xff0c;这意味着&#xff0c;包括京东金融APP在内的所有安卓应用&#xff0c;在新的鸿蒙系统上将无法运行&#xff0c;需要重新开发专门适用于新鸿蒙系统的专版APP。 …

Java项目: 基于SpringBoot+mysql网上点餐系统分前后台(含源码+数据库+答辩PPT+毕业论文)

一、项目简介 本项目是一套基于SpringBootmysql网上点餐系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功能齐…

深度学习特征提取魔改版太强了!发文香饽饽!

要说CV领域经久不衰的研究热点&#xff0c;特征提取可以占一席&#xff0c;毕竟SLAM、三维重建等重要应用的底层都离不开它。 再加上近几年深度学习兴起&#xff0c;用深度学习做特征提取逐渐成了主流&#xff0c;比传统算法无论是性能、准确性还是效率都更胜一筹。 目前比较…

AAC高级音频编码技术

一、什么是AAC AAC的中文名称是高级音频编码技术&#xff0c;它是基于MPEG-2的一种全新的音频编码技术。随着时代的发展&#xff0c;目前AAC的技术升级到MPEG-4表准。AAC广泛的应用在网络传输、高清录制等领域&#xff0c;而AAC技术的出现就是为了取代之前的MP3格式。 二、为什…

类的加载过程与初始化小记

//部分内容来自“狂神说java” 代码验证 解释 1.加载类的信息&#xff0c;加载到内存中&#xff0c;如例子&#xff0c;将Test05和A类的信息加载到方法区&#xff0c; 2.加载完成后&#xff0c;立马生成一个class对象&#xff0c;如例 java.lang.class对象代表Test05类..., 3…

畅捷通ERP远程访问:使用巴比达内网穿透的体验

ERP系统的应用越来越普遍。畅捷通ERP作为一款优秀的企业资源管理工具&#xff0c;帮助企业整合资源&#xff0c;提高运营效率。然而&#xff0c;很多企业在使用畅捷通ERP时&#xff0c;面临着远程访问的问题。为了有效解决这一问题&#xff0c;我尝试了巴比达内网穿透&#xff…

2024高教社杯全国大学生数学建模竞赛C题保姆级分析完整思路+代码+数据教学

2024高教社杯全国大学生数学建模竞赛C题保姆级分析完整思路代码数据教学 C题题目&#xff1a;农作物的种植策略 今年的题目很有意思&#xff0c;和去年C题题目相关联了&#xff0c;接下来我们将按照题目总体分析-背景分析-各小问分析的形式来 1 总体分析 1.1 问题背景&…

[算法]单调栈解法

目录 739. 每日温度 - 力扣&#xff08;LeetCode&#xff09; 42. 接雨水 - 力扣&#xff08;LeetCode&#xff09; 84. 柱状图中最大的矩形 - 力扣&#xff08;LeetCode&#xff09; 739. 每日温度 - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a; 通常是一维数…