Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ae2e138

Browse files
完善购物车功能
1 parent 70fd459 commit ae2e138

File tree

15 files changed

+279
-35
lines changed

15 files changed

+279
-35
lines changed

‎README.md‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,50 @@
11
## react 项目案例-汉堡侠
2+
3+
### 函数式组件和 css module,结合 antd 组件库开发项目
4+
5+
- `useState` 为组件添加状态
6+
- `useContext` 向组件树深层传递数据
7+
- 使用方式一: 1.引入 `context` 2.使用 `Xxx.Consumer` 组件来创建元素
8+
`Consumer` 的标签体需要一个回调函数
9+
它会将 `context` 设置为回调函数的参数,通过参数就可以访问到 `context` 中存储的数据
10+
11+
```jsx
12+
const A = () => {
13+
return (
14+
<TestContext.Consumer>
15+
{(ctx) => {
16+
return (
17+
<div>
18+
{ctx.name} - {ctx.age}
19+
</div>
20+
)
21+
}}
22+
</TestContext.Consumer>
23+
)
24+
}
25+
```
26+
27+
- 使用 `Context` 方式二: 1.导入 Context 2.使用钩子函数 `useContext()`获取到 `context`
28+
`useContext()` 需要一个 `Context` 作为参数
29+
它会将 Context 中数据获取并作为返回值返回
30+
31+
Xxx.`Provider` - 表示数据的生产者,可以使用它来指定 `Context` 中的数据 - 通过 `value` 来指定 Context 中存储的数据,
32+
这样一来,在该组件的所有的子组件中都可以通过 `Context` 来访问它所指定数据
33+
34+
当我们通过 `Context` 访问数据时,他会读取离他最近的 `Provider` 中的数据,
35+
如果没有 `Provider`,则读取 `Context` 中的默认数据
36+
37+
```jsx
38+
const B = () => {
39+
// 使用钩子函数获取Context
40+
const ctx = useContext(TestContext)
41+
42+
return (
43+
<div>
44+
{ctx.name} -- {ctx.age}
45+
</div>
46+
)
47+
}
48+
```
49+
50+
- `useEffect` 解决组件产生的副作用

‎public/index.html‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
<meta name="viewport" content="width=device-width, initial-scale=1" />
88
<meta name="theme-color" content="#000000" />
99
<meta name="description" content="汉堡大侠让你横扫饥饿,做回自己" />
10-
<title>汉堡大侠</title>
10+
<title>汉堡侠</title>
1111
</head>
1212

1313
<body>
1414
<noscript>You need to enable JavaScript to run this app.</noscript>
1515
<div id="root"></div>
16+
17+
<div id="backdrop-root"></div>
18+
<div id="checkout-root"></div>
1619
</body>
1720

1821
</html>

‎src/App.js‎

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { ConfigProvider } from 'antd'
22
import Meals from './components/meals'
33
import Search from './components/search'
44
import Cart from './components/cart'
5+
import cartContext from './store/cart'
6+
import { useState } from 'react'
57

68
// 模拟一组食物数据
79
const MEALS_DATA = [
@@ -53,14 +55,48 @@ const MEALS_DATA = [
5355
]
5456

5557
function App () {
58+
const [mealsData] = useState(MEALS_DATA)
59+
const [cartData, setCartData] = useState({
60+
items: [],
61+
totalPrice: 0,
62+
amount: 0
63+
})
64+
65+
const addItem = (item) => {
66+
let newMeal = { ...cartData }
67+
newMeal.amount++
68+
newMeal.totalPrice += item?.price
69+
if (newMeal.items.indexOf(item) === -1) {
70+
newMeal.items.push(item)
71+
item.count = 1
72+
73+
} else {
74+
item.count++
75+
}
76+
setCartData(newMeal)
77+
}
78+
79+
const removeItem = (item) => {
80+
let newMeal = { ...cartData }
81+
newMeal.totalPrice -= item?.price
82+
newMeal.amount--
83+
item.count--
84+
if (item.count === 0) {
85+
newMeal.items.splice(newMeal.items.indexOf(item), 1)
86+
}
87+
setCartData(newMeal)
88+
}
89+
5690
return (
57-
<ConfigProvider theme={{ token: { colorPrimary: '#ffcd00' } }}>
58-
<div style={{ width: '750rem', height: '100vh', overflow: 'hidden', position: 'relative' }}>
59-
<Search></Search>
60-
<Meals meals={MEALS_DATA}></Meals>
61-
<Cart></Cart>
62-
</div>
63-
</ConfigProvider>
91+
<cartContext.Provider value={{ ...cartData, addItem, removeItem }}>
92+
<ConfigProvider theme={{ token: { colorPrimary: '#ffcd00' } }}>
93+
<div style={{ width: '750rem', height: '100vh', overflow: 'hidden', position: 'relative' }}>
94+
<Search></Search>
95+
<Meals meals={mealsData}></Meals>
96+
<Cart></Cart>
97+
</div>
98+
</ConfigProvider>
99+
</cartContext.Provider>
64100
)
65101
}
66102

‎src/components/cart/detail/index.js‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react'
2+
import { DeleteOutlined } from '@ant-design/icons'
3+
import detailCss from './index.module.css'
4+
import Backdrop from '../../ui/backdrop'
5+
import Counter from '../../meals/counter'
6+
7+
function Detail ({ meals }) {
8+
return (
9+
<Backdrop>
10+
<div className={detailCss.detailBox}>
11+
12+
<div className={detailCss.top}>
13+
<p className={detailCss.title}>餐品详情</p>
14+
<div className={detailCss.del}><DeleteOutlined className={detailCss.delIcon} />清空购物车</div>
15+
</div>
16+
<div>
17+
{
18+
meals.map(meal => {
19+
<div className={detailCss.item}>
20+
<img src={meal.img} alt={meal.title} className={detailCss.img} />
21+
<div>
22+
<div className={detailCss.name}>{meal.title}</div>
23+
<div className={detailCss.priceBox}>
24+
<div className={detailCss.price}>\{meal.price * meal.count}</div>
25+
<Counter meal={meal}></Counter>
26+
</div>
27+
</div>
28+
</div>
29+
})
30+
}
31+
</div>
32+
</div>
33+
</Backdrop>
34+
)
35+
}
36+
37+
export default Detail
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
.detailBox {
2+
position: absolute;
3+
bottom: 0;
4+
max-height: 70%;
5+
width: 100%;
6+
box-sizing: border-box;
7+
background-color: #fff;
8+
border-top-left-radius: 30rem;
9+
border-top-right-radius: 30rem;
10+
overflow: auto;
11+
padding: 20rem;
12+
}
13+
.top {
14+
display: flex;
15+
justify-content: space-between;
16+
align-items: center;
17+
}
18+
.title {
19+
color: #3223223;
20+
font-size: 20rem;
21+
}
22+
.del {
23+
color: #aaa;
24+
font-size: 10rem;
25+
cursor: pointer;
26+
display: flex;
27+
align-items: center;
28+
}
29+
.delIcon {
30+
font-size: 30rem;
31+
margin-right: 2rem;
32+
}
33+
.item {
34+
display: flex;
35+
}
36+
.img {
37+
width: 40rem;
38+
height: 40rem;
39+
border-radius: 8rem;
40+
margin-right: 10rem;
41+
}
42+
.name {
43+
color: #323232;
44+
font-size: 28rem;
45+
}
46+
.priceBox {
47+
display: flex;
48+
justify-content: space-between;
49+
align-items: center;
50+
}
51+
.price {
52+
or: #323232;
53+
font-size: 20rem;
54+
}

‎src/components/cart/index.js‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
1-
import React from 'react'
1+
import React,{useContext,useState} from 'react'
22
import { TabletFilled } from '@ant-design/icons'
33
import cartCss from './index.module.css'
44
import Badge from '../badge'
5+
import cartContext from '../../store/cart'
6+
import Detail from './detail'
57

68
function Cart () {
9+
const cartData = useContext(cartContext)
10+
11+
const [showDetails, setShowDetails] = useState(false)
12+
13+
const toggleDetail = () => {
14+
setShowDetails(!showDetails)
15+
}
716
return (
8-
<div className={cartCss.cartBox}>
17+
<div className={cartCss.cartBox} onClick={toggleDetail}>
18+
{showDetails && <Detail meals={cartData.items}></Detail>}
919
<div className={cartCss.content}>
1020
<div className={cartCss.tabletBox}>
11-
<TabletFilled className={cartCss.tablet}>
21+
<TabletFilled className={`${cartCss.tablet}${(cartData.amount>0 ? cartCss.active : null)}`}>
1222
</TabletFilled>
13-
<Badge count={1}></Badge>
23+
{
24+
cartData.amount > 0 ?
25+
<Badge count={cartData.amount}></Badge>
26+
: null
27+
}
1428
</div>
15-
<div className={cartCss.amount}>123</div>
16-
<div className={cartCss.account}>去结算</div>
29+
<div className={cartCss.amount}>\{cartData.totalPrice}</div>
30+
<div onClick={(e)=>{e.stopPropagation()}}className={`${cartCss.account}${(cartData.amount>0 ? cartCss.active : null)}`}>去结算</div>
1731
</div>
18-
1932
</div>
2033
)
2134
}

‎src/components/cart/index.module.css‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
left: 0;
66
right: 0;
77
padding: 12rem 32rem;
8+
z-index: 1000;
89
}
910
.content {
1011
display: flex;
@@ -16,6 +17,9 @@
1617
}
1718
.tablet {
1819
font-size: 96rem;
20+
color: #aaa;
21+
}
22+
.tablet.active {
1923
color: #ffcd00;
2024
}
2125
.tabletBox {
@@ -26,6 +30,7 @@
2630
.amount {
2731
font-size: 30rem;
2832
color: #fff;
33+
margin-left: 120rem;
2934
}
3035
.account {
3136
width: 180rem;
@@ -36,3 +41,6 @@
3641
text-align: center;
3742
background-color: #666;
3843
}
44+
.account.active {
45+
background-color: #ffcd00;
46+
}

‎src/components/meals/counter/index.js‎

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,26 @@
1-
import React, { useState } from 'react'
1+
import React, { useContext } from 'react'
22
import { PlusOutlined, MinusOutlined } from '@ant-design/icons'
33
import counterCss from './index.module.css'
4+
import CartCtx from '../../../store/cart'
45

5-
function Counter () {
6-
const [count, setCount] = useState(0)
7-
8-
const plusHandle = () => {
9-
setCount(count => count + 1)
6+
function Counter ({ meal }) {
7+
const ctx = useContext(CartCtx)
8+
const add = () => {
9+
ctx.addItem(meal)
1010
}
11-
const minusHandle = () => {
12-
setCount(count=>count-1)
11+
const remove = () => {
12+
ctx.removeItem(meal)
1313
}
14-
1514
return (
1615
<div className={counterCss.counterBox}>
1716
{
18-
count > 0 ? <>
19-
<MinusOutlined onClick={minusHandle} className={counterCss.minus} />
20-
<span className={counterCss.count}>{count}</span>
17+
meal?.count > 0 ? <>
18+
<MinusOutlined onClick={remove} className={counterCss.minus} />
19+
<span className={counterCss.count}>{meal.count}</span>
2120
</> : null
2221
}
2322

24-
<PlusOutlined onClick={plusHandle} className={counterCss.plus} />
23+
<PlusOutlined onClick={add} className={counterCss.plus} />
2524
</div>
2625
)
2726
}

‎src/components/meals/index.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default function Meals ({ meals }) {
77
<>
88
<div className={mealsCss.meals}>
99
{
10-
meals.map(meal => <Meal {...meal} key={meal.id} />)
10+
meals.map(meal => <Meal meal={meal} key={meal.id} />)
1111
}
1212

1313
</div>

‎src/components/meals/meal/index.js‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ import React from 'react'
22
import mealCss from './index.module.css'
33
import Counter from '../counter'
44

5-
export default function Meal ({ title, desc, price, img }) {
5+
export default function Meal ({ meal }) {
66
return (
77
<div className={mealCss.mealBox}>
8-
<img className={mealCss.mealImg} src={img} alt={title} />
8+
<img className={mealCss.mealImg} src={meal.img} alt={meal.title} />
99
<div>
1010

11-
<h2 className={mealCss.title}>{title}</h2>
12-
<p className={mealCss.desc}>{desc}</p>
11+
<h2 className={mealCss.title}>{meal.title}</h2>
12+
<p className={mealCss.desc}>{meal.desc}</p>
1313
<div className={mealCss.priceBox}>
14-
<p className={mealCss.price}>\<span className={mealCss.num}>{price}</span></p>
15-
<div><Counter></Counter></div>
14+
<p className={mealCss.price}>\<span className={mealCss.num}>{meal.price}</span></p>
15+
<div><Countermeal={meal}></Counter></div>
1616
</div>
1717
</div>
1818
</div>

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /