# 後端實作

在對應的 views 新增一個 API,取得此用戶的訂單紀錄。

# order_views.py
# 取得此用戶的訂單 進度-2024/12/12
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def getMyOrders(request):
    user = request.user
    orders = user.order_set.all()
    serializer = OrderSerializer(orders, many=True)
    return Response(serializer.data)

urls.py 新增一個路徑,對應到此 API,一定要寫在 <str:pk>/ 之前,因為 django 匹配路徑時,是從上往下找,匹配完成其他路徑就不會再繼續匹配

    # 要寫在 <str:pk>/ 之前,django 匹配路徑時,是從上往下找,匹配完成其他路徑就不會再找
    path('myorders/', views.getMyOrders, name='myorders'),

然後去 Postman 測試一下看後端是否建置成功,有的話應會出現此用戶的訂單列表回傳 JSON 資料。

# 前端實作

# Constants 定義

先到 redux 裡面新增一個 orderConstants.js,定義取得此用戶的訂單紀錄的常數。

// orderConstants.js

export const ORDER_LIST_MY_REQUEST = "ORDER_LIST_MY_REQUEST";
export const ORDER_LIST_MY_SUCCESS = "ORDER_LIST_MY_SUCCESS";
export const ORDER_LIST_MY_FAIL = "ORDER_LIST_MY_FAIL";
export const ORDER_LIST_MY_RESET = "ORDER_LIST_MY_RESET";

# Reducers 實作

然後將這些常數引入 orderReducers.js,並撰寫相關程式來處理這些常數。

// orderReducers.js
import{
  ...
  ORDER_LIST_MY_REQUEST,
  ORDER_LIST_MY_SUCCESS,
  ORDER_LIST_MY_FAIL,
  ORDER_LIST_MY_RESET,
} from '../constants/orderConstants'

// 用戶歷史訂單
export const orderListMyReducer = (state = {orders:[]}, action) => {
  switch (action.type) {
    case ORDER_LIST_MY_REQUEST:
      return {
        loading: true,
      };

    case ORDER_LIST_MY_SUCCESS:
      return {
        loading: false,
        // success:true
        orders: action.payload,
      };

    case ORDER_LIST_MY_FAIL:
      return {
        loading: false,
        error: action.payload,
      };

    case ORDER_LIST_MY_RESET:
      return {
        orders: [],
      };
    default:
      return state;
  }
};

然後在 store.js 引入這個 reducer

// store.js
import { ...,orderListMyReducer } from "./reducers/orderReducers";
...
const store = configureStore({
  reducer: {
    // 儲存多個state的地方,到時會再各個component用useSelector指定要使用哪一個
    ...
    // 訂單相關
    ...
    orderListMy:orderListMyReducer,
  },
  preloadedState: initialState,
  middleware: middleware,
});

# Actions 實作

到 action 裡面新增一個 action 來取得此用戶的訂單紀錄,這個 action 就不需要 id,因為是取得此用戶的訂單紀錄,不像 payOrder 或 getOrderDetails 需要 id。

// orderActions.js
// 列出用戶的所有訂單
export const listMyOrders = () => async (dispatch, getState) => {
 try {
  dispatch({
   type: ORDER_LIST_MY_REQUEST,
  });

  const {
   userLogin: { userInfo },
  } = getState();

  const config = {
   headers: {
    "Content-type": "application/json",
    Authorization: `Bearer ${userInfo.token}`,
   },
  };

  const { data } = await axios.get(`/api/orders/myorders/`, config);

  dispatch({
   type: ORDER_LIST_MY_SUCCESS,
   payload: data,
  });
 } catch (error) {
  dispatch({
   type: ORDER_LIST_MY_FAIL,
   payload: error.response && error.response.data.detail ? error.response.data.detail : error.message,
  });
 }
};

由於用戶登出後,要清空用戶的訂單紀錄,所以到 userActions.js 裡面新增一個 action 來清空用戶的訂單紀錄。

// userActions.js
export const logout = () => (dispatch) => {
  localStorage.removeItem("userInfo");
  ...
  dispatch({ type: ORDER_LIST_MY_RESET }) // 登出後清空訂單資料
};

# 訂單列表前端實作

接下來到 ProfilePage.js 來呼叫這個 action,並在 useEffect 裡面判斷是否有用戶登入,如果沒有登入就跳轉到登入頁面,如果有登入就取得此用戶的訂單紀錄。

// ProfilePage.js
useEffect(() => {
 if (!userInfo) {
  // 如果沒有登入就跳轉到登入頁面
  navigate("/login");
 } else {
  if (!user || !user.first_name || success) {
      ...
   dispatch(listMyOrders());
  } else {
      ...
  }
 }
}, [dispatch, navigate, userInfo, user, success]);

最後一步就是製作訂單列表的前端,這邊主要目的有:

  • 訂單列表在個人檔案的右側並佔據 5/6 的寬度,所以我們使用 Col=10 包住訂單區塊
  • 由於本身 Bootstrap 主題背景為白色,因此若要讓 Hover 效果明顯需要在 Table 加上 table-light 樣式,讓表格預設主題為淺白色
  • 判斷訂單是否已付款,如果已付款則顯示付款日期,如果未付款則顯示紅色的 X
  • 可以導向到訂單詳情頁面

table_code

由於 Table 標籤會導致此筆記頁面整個跑版,因此改用圖片代替,成品如下:

user_order_list