Compare commits

...

9 Commits

Author SHA1 Message Date
john 47bc37ac2c Merge tag '1.0.2' into dev
v1.0.2 1.0.2
2024-06-03 18:44:10 +08:00
john 101fe2bb10 Merge branch 'release/1.0.2' 2024-06-03 18:44:07 +08:00
john b90d388e1f Merge tag 'v1.0.1-issue' into dev
v1.0.1-issue v1.0.1-issue
2024-06-03 18:43:16 +08:00
john 0140ed789a Merge branch 'hotfix/v1.0.1-issue' 2024-06-03 18:43:13 +08:00
john d4a96614b3 🐞 fix:
修复翻译
2024-06-03 18:42:17 +08:00
john cc0f5e2bf5 🐞 fix:
1、去掉接口language参数
2、修复Accept-Language参数
3、手机号校验
4、全局语言标识
2024-06-03 18:37:13 +08:00
john 8e7625c2be Merge tag '1.0.1' into dev
v1.0.1 1.0.1
2024-06-03 14:43:13 +08:00
john e4ad56be66 Merge branch 'release/1.0.1' 2024-06-03 14:42:56 +08:00
john 007c207950 🐞 fix:
添加版本号
2024-06-03 14:42:20 +08:00
8 changed files with 133 additions and 92 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "neer-app-h5", "name": "neer-app-h5",
"private": true, "private": true,
"version": "0.0.0", "version": "1.0.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@ -1,8 +1,10 @@
import axios, { AxiosRequestConfig, AxiosRequestHeaders } from 'axios'; import axios, { AxiosRequestConfig, AxiosRequestHeaders } from "axios";
//基础URLaxios将会自动拼接在url前 //基础URLaxios将会自动拼接在url前
//process.env.NODE_ENV 判断是否为开发环境 根据不同环境使用不同的baseURL 方便调试 //process.env.NODE_ENV 判断是否为开发环境 根据不同环境使用不同的baseURL 方便调试
const baseURL = import.meta.env.DEV ? import.meta.env.VITE_API_URL : import.meta.env.VITE_API_URL; const baseURL = import.meta.env.DEV
? import.meta.env.VITE_API_URL
: import.meta.env.VITE_API_URL;
//默认请求超时时间 //默认请求超时时间
const timeout = 30000; const timeout = 30000;
@ -11,34 +13,37 @@ const service = axios.create({
timeout, timeout,
baseURL, baseURL,
//如需要携带cookie 该值需设为true //如需要携带cookie 该值需设为true
withCredentials: true withCredentials: true,
}); });
// 统一请求拦截,可配置自定义 headers 例如 language、token 等 // 统一请求拦截,可配置自定义 headers 例如 language、token 等
service.interceptors.request.use( service.interceptors.request.use(
(config: any): any => { (config: any): any => {
// 确保 headers 对象存在 // 确保 headers 对象存在
if (!config.headers) { if (!config.headers) {
config.headers = {}; config.headers = {};
} }
// 配置自定义请求头 // 配置自定义请求头
const customHeaders: Partial<AxiosRequestHeaders> = { const customHeaders: Partial<AxiosRequestHeaders> = {
language: 'zh-cn', // language: "zh-cn",
// 这里可以添加其他自定义头信息,例如 token // 这里可以添加其他自定义头信息,例如 token
}; };
// 合并自定义头信息到 config.headers 中 // 合并自定义头信息到 config.headers 中
config.headers = { ...config.headers, ...customHeaders } as AxiosRequestHeaders; config.headers = {
...config.headers,
...customHeaders,
} as AxiosRequestHeaders;
return config; return config;
}, },
error => { (error) => {
console.log(error); console.log(error);
return Promise.reject(error); return Promise.reject(error);
} }
); );
//axios返回格式 //axios返回格式
interface axiosTypes<T>{ interface axiosTypes<T> {
data: T; data: T;
status: number; status: number;
statusText: string; statusText: string;
@ -47,66 +52,75 @@ interface axiosTypes<T>{
//后台响应数据格式 //后台响应数据格式
//###该接口用于规定后台返回的数据格式意为必须携带code、msg以及result //###该接口用于规定后台返回的数据格式意为必须携带code、msg以及result
//###而result的数据格式 由外部提供。如此即可根据不同需求,定制不同的数据格式 //###而result的数据格式 由外部提供。如此即可根据不同需求,定制不同的数据格式
interface responseTypes<T>{ interface responseTypes<T> {
code: number, code: number;
msg: string, msg: string;
result: T result: T;
} }
//核心处理代码 将返回一个promise 调用then将可获取响应的业务数据 //核心处理代码 将返回一个promise 调用then将可获取响应的业务数据
const requestHandler = <T>(method: 'get' | 'post' | 'put' | 'delete', url: string, params: object = {}, config: AxiosRequestConfig = {}): Promise<T> => { const requestHandler = <T>(
method: "get" | "post" | "put" | "delete",
url: string,
params: object = {},
config: AxiosRequestConfig = {}
): Promise<T> => {
let response: Promise<axiosTypes<responseTypes<T>>>; let response: Promise<axiosTypes<responseTypes<T>>>;
switch(method){ switch (method) {
case 'get': case "get":
response = service.get(url, {params: { ...params }, ...config}); response = service.get(url, { params: { ...params }, ...config });
break; break;
case 'post': case "post":
response = service.post(url, {...params}, {...config}); response = service.post(url, { ...params }, { ...config });
break; break;
case 'put': case "put":
response = service.put(url, {...params}, {...config}); response = service.put(url, { ...params }, { ...config });
break; break;
case 'delete': case "delete":
response = service.delete(url, {params: { ...params }, ...config}); response = service.delete(url, { params: { ...params }, ...config });
break; break;
} }
return new Promise<T>((resolve, reject) => { return new Promise<T>((resolve, reject) => {
response.then(res => { response
//业务代码 可根据需求自行处理 .then((res) => {
//业务代码 可根据需求自行处理
const data = res.data; const data = res.data;
if(data.code !== 200 && data.code !== 0){ if (data.code !== 200 && data.code !== 0) {
//特定状态码 处理特定的需求
//特定状态码 处理特定的需求 if (data.code == 401) {
if(data.code == 401){ console.log("登录异常,执行登出...");
console.log('登录异常,执行登出...'); }
const e = JSON.stringify(data);
console.log(`请求错误:${e}`);
//数据请求错误 使用reject将错误返回
reject(data);
} else {
//数据请求正确 使用resolve将结果返回
resolve(res as T);
} }
})
const e = JSON.stringify(data); .catch((error) => {
console.log(`请求错误:${e}`) const e = JSON.stringify(error);
//数据请求错误 使用reject将错误返回 console.log(`网络错误:${e}`);
reject(data); reject(error);
}else{ });
//数据请求正确 使用resolve将结果返回 });
resolve(res as T); };
}
}).catch(error => {
const e = JSON.stringify(error);
console.log(`网络错误:${e}`)
reject(error);
})
})
}
// 使用 request 统一调用包括封装的get、post、put、delete等方法 // 使用 request 统一调用包括封装的get、post、put、delete等方法
const request = { const request = {
get: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('get', url, params, config), get: <T>(url: string, params?: object, config?: AxiosRequestConfig) =>
post: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('post', url, params, config), requestHandler<T>("get", url, params, config),
put: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('put', url, params, config), post: <T>(url: string, params?: object, config?: AxiosRequestConfig) =>
delete: <T>(url: string, params?: object, config?: AxiosRequestConfig) => requestHandler<T>('delete', url, params, config) requestHandler<T>("post", url, params, config),
put: <T>(url: string, params?: object, config?: AxiosRequestConfig) =>
requestHandler<T>("put", url, params, config),
delete: <T>(url: string, params?: object, config?: AxiosRequestConfig) =>
requestHandler<T>("delete", url, params, config),
}; };
// 导出至外层,方便统一使用 // 导出至外层,方便统一使用
export { request }; export { request };

View File

@ -10,6 +10,7 @@ import { Toast } from "antd-mobile";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { Md5 } from "ts-md5"; import { Md5 } from "ts-md5";
import { FormInstance } from "antd-mobile/es/components/form"; import { FormInstance } from "antd-mobile/es/components/form";
import useUserStore from "../store/user";
function EmailForm() { function EmailForm() {
const formRef = useRef<FormInstance>(null); const formRef = useRef<FormInstance>(null);
@ -20,10 +21,11 @@ function EmailForm() {
const [authCode, setAuthCode] = useState(""); const [authCode, setAuthCode] = useState("");
const [shareCode, setShareCode] = useState(""); const [shareCode, setShareCode] = useState("");
const [remainingTime, setRemainingTime] = useState(0); const [remainingTime, setRemainingTime] = useState(0);
const { Lang } = useUserStore();
const config = { const config = {
headers: { headers: {
"Accept-Language": navigator.language, "Accept-Language": Lang,
}, },
}; };
const sendVerificationCode = async () => { const sendVerificationCode = async () => {

View File

@ -20,6 +20,7 @@ function PhoneForm() {
CurrentPhoneNumber, CurrentPhoneNumber,
UpdateCurrentPhoneNumber, UpdateCurrentPhoneNumber,
UpdatePreviousPathName, UpdatePreviousPathName,
Lang,
} = useUserStore(); } = useUserStore();
const getShareCode = (url: string) => { const getShareCode = (url: string) => {
@ -51,7 +52,7 @@ function PhoneForm() {
}; };
const config = { const config = {
headers: { headers: {
"Accept-Language": navigator.language, "Accept-Language": Lang,
}, },
}; };
const sendVerificationCode = async () => { const sendVerificationCode = async () => {
@ -153,6 +154,7 @@ function PhoneForm() {
console.warn(error); console.warn(error);
} }
}; };
return ( return (
<Form <Form
name="form" name="form"
@ -177,6 +179,14 @@ function PhoneForm() {
name="account" name="account"
rules={[ rules={[
{ required: true, message: t("Please enter your phone number") }, { required: true, message: t("Please enter your phone number") },
{
validator: (_, v) => {
if (v != "" && !/^\d+$/.test(v)) {
return Promise.reject(new Error(t("Invalid phone number")));
}
return Promise.resolve();
},
},
]} ]}
> >
<div className="phone-box"> <div className="phone-box">

View File

@ -19,5 +19,6 @@
"Official Website": "官方網站", "Official Website": "官方網站",
"Download": "下載", "Download": "下載",
"send successfully": "發送成功", "send successfully": "發送成功",
"Send failure": "發送失敗" "Send failure": "發送失敗",
} "Invalid phone number": "無效手機號碼"
}

View File

@ -1,23 +1,24 @@
{ {
"E-mail": "E-mail", "E-mail": "E-mail",
"phone":"phone", "phone": "phone",
"Please enter your email address":"Please enter your email address", "Please enter your email address": "Please enter your email address",
"Please enter the email verification code":"Please enter the email verification code", "Please enter the email verification code": "Please enter the email verification code",
"Please enter your phone number":"Please enter your phone number", "Please enter your phone number": "Please enter your phone number",
"Please enter SMS verification code":"Please enter SMS verification code", "Please enter SMS verification code": "Please enter SMS verification code",
"Send verification code":"Send verification code", "Send verification code": "Send verification code",
"Please enter the invitation code (optional)":"Please enter the invitation code (optional)", "Please enter the invitation code (optional)": "Please enter the invitation code (optional)",
"register":"Register", "register": "Register",
"If you already have an account":"If you already have an account", "If you already have an account": "If you already have an account",
"download the APP directly":"download the APP directly", "download the APP directly": "download the APP directly",
"Continuing to represent you in agreeing to our":"Continuing to represent you in agreeing to our", "Continuing to represent you in agreeing to our": "Continuing to represent you in agreeing to our",
"Terms of Service":"Terms of Service", "Terms of Service": "Terms of Service",
"and":"and", "and": "and",
"Privacy Policy":"Privacy Policy", "Privacy Policy": "Privacy Policy",
"Select country/region":"Select country/region", "Select country/region": "Select country/region",
"seconds": "s", "seconds": "s",
"Official Website":"Official Website", "Official Website": "Official Website",
"Download":"Download", "Download": "Download",
"send successfully":"send successfully", "send successfully": "send successfully",
"Send failure":"Send failure" "Send failure": "Send failure",
"Invalid phone number": "Invalid phone number"
} }

View File

@ -20,6 +20,7 @@ function SignIn() {
const [selectIndex, setSelectIndex] = useState<number>(0); const [selectIndex, setSelectIndex] = useState<number>(0);
const { previousPathName, UpdatePreviousPathName } = useUserStore(); const { previousPathName, UpdatePreviousPathName } = useUserStore();
const navigate = useNavigate(); const navigate = useNavigate();
const { UpdateLang } = useUserStore();
const handleTabs = (num: number) => { const handleTabs = (num: number) => {
return () => { return () => {
UpdatePreviousPathName("/"); UpdatePreviousPathName("/");
@ -70,6 +71,7 @@ function SignIn() {
onClick={() => { onClick={() => {
i18n.changeLanguage("cn"); i18n.changeLanguage("cn");
setVisible(false); setVisible(false);
UpdateLang("zh-TW");
}} }}
> >
@ -80,6 +82,7 @@ function SignIn() {
onClick={() => { onClick={() => {
i18n.changeLanguage("en"); i18n.changeLanguage("en");
setVisible(false); setVisible(false);
UpdateLang("en-US");
}} }}
> >
English English

View File

@ -1,21 +1,25 @@
/* /*
* @LastEditors: John * @LastEditors: John
* @Date: 2024-01-19 11:31:12 * @Date: 2024-01-19 11:31:12
* @LastEditTime: 2024-01-25 15:42:25 * @LastEditTime: 2024-06-03 14:52:45
* @Author: John * @Author: John
*/ */
import { create } from "zustand"; import { create } from "zustand";
import { CountryItem } from "../type/SelectCountry"; import { CountryItem } from "../type/SelectCountry";
export type LanguageCode = "en-US" | "zh-CN" | "zh-TW" | "ko-KR";
// 状态管理器 // 状态管理器
export interface UserState { export interface UserState {
SelectCountry: CountryItem | null; SelectCountry: CountryItem | null;
UpdateSelectCountry: (item: CountryItem) => void UpdateSelectCountry: (item: CountryItem) => void;
Token: string; Token: string;
UpdateToken: (token: string) => void; UpdateToken: (token: string) => void;
CurrentPhoneNumber:string; CurrentPhoneNumber: string;
UpdateCurrentPhoneNumber: (number: string) => void; UpdateCurrentPhoneNumber: (number: string) => void;
previousPathName:string; previousPathName: string;
UpdatePreviousPathName:(number: string) => void; UpdatePreviousPathName: (number: string) => void;
Lang: LanguageCode;
UpdateLang: (lan: LanguageCode) => void;
} }
const useUserStore = create<UserState>()((set) => ({ const useUserStore = create<UserState>()((set) => ({
@ -30,16 +34,22 @@ const useUserStore = create<UserState>()((set) => ({
set((state) => { set((state) => {
return { ...state, Token: token }; return { ...state, Token: token };
}), }),
CurrentPhoneNumber:'', CurrentPhoneNumber: "",
UpdateCurrentPhoneNumber: (val) => UpdateCurrentPhoneNumber: (val) =>
set((state) => { set((state) => {
return { ...state, CurrentPhoneNumber: val }; return { ...state, CurrentPhoneNumber: val };
}), }),
previousPathName:'/', previousPathName: "/",
UpdatePreviousPathName:(url) => UpdatePreviousPathName: (url) =>
set((state)=>{ set((state) => {
return {...state,previousPathName:url} return { ...state, previousPathName: url };
}) }),
Lang: "en-US",
UpdateLang: (Lang) => {
set((state) => {
return { ...state, Lang };
});
},
})); }));
export default useUserStore; export default useUserStore;