入口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