u
This commit is contained in:
1 parent
6300778150
commit
e893ef2e48
27 files changed
+870
-33
No files matched your search
@@ -1,12 +0,0 @@
|
||||
import type { Method } from "alova";
|
||||
import type { AxiosError } from "axios";
|
||||
import { axiosRequestAdapter } from "@alova/adapter-axios";
|
||||
import { createAlova } from "alova";
|
||||
import { toast } from "sonner-native";
|
||||
|
||||
const alovaInstance = createAlova({
|
||||
baseURL: "",
|
||||
requestAdapter: axiosRequestAdapter(),
|
||||
timeout: 5000,
|
||||
cacheFor: null,
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import * as SplashScreen from "expo-splash-screen";
|
||||
import "@/global.css";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Stack } from "expo-router";
|
||||
import { Toaster } from "sonner-native";
|
||||
|
||||
SplashScreen.preventAutoHideAsync();
|
||||
|
||||
@@ -52,6 +53,8 @@ export default function RootLayout() {
|
||||
{!isDev && <StatusBar style={isDark ? "light" : "dark"} />}
|
||||
</HeroUINativeProvider>
|
||||
</ThemeProvider>
|
||||
|
||||
<Toaster duration={1000} />
|
||||
</GestureHandlerRootView>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,24 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import LayoutView from "@/components/layout/view";
|
||||
import { Text } from "react-native";
|
||||
import EmptyState from "@/components/EmptyStatus";
|
||||
import { loadDataApi } from "@/request/api";
|
||||
|
||||
export default function Home() {
|
||||
const [dataList, setDataList] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
loadDataApi({
|
||||
view_name: "v_finance_record",
|
||||
search_condition: "",
|
||||
search_columns: [],
|
||||
order_by: "",
|
||||
args: [],
|
||||
}).then((res) => {
|
||||
console.log(res);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<LayoutView>
|
||||
<LayoutView.SectionView<any>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { LoginQqParams, LoginResult, LoginWxParams } from "@/types/auth";
|
||||
import alovaInstance from "./index";
|
||||
|
||||
export function loginQqApi(params: LoginQqParams) {
|
||||
return alovaInstance.Post<ResponseData<LoginResult>>(`/auth/login/qq`, params);
|
||||
}
|
||||
|
||||
export function loginWxApi(params: LoginWxParams) {
|
||||
return alovaInstance.Post<ResponseData<LoginResult>>(`/auth/login/wechat`, params);
|
||||
}
|
||||
|
||||
interface LoadDataBySqlParams {
|
||||
sql: string;
|
||||
args: any[];
|
||||
}
|
||||
|
||||
export function loadDataBySqlApi(params: LoadDataBySqlParams) {
|
||||
return alovaInstance.Post<ResponseData<any[]>>(`/data/loadDataBySql`, params);
|
||||
}
|
||||
|
||||
interface LoadDataParams {
|
||||
view_name: string;
|
||||
search_condition: string;
|
||||
order_by: string;
|
||||
search_columns: string[];
|
||||
args: string[];
|
||||
}
|
||||
|
||||
export function loadDataApi(params: LoadDataParams) {
|
||||
return alovaInstance.Post<ResponseData<any[]>>(`/data/loadData`, params);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import type { Method } from "alova";
|
||||
import type { AxiosError } from "axios";
|
||||
import { axiosRequestAdapter } from "@alova/adapter-axios";
|
||||
import { createAlova } from "alova";
|
||||
import { toast } from "sonner-native";
|
||||
import { useUserStore } from "@/store/user";
|
||||
|
||||
enum HttpCode {
|
||||
Success = 1000,
|
||||
Unauthorized = 1001,
|
||||
Fail = 500,
|
||||
BusinessError = 2000,
|
||||
SystemError = 3000,
|
||||
}
|
||||
|
||||
// 仅保留「无需登录」接口
|
||||
const WhiteList = ["/auth/login/**"];
|
||||
|
||||
function isWhiteListed(url: string) {
|
||||
return WhiteList.some((pattern) => {
|
||||
if (pattern.endsWith("/**")) {
|
||||
const base = pattern.slice(0, -3);
|
||||
return url.startsWith(base);
|
||||
}
|
||||
return url === pattern;
|
||||
});
|
||||
}
|
||||
|
||||
const alovaInstance = createAlova({
|
||||
baseURL: process.env.EXPO_PUBLIC_API_URL,
|
||||
requestAdapter: axiosRequestAdapter(),
|
||||
timeout: 5000,
|
||||
cacheFor: null,
|
||||
|
||||
responded: {
|
||||
onSuccess: async (response, _method: Method) => {
|
||||
const resData = response.data as ResponseData<any>;
|
||||
|
||||
const result = {
|
||||
...resData,
|
||||
isSuccess: resData.code === HttpCode.Success,
|
||||
};
|
||||
|
||||
if (resData.code === HttpCode.Unauthorized) {
|
||||
toast.error("登录状态已过期,请重新登录");
|
||||
useUserStore.getState().setAuthExpired(true);
|
||||
return result;
|
||||
} else if (resData.code === HttpCode.BusinessError) {
|
||||
toast.error(resData.message || "业务异常,请稍后再试");
|
||||
return result;
|
||||
} else if (resData.code === HttpCode.SystemError) {
|
||||
toast.error(resData.message || "系统异常,请稍后再试");
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
onError: (error: AxiosError) => {
|
||||
return Promise.reject(error);
|
||||
},
|
||||
},
|
||||
|
||||
beforeRequest(method) {
|
||||
// 白名单接口直接放行
|
||||
if (isWhiteListed(method.url)) return;
|
||||
|
||||
const userStore = useUserStore.getState();
|
||||
|
||||
// 未登录直接拦截
|
||||
if (!userStore.isLogged()) {
|
||||
userStore.setAuthExpired(true);
|
||||
return Promise.reject(new Error("未登录"));
|
||||
}
|
||||
|
||||
// 注入 JWT
|
||||
method.config.headers.Token = userStore.userInfo!.token;
|
||||
},
|
||||
});
|
||||
|
||||
export default alovaInstance;
|
||||
@@ -4,9 +4,15 @@ import { createJSONStorage, persist } from "zustand/middleware";
|
||||
|
||||
interface UserState {
|
||||
userInfo: UserInfo;
|
||||
|
||||
isLogged: () => boolean;
|
||||
|
||||
setUserInfo: (userInfo: UserInfo) => void;
|
||||
|
||||
clear: () => void;
|
||||
|
||||
authExpired: boolean;
|
||||
setAuthExpired: (v: boolean) => void;
|
||||
}
|
||||
|
||||
const defaultUserInfo: UserInfo = {
|
||||
@@ -21,6 +27,8 @@ export const useUserStore = create<UserState>()(
|
||||
(set, get) => ({
|
||||
userInfo: defaultUserInfo,
|
||||
|
||||
authExpired: false,
|
||||
|
||||
isLogged: () => !!get().userInfo?.token,
|
||||
|
||||
setUserInfo: (userInfo) =>
|
||||
@@ -33,10 +41,19 @@ export const useUserStore = create<UserState>()(
|
||||
userInfo: defaultUserInfo,
|
||||
});
|
||||
},
|
||||
|
||||
setAuthExpired: (v: boolean) =>
|
||||
set({
|
||||
authExpired: v,
|
||||
}),
|
||||
}),
|
||||
{
|
||||
name: "user-storage",
|
||||
storage: createJSONStorage(() => AsyncStorage),
|
||||
|
||||
partialize: (state) => ({
|
||||
userInfo: state.userInfo,
|
||||
}),
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -0,0 +1,16 @@
|
||||
export interface LoginQqParams {
|
||||
openid: string
|
||||
nickname: string
|
||||
avatar: string
|
||||
}
|
||||
|
||||
export interface LoginWxParams {
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface LoginResult {
|
||||
token: string
|
||||
userId: number
|
||||
nickname: string
|
||||
avatar: string
|
||||
}
|
||||
+8
@@ -10,6 +10,14 @@ declare global {
|
||||
SystemError = 3000,
|
||||
}
|
||||
|
||||
interface ResponseData<T> {
|
||||
track_id: string;
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
isSuccess: boolean;
|
||||
}
|
||||
|
||||
interface UserInfo {
|
||||
id: number;
|
||||
token: string;
|
||||
|
||||
Reference in new issue
Block a user