後端
view(篩選+排序高評分產品)
- gte: greater than or equal to,只抓取評分 >= 4 的產品
- 排序方式為 rating 由高到低,只取前 5 個產品
1 2 3 4 5 6
| @api_view(['GET']) def getTopProducts(request): products = Product.objects.filter(rating__gte=4).order_by('-rating')[:5] serializer = ProductSerializer(products, many=True) return Response(serializer.data)
|
url
1 2
| path('top-rated/', views.getTopProducts,name="top_rated_products"),
|
此時去訪問 /api/products/top-rated/
,應能看到以下回傳:

前端
Constants
1 2 3 4
| export const PRODUCT_TOP_RATED_REQUEST = "PRODUCT_TOP_RATED_REQUEST"; export const PRODUCT_TOP_RATED_SUCCESS = "PRODUCT_TOP_RATED_SUCCESS"; export const PRODUCT_TOP_RATED_FAIL = "PRODUCT_TOP_RATED_FAIL";
|
Reducers
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
export const productTopRatedReducer = (state = { products: [] }, action) => { switch (action.type) { case PRODUCT_TOP_RATED_REQUEST: return { loading: true }; case PRODUCT_TOP_RATED_SUCCESS: return { loading: false, products: action.payload }; case PRODUCT_TOP_RATED_FAIL: return { loading: false, error: action.payload }; default: return state; } };
|
新增到 store.js
,一樣例行公事,先回 Redux DevTools 確認新增有新增成功:

Actions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export const listTopRatedProducts = () => async (dispatch) => { try { dispatch({ type: PRODUCT_TOP_RATED_REQUEST }); const { data } = await axios.get(`/api/products/top-rated/`); dispatch({ type: PRODUCT_TOP_RATED_SUCCESS, payload: data, }); } catch (error) { dispatch({ type: PRODUCT_TOP_RATED_FAIL, payload: error.response && error.response.data.message ? error.response.data.message : error.message, }); } };
|
Carousel 元件製作
<Carousel>
即為 Bootstrap 的輪播主體元件,<Carousel.Item>
為輪播的每個項目,<Image>
為顯示圖片的元件,而 <Carousel.Caption>
為顯示文字的元件
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
| import React, { useEffect } from "react"; import { Carousel, Image } from "react-bootstrap"; import { useDispatch, useSelector } from "react-redux"; import { Link } from "react-router-dom";
import Loader from "./Loader"; import Message from "./Message";
import { listTopRatedProducts } from "../actions/productActions";
const ProductCarousel = () => { const dispatch = useDispatch();
const { loading, error, products } = useSelector((state) => state.productTopRated);
useEffect(() => { dispatch(listTopRatedProducts()); }, [dispatch]);
return loading ? ( <Loader /> ) : error ? ( <Message variant='danger'>{error}</Message> ) : ( <Carousel pause='hover' className='product-carousel'> {products.map((product) => ( <Carousel.Item key={product.id} className='carousel-item'> <Link to={`/product/${product.id}`}> <div className='image-container'> <Image src={product.image} alt={product.name} className='carousel-image' /> </div> <Carousel.Caption className='carousel-caption'> <h2> {product.name} <span>${product.price}</span> </h2> </Carousel.Caption> </Link> </Carousel.Item> ))} </Carousel> ); };
export default ProductCarousel;
|
加到 HomePage.js
後,為了確保用戶在進行關鍵字搜尋的情況下,不顯示 Carousel 輪播,判斷無 keyword
時才顯示 Carousel:
1 2 3 4 5 6 7 8
| return ( <div> ... {!keyword && <ProductCarousel />} ... </div> );
|
最後自己寫 CSS 簡單調整一下 Carousel 的樣式,
- 固定圖片大小,並添加陰影效果及圓邊
- 讓產品及產品價格於
Carousel
相對位置的中間顯示
- Hover時能有放大產品效果
得到以下結果:

若您覺得這篇文章對您有幫助,歡迎分享出去讓更多人看到⊂◉‿◉つ~
留言版