✨
This commit is contained in:
4
src/store/Action.ts
Normal file
4
src/store/Action.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface Action<A, T> {
|
||||
type: A;
|
||||
payload?: Partial<T>;
|
||||
}
|
||||
13
src/store/AppContextProvider.tsx
Normal file
13
src/store/AppContextProvider.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { useReducer, type PropsWithChildren } from "react";
|
||||
import { appContextReducer, initialState } from "./appcontext";
|
||||
import { AppContext, AppContextDispatch } from "./AppContexts";
|
||||
|
||||
export function AppContextProvider({ children }: PropsWithChildren) {
|
||||
const [state, dispatch] = useReducer(appContextReducer, initialState);
|
||||
|
||||
return (
|
||||
<AppContext value={state}>
|
||||
<AppContextDispatch value={dispatch}>{children}</AppContextDispatch>
|
||||
</AppContext>
|
||||
);
|
||||
}
|
||||
11
src/store/AppContexts.ts
Normal file
11
src/store/AppContexts.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { createContext, type ActionDispatch } from "react";
|
||||
import {
|
||||
type AppContextType,
|
||||
type AppContextAction,
|
||||
initialState,
|
||||
} from "./appcontext";
|
||||
|
||||
export const AppContext = createContext<AppContextType>(initialState);
|
||||
export const AppContextDispatch = createContext<
|
||||
ActionDispatch<[AppContextAction]>
|
||||
>(() => {});
|
||||
7
src/store/Types.ts
Normal file
7
src/store/Types.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const orderByTypes = {
|
||||
ASC: "asc",
|
||||
DESC: "desc",
|
||||
} as const;
|
||||
export type OrderByTypes =
|
||||
| (typeof orderByTypes)[keyof typeof orderByTypes]
|
||||
| null;
|
||||
9
src/store/appcontext/action.ts
Normal file
9
src/store/appcontext/action.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { Action } from "../Action";
|
||||
import type { AppContextType } from "./state";
|
||||
|
||||
export const SWITCH_THEME = "SWITCH_THEME";
|
||||
export const RESTORE_APPCONTEXT = "RESTORE_APPCONTEXT";
|
||||
|
||||
type ActionType = typeof SWITCH_THEME | typeof RESTORE_APPCONTEXT;
|
||||
|
||||
export type AppContextAction = Action<ActionType, AppContextType>;
|
||||
3
src/store/appcontext/index.ts
Normal file
3
src/store/appcontext/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./action";
|
||||
export * from "./reducer";
|
||||
export * from "./state";
|
||||
28
src/store/appcontext/reducer.ts
Normal file
28
src/store/appcontext/reducer.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { AppContextAction } from "./action";
|
||||
import { Themes, type AppContextType } from "./state";
|
||||
|
||||
export const initialState: AppContextType = {
|
||||
theme: "light",
|
||||
};
|
||||
|
||||
export const appContextReducer = (
|
||||
state: AppContextType,
|
||||
action: AppContextAction,
|
||||
): AppContextType => {
|
||||
switch (action.type) {
|
||||
case "SWITCH_THEME": {
|
||||
return {
|
||||
...state,
|
||||
theme: state.theme === Themes.Dark ? Themes.Light : Themes.Dark,
|
||||
};
|
||||
}
|
||||
case "RESTORE_APPCONTEXT": {
|
||||
return {
|
||||
...state,
|
||||
...action.payload,
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw Error(`wrong action:${action.type}`);
|
||||
}
|
||||
};
|
||||
13
src/store/appcontext/state.ts
Normal file
13
src/store/appcontext/state.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export const Themes = {
|
||||
Dark: "dark",
|
||||
Light: "light",
|
||||
} as const;
|
||||
export type ThemesType = (typeof Themes)[keyof typeof Themes];
|
||||
|
||||
interface AppContext {
|
||||
theme: ThemesType;
|
||||
nickName: string;
|
||||
avatarUrl: string | null;
|
||||
}
|
||||
|
||||
export type AppContextType = Partial<AppContext>;
|
||||
182
src/store/home/action.ts
Normal file
182
src/store/home/action.ts
Normal 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
3
src/store/home/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./action";
|
||||
export * from "./reducer";
|
||||
export * from "./state";
|
||||
168
src/store/home/reducer.ts
Normal file
168
src/store/home/reducer.ts
Normal 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
65
src/store/home/state.ts
Normal 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;
|
||||
135
src/store/product/action.ts
Normal file
135
src/store/product/action.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import type { Action } from "../Action";
|
||||
import type { ProductState, SearchResult, EditProductType } from "./state";
|
||||
import type { Category } from "../../api/models";
|
||||
|
||||
export const SET_LOADING = "set_loading";
|
||||
export const SET_CATEGORIES = "set_categories";
|
||||
export const SET_CATEGORY = "set_category";
|
||||
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_EDIT_PRODUCT = "set_edit_product";
|
||||
export const REMOVE_PRODUCT = "remove_product";
|
||||
export const TOGGLE_ORDER_BY_CREATE_TIME = "toggle_order_by_create_time";
|
||||
export const TOGGLE_ORDER_BY_CATEGORY_ID = "toggle_order_by_category_id";
|
||||
export const TOGGLE_ORDER_BY_SOLD_AMOUNT = "toggle_order_by_sold_amount";
|
||||
|
||||
export type ActionsType =
|
||||
| typeof SET_LOADING
|
||||
| typeof SET_CATEGORIES
|
||||
| typeof SET_CATEGORY
|
||||
| typeof SET_KEYWORD
|
||||
| typeof SET_PAGE_INDEX
|
||||
| typeof SET_IS_SEARCHING
|
||||
| typeof SET_SEARCH_RESULT
|
||||
| typeof SET_EDIT_PRODUCT
|
||||
| typeof REMOVE_PRODUCT
|
||||
| typeof TOGGLE_ORDER_BY_CREATE_TIME
|
||||
| typeof TOGGLE_ORDER_BY_CATEGORY_ID
|
||||
| typeof TOGGLE_ORDER_BY_SOLD_AMOUNT;
|
||||
|
||||
export type ProductAction = Action<ActionsType, ProductState>;
|
||||
|
||||
export function setLoading(isLoading: boolean): ProductAction {
|
||||
return {
|
||||
type: "set_loading",
|
||||
payload: {
|
||||
isLoading,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setCategories(categories: Array<Category>): ProductAction {
|
||||
return {
|
||||
type: "set_categories",
|
||||
payload: {
|
||||
categories,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setCategory(category: Category): ProductAction {
|
||||
return {
|
||||
type: "set_category",
|
||||
payload: {
|
||||
categoryId: category.id,
|
||||
editProduct: {
|
||||
categoryId: category.id,
|
||||
categoryName: category.name,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setKeyword(keyword: string): ProductAction {
|
||||
return {
|
||||
type: "set_keyword",
|
||||
payload: {
|
||||
keyword,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setPageIndex(pageIndex: number): ProductAction {
|
||||
return {
|
||||
type: "set_page_index",
|
||||
payload: {
|
||||
pageIndex,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setIsSearch(isSearching: boolean): ProductAction {
|
||||
return {
|
||||
type: "set_is_searching",
|
||||
payload: {
|
||||
isSearching,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setSearchResult(result: SearchResult): ProductAction {
|
||||
return {
|
||||
type: "set_search_result",
|
||||
payload: {
|
||||
...result,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setEditProduct(product: EditProductType): ProductAction {
|
||||
return {
|
||||
type: "set_edit_product",
|
||||
payload: {
|
||||
editProduct: product,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function removeProduct(productId: number): ProductAction {
|
||||
return {
|
||||
type: "remove_product",
|
||||
payload: {
|
||||
removedProductId: productId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleOrderByCreateTime(): ProductAction {
|
||||
return {
|
||||
type: "toggle_order_by_create_time",
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleOrderByCategoryId(): ProductAction {
|
||||
return {
|
||||
type: "toggle_order_by_category_id",
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleOrderBySoldAmount(): ProductAction {
|
||||
return {
|
||||
type: "toggle_order_by_sold_amount",
|
||||
};
|
||||
}
|
||||
3
src/store/product/index.ts
Normal file
3
src/store/product/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./action";
|
||||
export * from "./reducer";
|
||||
export * from "./state";
|
||||
144
src/store/product/reducer.ts
Normal file
144
src/store/product/reducer.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import type { ProductAction } from "./action";
|
||||
import type { ProductState } from "./state";
|
||||
|
||||
export const initialState: ProductState = {
|
||||
categories: [],
|
||||
isLoading: true,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
categoryId: 0,
|
||||
keyword: "",
|
||||
isSearching: false,
|
||||
pageCount: 0,
|
||||
products: [],
|
||||
removedProductId: 0,
|
||||
editProduct: {
|
||||
categoryId: undefined,
|
||||
description: null,
|
||||
detail: null,
|
||||
id: 0,
|
||||
logoName: null,
|
||||
logoUrl: null,
|
||||
minimumUnit: null,
|
||||
name: null,
|
||||
unitPrice: undefined,
|
||||
},
|
||||
orderBys: {
|
||||
createTime: "desc",
|
||||
categoryId: null,
|
||||
soldAmount: null,
|
||||
},
|
||||
};
|
||||
|
||||
export const productReducer = (
|
||||
state: ProductState,
|
||||
action: ProductAction,
|
||||
): ProductState => {
|
||||
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": {
|
||||
return {
|
||||
...state,
|
||||
categoryId: action.payload?.categoryId || 0,
|
||||
editProduct: {
|
||||
...state.editProduct,
|
||||
...action.payload?.editProduct,
|
||||
},
|
||||
};
|
||||
}
|
||||
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,
|
||||
products:
|
||||
state.pageIndex == 1
|
||||
? action.payload?.products || []
|
||||
: [...state.products, ...(action.payload?.products || [])],
|
||||
pageCount: action.payload?.pageCount || 0,
|
||||
};
|
||||
}
|
||||
case "set_edit_product": {
|
||||
return {
|
||||
...state,
|
||||
editProduct: {
|
||||
...state.editProduct,
|
||||
...(action.payload?.editProduct || {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
case "remove_product": {
|
||||
return {
|
||||
...state,
|
||||
products: state.products.filter(
|
||||
(p) => p.id != action.payload?.removedProductId,
|
||||
),
|
||||
};
|
||||
}
|
||||
case "toggle_order_by_create_time": {
|
||||
return {
|
||||
...state,
|
||||
orderBys: {
|
||||
...state.orderBys,
|
||||
createTime:
|
||||
!state.orderBys.createTime || state.orderBys.createTime == "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_sold_amount": {
|
||||
return {
|
||||
...state,
|
||||
orderBys: {
|
||||
...state.orderBys,
|
||||
soldAmount:
|
||||
!state.orderBys.soldAmount || state.orderBys.soldAmount == "desc"
|
||||
? "asc"
|
||||
: "desc",
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw Error(`未知操作(${action.type})`);
|
||||
}
|
||||
};
|
||||
34
src/store/product/state.ts
Normal file
34
src/store/product/state.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { Category, Product, EditProductParams } from "../../api/models";
|
||||
import type { OrderByTypes } from "../Types";
|
||||
|
||||
interface OrderByState {
|
||||
createTime: OrderByTypes;
|
||||
categoryId: OrderByTypes;
|
||||
soldAmount: OrderByTypes;
|
||||
}
|
||||
|
||||
interface SearchState {
|
||||
pageIndex: number;
|
||||
pageSize: number;
|
||||
categoryId: number;
|
||||
keyword: string;
|
||||
orderBys: OrderByState;
|
||||
isSearching: boolean;
|
||||
}
|
||||
|
||||
export interface SearchResult {
|
||||
products: Array<Product>;
|
||||
pageCount: number;
|
||||
}
|
||||
|
||||
export type EditProductType = Partial<
|
||||
EditProductParams & { logoUrl: string | null; categoryName: string | null }
|
||||
>;
|
||||
|
||||
export type ProductState = {
|
||||
categories: Array<Category>;
|
||||
isLoading: boolean;
|
||||
editProduct: EditProductType;
|
||||
removedProductId: number;
|
||||
} & SearchState &
|
||||
SearchResult;
|
||||
144
src/store/request/action.ts
Normal file
144
src/store/request/action.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import type { Action } from "../Action";
|
||||
import type { RequestState, SearchResult } from "./state";
|
||||
import type { Category, Reply, CreateRequestParams } 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_PUBLISH = "set_is_publish";
|
||||
export const SET_REQUEST_PUBLISH = "set_request_publish";
|
||||
|
||||
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_PUBLISH
|
||||
| typeof SET_REQUEST_PUBLISH;
|
||||
|
||||
export type RequestAction = Action<ActionsType, RequestState>;
|
||||
|
||||
export function setLoading(isLoading: boolean): RequestAction {
|
||||
return {
|
||||
type: "set_loading",
|
||||
payload: {
|
||||
isLoading,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setCategories(categories: Array<Category>): RequestAction {
|
||||
return {
|
||||
type: "set_categories",
|
||||
payload: {
|
||||
categories,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setCategoryId(categoryId: number): RequestAction {
|
||||
return {
|
||||
type: "set_category_id",
|
||||
payload: {
|
||||
categoryId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setKeyword(keyword: string): RequestAction {
|
||||
return {
|
||||
type: "set_keyword",
|
||||
payload: {
|
||||
keyword,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setPageIndex(pageIndex: number): RequestAction {
|
||||
return {
|
||||
type: "set_page_index",
|
||||
payload: {
|
||||
pageIndex,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setIsSearch(isSearching: boolean): RequestAction {
|
||||
return {
|
||||
type: "set_is_searching",
|
||||
payload: {
|
||||
isSearching,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setSearchResult(result: SearchResult): RequestAction {
|
||||
return {
|
||||
type: "set_search_result",
|
||||
payload: {
|
||||
...result,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setReplies(replies: Array<Reply> | null): RequestAction {
|
||||
return {
|
||||
type: "set_replies",
|
||||
payload: {
|
||||
replies,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleOrderByPublishTime(): RequestAction {
|
||||
return {
|
||||
type: "toggle_order_by_publish_time",
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleOrderByCategoryId(): RequestAction {
|
||||
return {
|
||||
type: "toggle_order_by_category_id",
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleOrderByReplyAmount(): RequestAction {
|
||||
return {
|
||||
type: "toggle_order_by_reply_amount",
|
||||
};
|
||||
}
|
||||
|
||||
export function setIsPublish(isPublish: boolean): RequestAction {
|
||||
return {
|
||||
type: "set_is_publish",
|
||||
payload: {
|
||||
isPublish,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setRequestPublish(
|
||||
requestPublish: Partial<CreateRequestParams>,
|
||||
): RequestAction {
|
||||
return {
|
||||
type: "set_request_publish",
|
||||
payload: {
|
||||
requestPublish,
|
||||
},
|
||||
};
|
||||
}
|
||||
3
src/store/request/index.ts
Normal file
3
src/store/request/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./action";
|
||||
export * from "./reducer";
|
||||
export * from "./state";
|
||||
140
src/store/request/reducer.ts
Normal file
140
src/store/request/reducer.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import type { RequestAction } from "./action";
|
||||
import type { RequestState } from "./state";
|
||||
|
||||
export const initialState: RequestState = {
|
||||
categories: [],
|
||||
isLoading: true,
|
||||
pageIndex: 1,
|
||||
categoryId: 0,
|
||||
keyword: "",
|
||||
isSearching: false,
|
||||
pageCount: 0,
|
||||
pageSize: 10,
|
||||
requests: [],
|
||||
orderBys: {
|
||||
categoryId: null,
|
||||
publishTime: "desc",
|
||||
replyAmount: null,
|
||||
},
|
||||
replies: null,
|
||||
isPublish: false,
|
||||
requestPublish: {
|
||||
categoryId: 0,
|
||||
deadline: "",
|
||||
description: null,
|
||||
name: "",
|
||||
},
|
||||
};
|
||||
|
||||
export const requestReducer = (
|
||||
state: RequestState,
|
||||
action: RequestAction,
|
||||
): RequestState => {
|
||||
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,
|
||||
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_publish": {
|
||||
return {
|
||||
...state,
|
||||
isPublish: action.payload?.isPublish ?? false,
|
||||
};
|
||||
}
|
||||
case "set_request_publish": {
|
||||
return {
|
||||
...state,
|
||||
requestPublish: {
|
||||
...state.requestPublish,
|
||||
...action.payload?.requestPublish,
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw Error(`未知操作(${action.type})`);
|
||||
}
|
||||
};
|
||||
44
src/store/request/state.ts
Normal file
44
src/store/request/state.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type {
|
||||
Category,
|
||||
Request,
|
||||
CreateRequestParams,
|
||||
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 PublishState {
|
||||
isPublish: boolean;
|
||||
requestPublish: Partial<CreateRequestParams>;
|
||||
}
|
||||
|
||||
export type RequestState = {
|
||||
categories: Array<Category>;
|
||||
isLoading: boolean;
|
||||
} & SearchState &
|
||||
SearchResult &
|
||||
RequestReplyState &
|
||||
PublishState;
|
||||
40
src/store/signin/action.ts
Normal file
40
src/store/signin/action.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { type Action } from "../Action";
|
||||
import type { SignInState, SignInType, SignUpType } from "./state";
|
||||
|
||||
export const SET_SIGN_IN = "set_sign_in";
|
||||
export const SET_SIGN_UP = "set_sign_up";
|
||||
export const SET_IS_SUBMITING = "set_is_submiting";
|
||||
|
||||
export type ActionType =
|
||||
| typeof SET_SIGN_IN
|
||||
| typeof SET_SIGN_UP
|
||||
| typeof SET_IS_SUBMITING;
|
||||
|
||||
export type SignInAction = Action<ActionType, SignInState>;
|
||||
|
||||
export function setSignIn(signIn: SignInType): SignInAction {
|
||||
return {
|
||||
type: "set_sign_in",
|
||||
payload: {
|
||||
signInUser: signIn,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setSignUp(signUp: SignUpType): SignInAction {
|
||||
return {
|
||||
type: "set_sign_up",
|
||||
payload: {
|
||||
signUpUser: signUp,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setIsSubmiting(isSubmiting: boolean): SignInAction {
|
||||
return {
|
||||
type: "set_is_submiting",
|
||||
payload: {
|
||||
isSubmiting,
|
||||
},
|
||||
};
|
||||
}
|
||||
3
src/store/signin/index.ts
Normal file
3
src/store/signin/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./action";
|
||||
export * from "./reducer";
|
||||
export * from "./state";
|
||||
47
src/store/signin/reducer.ts
Normal file
47
src/store/signin/reducer.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { type SignInState } from "./state";
|
||||
import { type SignInAction } from "./action";
|
||||
|
||||
export const initialState: SignInState = {
|
||||
isSubmiting: false,
|
||||
signInUser: {
|
||||
account: "",
|
||||
password: "",
|
||||
},
|
||||
signUpUser: {
|
||||
account: "",
|
||||
defaultRole: "Buyer",
|
||||
nickName: "",
|
||||
password: "",
|
||||
},
|
||||
};
|
||||
|
||||
export function signInReducer(
|
||||
state: SignInState,
|
||||
action: SignInAction,
|
||||
): SignInState {
|
||||
switch (action.type) {
|
||||
case "set_sign_in":
|
||||
return {
|
||||
...state,
|
||||
signInUser: {
|
||||
...state.signInUser,
|
||||
...action.payload?.signInUser,
|
||||
},
|
||||
};
|
||||
case "set_sign_up":
|
||||
return {
|
||||
...state,
|
||||
signUpUser: {
|
||||
...state.signUpUser,
|
||||
...action.payload?.signUpUser,
|
||||
},
|
||||
};
|
||||
case "set_is_submiting":
|
||||
return {
|
||||
...state,
|
||||
isSubmiting: action.payload?.isSubmiting ?? false,
|
||||
};
|
||||
default:
|
||||
throw Error(`wrong action type:${action.type}`);
|
||||
}
|
||||
}
|
||||
10
src/store/signin/state.ts
Normal file
10
src/store/signin/state.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { SignInParams, SignUpParams } from "../../api/models";
|
||||
|
||||
export type SignInType = Partial<SignInParams>;
|
||||
export type SignUpType = Partial<SignUpParams & { confirmPassword: string }>;
|
||||
|
||||
export interface SignInState {
|
||||
isSubmiting: boolean;
|
||||
signInUser: SignInType;
|
||||
signUpUser: SignUpType;
|
||||
}
|
||||
Reference in New Issue
Block a user