This commit is contained in:
2026-03-25 14:59:06 +08:00
commit ae315100b4
92 changed files with 9285 additions and 0 deletions

182
src/store/home/action.ts Normal file
View File

@@ -0,0 +1,182 @@
import type { Action } from "../Action";
import type {
HomeState,
SearchResult,
ReplyToType,
ProductSearchType,
} from "./state";
import type { Category, Reply } from "../../api/models";
export const SET_LOADING = "set_loading";
export const SET_CATEGORIES = "set_categories";
export const SET_CATEGORY_ID = "set_category_id";
export const SET_KEYWORD = "set_keyword";
export const SET_PAGE_INDEX = "set_page_index";
export const SET_IS_SEARCHING = "set_is_searching";
export const SET_SEARCH_RESULT = "set_search_result";
export const SET_REPLIES = "set_replies";
export const TOGGLE_ORDER_BY_PUBLISH_TIME = "toggle_order_by_publish_time";
export const TOGGLE_ORDER_BY_CATEGORY_ID = "toggle_order_by_category_id";
export const TOGGLE_ORDER_BY_REPLY_AMOUNT = "toggle_order_by_reply_amount";
export const SET_IS_REPLING = "set_is_repling";
export const SET_REPLY_TO = "set_reply_to";
export const SET_WILL_SEARCHING = "set_will_searching";
export const SET_SEARCH_PRODUCTS = "set_search_products";
export const SET_SEARCH_FOCUSED = "set_search_focused";
export type ActionsType =
| typeof SET_LOADING
| typeof SET_CATEGORIES
| typeof SET_CATEGORY_ID
| typeof SET_KEYWORD
| typeof SET_PAGE_INDEX
| typeof SET_IS_SEARCHING
| typeof SET_SEARCH_RESULT
| typeof SET_REPLIES
| typeof TOGGLE_ORDER_BY_PUBLISH_TIME
| typeof TOGGLE_ORDER_BY_CATEGORY_ID
| typeof TOGGLE_ORDER_BY_REPLY_AMOUNT
| typeof SET_IS_REPLING
| typeof SET_REPLY_TO
| typeof SET_WILL_SEARCHING
| typeof SET_SEARCH_PRODUCTS
| typeof SET_SEARCH_FOCUSED;
export type HomeAction = Action<ActionsType, HomeState>;
export function setLoading(isLoading: boolean): HomeAction {
return {
type: "set_loading",
payload: {
isLoading,
},
};
}
export function setCategories(categories: Array<Category>): HomeAction {
return {
type: "set_categories",
payload: {
categories,
},
};
}
export function setCategoryId(categoryId: number): HomeAction {
return {
type: "set_category_id",
payload: {
categoryId,
},
};
}
export function setKeyword(keyword: string): HomeAction {
return {
type: "set_keyword",
payload: {
keyword,
},
};
}
export function setPageIndex(pageIndex: number): HomeAction {
return {
type: "set_page_index",
payload: {
pageIndex,
},
};
}
export function setIsSearch(isSearching: boolean): HomeAction {
return {
type: "set_is_searching",
payload: {
isSearching,
},
};
}
export function setSearchResult(result: SearchResult): HomeAction {
return {
type: "set_search_result",
payload: {
...result,
},
};
}
export function setReplies(replies: Array<Reply> | null): HomeAction {
return {
type: "set_replies",
payload: {
replies,
},
};
}
export function toggleOrderByPublishTime(): HomeAction {
return {
type: "toggle_order_by_publish_time",
};
}
export function toggleOrderByCategoryId(): HomeAction {
return {
type: "toggle_order_by_category_id",
};
}
export function toggleOrderByReplyAmount(): HomeAction {
return {
type: "toggle_order_by_reply_amount",
};
}
export function setIsRepling(isRepling: boolean): HomeAction {
return {
type: "set_is_repling",
payload: {
isRepling,
},
};
}
export function setReplyTo(reply: Partial<ReplyToType>): HomeAction {
return {
type: "set_reply_to",
payload: {
reply,
},
};
}
export function setWillSearching(willSearching: boolean): HomeAction {
return {
type: "set_will_searching",
payload: {
willSearching,
},
};
}
export function setSearchProducts(
products: Array<ProductSearchType>,
): HomeAction {
return {
type: "set_search_products",
payload: {
products,
},
};
}
export function setSearchFocused(searchFocused: boolean): HomeAction {
return {
type: "set_search_focused",
payload: {
searchFocused,
},
};
}

3
src/store/home/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from "./action";
export * from "./reducer";
export * from "./state";

168
src/store/home/reducer.ts Normal file
View File

@@ -0,0 +1,168 @@
import type { HomeAction } from "./action";
import type { HomeState } from "./state";
export const initialState: HomeState = {
categories: [],
isLoading: true,
pageIndex: 1,
categoryId: 0,
keyword: "",
isSearching: false,
pageCount: 0,
pageSize: 10,
requests: [],
orderBys: {
categoryId: null,
publishTime: "desc",
replyAmount: null,
},
replies: null,
isRepling: false,
reply: {
amount: 0,
categoryId: 0,
memo: "",
price: 0,
productId: 0,
productName: "",
requestId: 0,
},
searchFocused: false,
willSearching: false,
products: [],
};
export const homeReducer = (
state: HomeState,
action: HomeAction,
): HomeState => {
switch (action.type) {
case "set_loading": {
return {
...state,
isLoading: action.payload?.isLoading || false,
};
}
case "set_categories": {
return {
...state,
categories: action.payload?.categories || [],
};
}
case "set_category_id": {
return {
...state,
categoryId: action.payload?.categoryId || 0,
};
}
case "set_is_searching": {
return {
...state,
isSearching: action.payload?.isSearching || false,
};
}
case "set_keyword": {
return {
...state,
keyword: action.payload?.keyword || "",
};
}
case "set_page_index": {
return {
...state,
pageIndex: action.payload?.pageIndex || 1,
};
}
case "set_search_result": {
return {
...state,
requests:
state.pageIndex == 1
? action.payload?.requests || []
: [...state.requests, ...(action.payload?.requests || [])],
pageCount: action.payload?.pageCount || 0,
};
}
case "set_replies": {
return {
...state,
isRepling: false,
replies: action.payload?.replies || null,
};
}
case "toggle_order_by_publish_time": {
return {
...state,
orderBys: {
...state.orderBys,
publishTime:
!state.orderBys.publishTime || state.orderBys.publishTime == "desc"
? "asc"
: "desc",
},
};
}
case "toggle_order_by_category_id": {
return {
...state,
orderBys: {
...state.orderBys,
categoryId:
!state.orderBys.categoryId || state.orderBys.categoryId == "desc"
? "asc"
: "desc",
},
};
}
case "toggle_order_by_reply_amount": {
return {
...state,
orderBys: {
...state.orderBys,
replyAmount:
!state.orderBys.replyAmount || state.orderBys.replyAmount == "desc"
? "asc"
: "desc",
},
};
}
case "set_is_repling": {
return {
...state,
isRepling: action.payload?.isRepling ?? false,
};
}
case "set_reply_to": {
return {
...state,
isRepling: true,
willSearching: false,
reply: {
...state.reply,
...action.payload?.reply,
},
};
}
case "set_will_searching": {
return {
...state,
willSearching: action.payload?.willSearching ?? false,
};
}
case "set_search_products": {
return {
...state,
willSearching: false,
products: action.payload?.products ?? [],
};
}
case "set_search_focused": {
return {
...state,
searchFocused: action.payload?.searchFocused ?? false,
};
}
default:
throw Error(`未知操作(${action.type})`);
}
};

65
src/store/home/state.ts Normal file
View File

@@ -0,0 +1,65 @@
import type { Category } from "../../api/models";
import type { Request } from "../../api/models";
import type { Reply } from "../../api/models";
import type { OrderByTypes } from "../Types";
interface OrderByState {
publishTime: OrderByTypes;
categoryId: OrderByTypes;
replyAmount: OrderByTypes;
}
interface SearchState {
pageIndex: number;
pageSize: number;
categoryId: number;
keyword: string;
orderBys: OrderByState;
isSearching: boolean;
}
export interface SearchResult {
requests: Array<Request>;
pageCount: number;
}
export interface RequestReplyState {
replies: Array<Reply> | null;
}
export interface ReplyToType {
categoryId: number;
requestId: number;
productId: number;
productName: string;
amount: number;
price: number;
memo: string;
}
export interface ReplyToState {
isRepling: boolean;
reply: Partial<ReplyToType>;
}
export interface ProductSearchType {
productId: number;
productName: string;
productDescription: string | null;
price: number;
}
export interface ProductSearchState {
searchFocused: boolean;
willSearching: boolean;
products: Array<ProductSearchType>;
}
export type HomeState = {
categories: Array<Category>;
isLoading: boolean;
} & SearchState &
SearchResult &
RequestReplyState &
ReplyToState &
ProductSearchState;