Last updated on August 17, 2024 pm
本节课建立了一个旅游需带物品清单App,在构建的时候,第一步是分析组件,第二步是构建静态网页,然后才是开始实现动态功能。
一、代码
(1)index.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
| @import url("https://fonts.googleapis.com/css2?family=Monoton&family=Quicksand:wght@500;700&display=swap");
* { margin: 0; padding: 0; box-sizing: border-box; }
html { font-size: 62.5%; }
body { font-size: 2.4rem; font-family: sans-serif; color: #367031; font-family: "Quicksand"; font-weight: 500; }
.app { width: 100%; height: 100vh; display: grid; grid-template-rows: auto auto 1fr auto; }
h1 { text-align: center; background-color: #9ad0c4; font-family: "Monoton"; font-size: 8rem; text-transform: uppercase; font-weight: 400; word-spacing: 30px; letter-spacing: -5px; padding: 2.4rem 0; }
.add-form { background-color: #74b8b1; padding: 2.8rem 0; display: flex; align-items: center; justify-content: center; gap: 0.8rem; }
h3 { margin-right: 1.6rem; font-size: 2.4rem; }
button, select, input { background-color: #d1d8c8; color: #295c59; font-family: inherit; border: none; border-radius: 10rem; padding: 1.2rem 3.2rem; font-weight: 700; font-size: 1.8rem; cursor: pointer; }
.add-form button { text-transform: uppercase; background-color: #8cd3bc; }
.list { background-color: #8eb1ce; color: #e1ce98; padding: 4rem 0;
display: flex; justify-content: space-between; flex-direction: column; gap: 3.2rem; align-items: center; }
.actions button, .list select { text-transform: uppercase; padding: 0.8rem 2.4rem; font-size: 1.4rem; font-weight: 700; margin: 0 0.8rem; }
.list ul { list-style: none; width: 80%; overflow: scroll;
display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.2rem; justify-content: center; align-content: start; }
.list li { display: flex; align-items: center; gap: 1.2rem; }
input[type="checkbox"] { height: 2rem; width: 2rem; accent-color: #e5771f; }
.list li button { cursor: pointer; background: none; border: none; font-size: 1.8rem; padding: 0.8rem; transform: translateY(2px); }
.stats { background-color: #9ad0c4; text-align: center; font-weight: 700; padding: 3.2rem 0; }
|
(2)App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import { useState } from "react"; import Logo from "./Logo"; import Form from "./Form"; import PackingList from "./PackingList"; import Stats from "./Stats";
export default function App() { const [items, setItems] = useState([]);
function handleAddItems(item) { setItems((items) => [...items, item]); }
function handleDeleteItems(id) { setItems((items) => items.filter((item) => item.id !== id)); }
function handleToggleItem(id) { setItems((items) => items.map((item) => item.id === id ? { ...item, packed: !item.packed } : item ) ); }
function handleClearItems() { const confirmed = window.confirm( "Are you sure you want to delete all items?" );
if (confirmed) setItems([]); }
return ( <div className="app"> <Logo /> <Form onAddItems={handleAddItems} /> <PackingList items={items} onDeleteItem={handleDeleteItems} onToggleItem={handleToggleItem} onClearItems={handleClearItems} /> <Stats items={items} /> </div> ); }
|
(3)Form.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import { useState } from "react";
export default function Form({ onAddItems }) { const [description, setDescription] = useState(""); const [quantity, setQuantity] = useState(1);
function handleSubmit(e) { e.preventDefault();
if (!description) return; const newItem = { description, quantity, packed: false, id: Date.now() };
onAddItems(newItem); setDescription(""); setQuantity(1); }
return ( <form className="add-form" onSubmit={handleSubmit}> <h3>What do you need for your trip?</h3> <select value={quantity} onChange={(e) => setQuantity(Number(e.target.value))} > {Array.from({ length: 20 }, (_, i) => i + 1).map((num) => ( <option value={num} key={num}> {num} </option> ))} </select> <input type="text" placeholder="Item..." value={description} onChange={(e) => setDescription(e.target.value)} /> <button>Add</button> </form> ); }
|
(4)Item.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export default function Item({ item, onDeleteItem, onToggleItem }) { return ( <li> {" "} <input type="checkbox" value={item.packed} onChange={() => onToggleItem(item.id)} />{" "} <span style={item.packed ? { textDecoration: "line-through" } : {}}> {" "} {item.quantity}🐶{item.description}{" "} </span>{" "} <button onClick={() => onDeleteItem(item.id)}>❌</button>{" "} </li> ); }
|
(5)PackingList.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import { useState } from "react"; import Item from "./Item";
export default function PackingList({ items, onDeleteItem, onToggleItem, onClearItems, }) { const [sortBy, setSortBy] = useState("input");
let sortedItems;
if (sortBy === "input") sortedItems = items; if (sortBy === "description") sortedItems = items .slice() .sort((a, b) => a.description.localeCompare(b.description)); if (sortBy === "packed") sortedItems = items .slice() .sort((a, b) => Number(a.packed) - Number(b.packed)); return ( <div className="list"> <ul> {sortedItems.map((item) => ( <Item item={item} onDeleteItem={onDeleteItem} key={item.id} onToggleItem={onToggleItem} /> ))} </ul>
<div className="actions"> <select value={sortBy} onChange={(e) => setSortBy(e.target.value)}> <option value="input">sort by input order</option> <option value="description">sort by description</option> <option value="packed">sort by packed</option> </select> <button onClick={onClearItems}>Clear List</button> </div> </div> ); }
|
(6)Stats.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| export default function Stats({ items }) { if (!items.length) return ( <p className="stats"> <em>Start adding some items to your packing list!</em> </p> ); const numItems = items.length; const numPacked = items.filter((item) => item.packed).length; const percentage = Math.round((numPacked / numItems) * 100); return ( <footer className="stats"> <em> {percentage === 100 ? "You got everything!Ready to go!" : `You have ${numItems} items on your list,and you already packed ${numPacked} (${percentage}%)`} </em> </footer> ); }
|
(7)Logo.js
1 2 3 4
| export default function Logo() { return <h1>🏞️Trip List🏜️</h1>; }
|
react课程5-TravelList
http://example.com/2024/08/16/react课程5-TravelList/