FoodOrderer: Basics
This commit is contained in:
parent
7d395f5d15
commit
4fefa48079
3 changed files with 98 additions and 37 deletions
|
@ -19,6 +19,7 @@
|
||||||
"typescript": "^5.6.3"
|
"typescript": "^5.6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"decimal.js": "^10.4.3",
|
||||||
"react-router-dom": "^6.28.0",
|
"react-router-dom": "^6.28.0",
|
||||||
"tailwindcss": "^3.4.15"
|
"tailwindcss": "^3.4.15"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,72 +1,100 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import Decimal from 'decimal.js';
|
||||||
|
|
||||||
interface ShopItem {
|
interface ShopItem {
|
||||||
name: string,
|
name: string,
|
||||||
need: number, // 需求数量
|
need: number, // 需求数量
|
||||||
provide: number, // 提供的数量
|
provider: {
|
||||||
|
name: string
|
||||||
|
price: number
|
||||||
|
storage: number // 库存
|
||||||
|
}, // 提供者的信息
|
||||||
image: {
|
image: {
|
||||||
src: string
|
src: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const getNeedPrice = (item: ShopItem) => {
|
||||||
|
let result = new Decimal(item.provider.price).times(item.need)
|
||||||
|
return result.toNumber()
|
||||||
|
}
|
||||||
|
|
||||||
const shopItems: ShopItem[] = [
|
const shopItems: ShopItem[] = [
|
||||||
{
|
{
|
||||||
name: "Apple",
|
name: "Apple",
|
||||||
need: 10,
|
need: 999,
|
||||||
provide: 5,
|
provider: {
|
||||||
|
name: "Fresh Farms",
|
||||||
|
price: 2.5,
|
||||||
|
storage: 1000,
|
||||||
|
},
|
||||||
image: {
|
image: {
|
||||||
src: "https://i.imgur.com/FsNUv2x.jpeg"
|
src: "https://i.imgur.com/FsNUv2x.jpeg",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Orange Jam",
|
name: "Orange Jam",
|
||||||
need: 8,
|
need: 8,
|
||||||
provide: 10,
|
provider: {
|
||||||
|
name: "Sweet Treats Co.",
|
||||||
|
price: 4.0,
|
||||||
|
storage: 50,
|
||||||
|
},
|
||||||
image: {
|
image: {
|
||||||
src: "https://i.imgur.com/hB46Z9s.png"
|
src: "https://i.imgur.com/hB46Z9s.png",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Milk (Gone bad)",
|
name: "Milk (Gone bad)",
|
||||||
need: 0,
|
need: 0,
|
||||||
provide: 3,
|
provider: {
|
||||||
|
name: "Dairy Delights",
|
||||||
|
price: 1.2,
|
||||||
|
storage: 20,
|
||||||
|
},
|
||||||
image: {
|
image: {
|
||||||
src: "https://i.imgur.com/ZV7vzDu.png"
|
src: "https://i.imgur.com/ZV7vzDu.png",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Bread",
|
name: "Bread",
|
||||||
need: 12,
|
need: 12,
|
||||||
provide: 6,
|
provider: {
|
||||||
|
name: "Baker's Best",
|
||||||
|
price: 3.0,
|
||||||
|
storage: 30,
|
||||||
|
},
|
||||||
image: {
|
image: {
|
||||||
src: "https://i.imgur.com/qBn8jWS.jpeg"
|
src: "https://i.imgur.com/qBn8jWS.jpeg",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function FoodList({ list, setList }: {
|
function FoodList({ list, setList }: {
|
||||||
list: ShopItem[],
|
list: ShopItem[],
|
||||||
setList: React.Dispatch<React.SetStateAction<ShopItem[]>>
|
setList: React.Dispatch<React.SetStateAction<ShopItem[]>>
|
||||||
}) {
|
}) {
|
||||||
enum ButtonOperation {INCREASE , DECREASE}
|
enum ButtonOperation { INCREASE, DECREASE }
|
||||||
const handleVolumeButton = (item: ShopItem ,operation: ButtonOperation) => {
|
const handleVolumeButton = (item: ShopItem, operation: ButtonOperation) => {
|
||||||
const nowNeed = item.need
|
const nowNeed = item.need
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case ButtonOperation.INCREASE:
|
case ButtonOperation.INCREASE:
|
||||||
if (nowNeed >= item.provide){
|
if (nowNeed >= item.provider.storage) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setList(list.map((it)=>
|
setList(list.map((it) =>
|
||||||
it == item? {...it,need: nowNeed+1}:it
|
it == item ? { ...it, need: nowNeed + 1 } : it
|
||||||
))
|
))
|
||||||
break
|
break
|
||||||
case ButtonOperation.DECREASE:
|
case ButtonOperation.DECREASE:
|
||||||
if (nowNeed <= 0){
|
if (nowNeed <= 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setList(list.map((it)=>
|
setList(list.map((it) =>
|
||||||
it == item? {...it,need: nowNeed-1}:it
|
it == item ? { ...it, need: nowNeed - 1 } : it
|
||||||
))
|
))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -76,21 +104,51 @@ function FoodList({ list, setList }: {
|
||||||
return (
|
return (
|
||||||
<div className="table gap-3 min-w-max space-y-3 mx-auto items-center">
|
<div className="table gap-3 min-w-max space-y-3 mx-auto items-center">
|
||||||
{list.map((item) =>
|
{list.map((item) =>
|
||||||
<div key={item.name} className="flex gap-5 bg-slate-200 rounded-md shadow-md border-spacing-1 px-5 py-2">
|
<div
|
||||||
|
key={item.name}
|
||||||
|
className="flex gap-5 bg-slate-200 rounded-md shadow-md border-spacing-1 px-5 py-3 relative"
|
||||||
|
>
|
||||||
<img className="size-24 rounded-full" {...item.image} />
|
<img className="size-24 rounded-full" {...item.image} />
|
||||||
<div>
|
<div className="flex-1">
|
||||||
<p className="text-3xl">{item.name}</p>
|
<p className="text-3xl">{item.name}</p>
|
||||||
<div className="flex">
|
<p className="text-base text-gray-400">{item.provider.name}</p>
|
||||||
|
<p className="text-sm text-zinc-500">{item.provider.price} 元/个</p>
|
||||||
|
<p className="text-sm text-neutral-500">库存: {item.provider.storage}</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col justify-between items-end gap-2">
|
||||||
|
<p className="text-xl text-stone-700 text-center">{getNeedPrice(item)} 元</p>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
<button
|
<button
|
||||||
onClick={()=>handleVolumeButton(item,ButtonOperation.DECREASE)}
|
onClick={() => handleVolumeButton(item, ButtonOperation.DECREASE)}
|
||||||
className="size-6 bg-lime-50 rounded-2xl">-</button>
|
className="w-8 h-8 flex items-center justify-center bg-lime-50 rounded-full text-lg font-bold"
|
||||||
<p>{item.need}</p>
|
>
|
||||||
|
-
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={item.need}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = parseInt(e.target.value, 10) || 0;
|
||||||
|
if (value <= item.provider.storage && value >= 0) {
|
||||||
|
setList(list.map((it) =>
|
||||||
|
it == item ? { ...it, need: value } : it
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="text-center w-16 h-8 bg-gray-50 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-blue-400 text-base appearance-none pl-1.5"
|
||||||
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={()=>handleVolumeButton(item,ButtonOperation.INCREASE)}
|
onClick={() => handleVolumeButton(item, ButtonOperation.INCREASE)}
|
||||||
className="size-6 bg-red-50 rounded-2xl">+</button>
|
className="w-8 h-8 flex items-center justify-center bg-red-50 rounded-full text-lg font-bold"
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -100,8 +158,10 @@ function FoodList({ list, setList }: {
|
||||||
const FoodOrderer = () => {
|
const FoodOrderer = () => {
|
||||||
const [items, setItems] = useState<ShopItem[]>(shopItems)
|
const [items, setItems] = useState<ShopItem[]>(shopItems)
|
||||||
return (
|
return (
|
||||||
<div className="items-center">
|
<div className="flex flex-col items-center">
|
||||||
<p className="text-center">点些什么</p>
|
<p className="text-center text-2xl sm:text-5xl py-5 sm:py-10 bg-gradient-to-r from-blue-600 to-pink-600 via-green-700 inline-block text-transparent bg-clip-text">
|
||||||
|
点些什么
|
||||||
|
</p>
|
||||||
<FoodList list={items} setList={setItems} />
|
<FoodList list={items} setList={setItems} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -3616,7 +3616,7 @@ debug@^3.2.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
decimal.js@^10.2.1:
|
decimal.js@^10.2.1, decimal.js@^10.4.3:
|
||||||
version "10.4.3"
|
version "10.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
|
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
|
||||||
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
|
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
|
||||||
|
|
Loading…
Reference in a new issue