|
@@ -1,50 +1,91 @@
|
|
|
-import { useState } from 'react';
|
|
|
import './shop-list.css';
|
|
|
-
|
|
|
-const initialProducts = [
|
|
|
- {
|
|
|
- category: 'Sporting Goods',
|
|
|
- price: '$49.99',
|
|
|
- stocked: true,
|
|
|
- name: 'Football',
|
|
|
- },
|
|
|
- {
|
|
|
- category: 'Sporting Goods',
|
|
|
- price: '$9.99',
|
|
|
- stocked: true,
|
|
|
- name: 'Baseball',
|
|
|
- },
|
|
|
- {
|
|
|
- category: 'Sporting Goods',
|
|
|
- price: '$29.99',
|
|
|
- stocked: false,
|
|
|
- name: 'Basketball',
|
|
|
- },
|
|
|
- {
|
|
|
- category: 'Electronics',
|
|
|
- price: '$99.99',
|
|
|
- stocked: true,
|
|
|
- name: 'iPod Touch',
|
|
|
- },
|
|
|
- {
|
|
|
- category: 'Electronics',
|
|
|
- price: '$399.99',
|
|
|
- stocked: false,
|
|
|
- name: 'iPhone 5',
|
|
|
- },
|
|
|
- { category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7' },
|
|
|
-];
|
|
|
+import { Fragment } from 'react';
|
|
|
+import { useSelector, useDispatch } from 'react-redux';
|
|
|
+import { useEffect } from 'react';
|
|
|
+import {
|
|
|
+ setFilterName,
|
|
|
+ setShowStocked,
|
|
|
+ fetchProducts,
|
|
|
+} from '../store/slices/shop';
|
|
|
|
|
|
export default function ShopList() {
|
|
|
- let [products] = useState(() => initialProducts);
|
|
|
+ const dispatch = useDispatch();
|
|
|
+ let products = useSelector(({ shop }) => shop.products);
|
|
|
+ let filterName = useSelector(({ shop }) => shop.filterName);
|
|
|
+ let showStocked = useSelector(({ shop }) => shop.showStocked);
|
|
|
+
|
|
|
+ // 组件首次渲染后,获取数据
|
|
|
+ useEffect(() => {
|
|
|
+ dispatch(fetchProducts());
|
|
|
+ }, []);
|
|
|
+ let prevCategory = ''; // 上一次的分类标题
|
|
|
+
|
|
|
+ const renderProductsRow = () => {
|
|
|
+ // 根据 products, filterName, showStocked 得到要渲染的数据列表
|
|
|
+ // 1 showStocked = true ; product.name.includes(fitlerName) && product.stoked
|
|
|
+ // 2 showStoked = false; product.name.includes(fitlerName) && true
|
|
|
+ let renderProducts = products.filter(
|
|
|
+ (product) =>
|
|
|
+ product.name.includes(filterName) &&
|
|
|
+ (showStocked ? product.stocked : true)
|
|
|
+ );
|
|
|
+
|
|
|
+ return renderProducts.map((product) => {
|
|
|
+ let categoryElement = null;
|
|
|
+ if (prevCategory !== product.category) {
|
|
|
+ categoryElement = (
|
|
|
+ <tr>
|
|
|
+ <th colSpan={2}>{product.category}</th>
|
|
|
+ </tr>
|
|
|
+ );
|
|
|
+ // 只要重新绘制分类行,就需要将当前分类标题更新一下
|
|
|
+ prevCategory = product.category;
|
|
|
+ }
|
|
|
+
|
|
|
+ let productNameElement = <span>{product.name}</span>;
|
|
|
+ if (!product.stocked) {
|
|
|
+ productNameElement = (
|
|
|
+ <span
|
|
|
+ style={{
|
|
|
+ color: 'red',
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {product.name}
|
|
|
+ </span>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return (
|
|
|
+ <Fragment key={product.name}>
|
|
|
+ {categoryElement}
|
|
|
+ <tr>
|
|
|
+ <td>{productNameElement}</td>
|
|
|
+ <td>{product.price}</td>
|
|
|
+ </tr>
|
|
|
+ </Fragment>
|
|
|
+ );
|
|
|
+ });
|
|
|
+ };
|
|
|
|
|
|
return (
|
|
|
<div className="shop-list">
|
|
|
<h3 style={{ marginTop: '0' }}>商品列表</h3>
|
|
|
<div className="list-header">
|
|
|
- <input type="text" placeholder="Search..." />
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ placeholder="Search..."
|
|
|
+ value={filterName}
|
|
|
+ onChange={(e) => {
|
|
|
+ dispatch(setFilterName(e.target.value));
|
|
|
+ }}
|
|
|
+ />
|
|
|
<label>
|
|
|
- <input type="checkbox" />
|
|
|
+ <input
|
|
|
+ type="checkbox"
|
|
|
+ value={showStocked}
|
|
|
+ onChange={(e) => {
|
|
|
+ dispatch(setShowStocked(e.target.checked));
|
|
|
+ }}
|
|
|
+ />
|
|
|
仅显示在库商品
|
|
|
</label>
|
|
|
</div>
|
|
@@ -56,22 +97,7 @@ export default function ShopList() {
|
|
|
<th>商品价格</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
- <tbody>
|
|
|
- <tr>
|
|
|
- <th colSpan={2}>运动商品</th>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <td>篮球</td>
|
|
|
- <td>$19.9</td>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <th colSpan={2}>电子商品</th>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <td>Iphone 14 pm</td>
|
|
|
- <td>$1399.9</td>
|
|
|
- </tr>
|
|
|
- </tbody>
|
|
|
+ <tbody>{renderProductsRow()}</tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</div>
|