简洁容易上手的 react 后台管理应用模板设计 UI 库
中文 | English
- 只有一个 App 入口,无需设置 router、fetch、store
- React18+ React-router6+
- 约定式路由
- 基于 react-query 的简单易用的 fetch
- 基于 recoil,可以像 pinia 一样在 react 中定义全局管理状态
- 您可以在 react 中注册属性,而不是直接分配它们
- 缓存路由页面
- 使用 Ant Design UI
- 在每个组件中使用 Windicss(TailWind) 和 css 注入
- 使用 react-intl 实现国际化处理
- 使用 react-evefyou-hooks 定义可继承的状态钩子并支持 typecript
- 基于 Vite 生态构建
解析以$为前缀的文件,满足格式即可,可以不是 Vite
{
'/src/views/$.ts': () => import('/src/views/$.ts'),
'/src/views/dashboard/$index.tsx': () => import('/src/views/dashboard/$index.tsx'),
'/src/views/dashboard/$Other.tsx': () => import('/src/views/dashboard/$Other.tsx'),
}
| file | description |
|---|---|
| $.ts | Layout(nested Outlet if need) |
| $index.ts | default page |
| $other.tsx |
- pages
- login (Already built into the library)
$.ts
- views
$.ts (Already built into the library)
$index.ts
- dashboard
$index.tsx
- project
$index.tsx
$List.tsx
- other
$.ts
- views
$index.ts
- dashboard
$index.tsx
- project
$index.tsx
$List.tsx
import { queryFetch, queryFetchPage, MenuTreeList, Page } from "react-evefyou-app";
import { Project, ProjectReq } from '@models/project';
enum Api {
GetProjectList = '/getProjectList',
}
export const queryGetProjectList = queryFetchPage<Page<Project>, ProjectReq>({ url: Api.GetProjectList })
queryGetProjectList.useQuery({
params,
})
queryGetProjectList.useQueryRes({
params,
})
queryGetProjectList.fetchQuery({
params,
})
export const DEFAULT_USER_STATE: UserState = {
token: '',
userInfo: null,
isSessionTimeout: false,
lastUpdateTime: new Date().getTime()
}
export const userAtom = atom<UserState>({
key: 'userAtom',
default: DEFAULT_USER_STATE
});
export const useUserRecoilState = defineRecoilValue({
name: 'userState',
state: DEFAULT_USER_STATE,
getters: {
getToken(state) {
return state.token
},
...
},
setters: {
setToken(token: string) {
this.setProps({ token })
},
...
},
useFn: () => {
const loginMutation = mutationLogin.useMutation()
const navigate = useNavigate()
const [, { setProps: setAuthProps, refreshAuthAction }] = useAuthRecoilState()
return {
loginMutation,
setAuthProps,
refreshAuthAction,
navigate
}
},
actions: {
async login(
params: LoginByUsernameReq,
options?: {
goHome?: boolean;
mode?: ErrorMessageMode;
},
): Promise<Nullable<UserInfo>> {
try {
const { goHome = true } = options ?? {};
const { token } = await this.loginMutation.mutateAsync(params);
this.setToken(token);
...
} catch (error) {
return Promise.reject(error);
}
}
},
}, userAtom)
export const defineActiveItemsState = <
T extends KeyItem<K>,
K = T extends KeyItem<infer P> ? P : React.Key,
N extends string = string,
>(
name: N = 'activeItemsState' as N
) => {
const useKeyItemsState = defineKeyItemsState<T, K>()
return defineUseState({
name,
useState: (initialSt?: ActiveItem<T, K>) => useRelationState({
itemsState: useKeyItemsState(initialSt?.itemsState),
activeKeyState: useState(initialSt?.activeKeyState)
}),
getters: {
getActiveKey(state: ActiveItem<T, K>) {
return state.activeKeyState
},
...
},
setters: {
active(key: K) {
this.activeKeyState.set(key)
}
...
},
actions: {
removeByKey(key: K) {
...
}
...
}
})
}
export const useTabsContainerItemsState = defineUseState({
name: 'useTabsContainerItemsState',
useState: () => useActiveItemsState(),
getters: {
getViewTabItems(state) {
const items = state.itemsState
if (items.length === 1) {
items[0].closable = false;
} else if (items.length > 1) {
items[0].closable = true;
}
return items
}
}
})