diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index 389d8e3..9e327a5 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -1,37 +1,56 @@ +import { Layout, Tabs, Input, Button, Typography, Row, Col, Form } from "antd"; +import { QrcodeOutlined, UserOutlined, LockOutlined } from "@ant-design/icons"; import SelectLang from "@/components/select-lang"; import { createFileRoute } from "@tanstack/react-router"; -import { Button, Form, Input, Radio, Space } from "antd"; import { useAtom, useAtomValue } from "jotai"; import { useTranslation } from "@/i18n.ts"; -import { loginAtom, loginFormAtom } from "@/store/system/user.ts"; -import React, {memo, useLayoutEffect, useState} from "react"; +import { emailCodeAtom, emailLoginAtom, upLoginAtom, upLoginFormAtom } from "@/store/system/user.ts"; +import React, { memo, useEffect, useLayoutEffect, useState } from "react"; import { useStyles } from "./style.ts"; +const { Title, Text, Link } = Typography; +const { TabPane } = Tabs; const Login = memo(() => { - const { styles } = useStyles(); - const { t } = useTranslation(); - const [values, setValues] = useAtom(loginFormAtom); - const { isPending, mutate } = useAtomValue(loginAtom); - const [form] = Form.useForm(); + const [upform] = Form.useForm(); + const [emailform] = Form.useForm(); + const { mutate: upMutate } = useAtomValue(upLoginAtom); + const { mutate: emailCodeMutate } = useAtomValue(emailCodeAtom); + const { mutate: emailLoginMutate } = useAtomValue(emailLoginAtom); - const handleSubmit = () => { - form.validateFields().then(() => { - mutate(values); - }); + const uphandleSubmit = (values: any) => { + console.log(values); + upMutate(values); }; + const emailhandleSubmit = (values: any) => { + console.log(values); + emailLoginMutate(values); + }; + const handleGetCode = () => { + const email = emailform.getFieldValue("email"); + setCountdown(10); + setIsButtonDisabled(true); + emailCodeMutate({ email }); + }; + + const [countdown, setCountdown] = useState(0); + const [isButtonDisabled, setIsButtonDisabled] = useState(false); + + useEffect(() => { + let timer: number; + if (countdown > 0) { + timer = setTimeout(() => setCountdown(countdown - 1), 1000); + } else { + setIsButtonDisabled(false); + } + return () => clearTimeout(timer); + }, [countdown]); - const [loginMod, setLoginMod] = useState([ + const [loginMod] = useState([ { value: "single", info: "帐号密码登录" }, { value: "multiple-email", info: "邮箱登录" }, { value: "multiple-plane", info: "飞机登录" }, ]); - const [selectedMode, setSelectedMode] = useState(loginMod[0].value); - - const handleModeChange = (e: any) => { - setSelectedMode(e.target.value); - }; - useLayoutEffect(() => { document.body.className = "login"; return () => { @@ -40,58 +59,119 @@ const Login = memo(() => { }, []); return ( -
-
- -
-
-
- - {loginMod.map((mod) => ( - - {mod.info} - - ))} - -
{ - setValues(allValues); - }} - size="large" - > - - - - - - - - - - - {/*验证码*/} - - - - - - -
-
-
-
+ + 注册 + + + + +
+ + } /> + + + } /> + + {/**/} + {/* } />*/} + {/**/} + +
+
+ +
+ + } + addonAfter={ + + } + /> + + + } /> + + +
+
+ {/**/} + {/*
*/} + {/* */} + {/* } />*/} + {/* */} + {/*
*/} + {/*
*/} +
+ + 忘记账号 + 找回密码 + + + + + + ); }); diff --git a/src/request-base-url-interceptors.ts b/src/request-base-url-interceptors.ts index 2fcae44..b506dc2 100644 --- a/src/request-base-url-interceptors.ts +++ b/src/request-base-url-interceptors.ts @@ -1,32 +1,33 @@ -import { AxiosInstance } from 'axios' - +import { AxiosInstance } from "axios"; const baseURLMap = { - package: 'http://154.88.7.8:45321/api/v1', - movie: 'http://47.113.117.106:10000/api/v1', + package: "http://154.88.7.8:45321/api/v1", + movie: "http://47.113.117.106:10000/api/v1", default: 'http://127.0.0.1:8686/api/v1', -} +}; /** * 拦截url,适应不同的baseURL * @param axiosInstance */ export const requestBaseUrlInterceptors = (axiosInstance: AxiosInstance) => { - -//拦截url,适应不同的baseURL - axiosInstance.interceptors.request.use((config) => { - const { url } = config - //取url的第1个/后的字符串 - const key = url?.split('/')[1] - const baseURL = baseURLMap[key!] - if (baseURL) { - config.baseURL = baseURL - } else { - config.baseURL = baseURLMap['default'] - } - return config - }, (error) => { - // console.log('error', error) - return Promise.reject(error) - }) -} \ No newline at end of file + //拦截url,适应不同的baseURL + axiosInstance.interceptors.request.use( + (config) => { + const { url } = config; + //取url的第1个/后的字符串 + const key = url?.split("/")[1]; + const baseURL = baseURLMap[key!]; + if (baseURL) { + config.baseURL = baseURL; + } else { + config.baseURL = baseURLMap["default"]; + } + return config; + }, + (error) => { + // console.log('error', error) + return Promise.reject(error); + }, + ); +}; diff --git a/src/request.ts b/src/request.ts index 805dc39..0f39b4e 100644 --- a/src/request.ts +++ b/src/request.ts @@ -1,178 +1,179 @@ -import { getToken, setToken } from '@/store/system.ts' -import { IApiResult } from '@/global' -import { Record } from '@icon-park/react' -import { message } from 'antd' -import axios, { - AxiosRequestConfig, - AxiosInstance, AxiosResponse, -} from 'axios' - - -export type { AxiosRequestConfig } -type FetchMethod = (url: string, data?: D, config?: AxiosRequestConfig) => Promise> - -interface RequestMethods extends Pick { - download: (url: string, data?: any) => Promise +import { getToken, setToken } from "@/store/system.ts"; +import { IApiResult } from "@/global"; +import { Record } from "@icon-park/react"; +import { message } from "antd"; +import axios, { AxiosRequestConfig, AxiosInstance, AxiosResponse } from "axios"; + +export type { AxiosRequestConfig }; +type FetchMethod = (url: string, data?: D, config?: AxiosRequestConfig) => Promise>; + +interface RequestMethods + extends Pick< + AxiosInstance, + "get" | "post" | "put" | "delete" | "request" | "postForm" | "patch" | "patchForm" | "putForm" | "options" + > { + download: (url: string, data?: any) => Promise; } - const axiosInstance = axios.create({ - baseURL: '/api/v1', + baseURL: "/api/v1", // timeout: 1000, headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, - validateStatus: status => { - return status >= 200 && status < 300 - } -}) - + validateStatus: (status) => { + return status >= 200 && status < 300; + }, +}); //拦截request,添加token -axiosInstance.interceptors.request.use((config) => { - - const token = getToken() - if (token) { - config.headers.Authorization = `Bearer ${token}` - } - - return config -}, (error) => { - console.log('error', error) - return Promise.reject(error) -}) +axiosInstance.interceptors.request.use( + (config) => { + const token = getToken(); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => { + console.log("error", error); + return Promise.reject(error); + }, +); //拦截response,返回data axiosInstance.interceptors.response.use( - (response) => { - // console.log('response', response.data) - - message.destroy() - - const result = response.data as IApiResult - switch (result.code) { - case 0: - case 200: - //login - if (response.config.url?.includes('/sys/login')) { - setToken(result.data.token) - const search = new URLSearchParams(window.location.search) - // eslint-disable-next-line no-case-declarations - const redirect = search.get('redirect') - if (redirect) { - window.location.href = redirect - } - } - return response - case 401: - setToken('') - if (window.location.pathname === '/login') { - return Promise.reject(new Error('to login')) - } - - // 401: 未登录 - message.error('登录失败,跳转重新登录') - // eslint-disable-next-line no-case-declarations - const search = new URLSearchParams(window.location.search) - // eslint-disable-next-line no-case-declarations - let redirect = window.location.pathname - if (search.toString() !== '') { - redirect = window.location.pathname + '?=' + search.toString() - } - window.location.href = `/login?redirect=${encodeURIComponent(redirect)}` - return Promise.reject(new Error('to login')) - default: - message.error(result.message ?? '请求失败') - return Promise.reject(response) + (response) => { + // console.log('response', response.data) + + message.destroy(); + + const result = response.data as IApiResult; + switch (result.code) { + case 0: + case 200: + //login + if (response.config.url?.includes("/sys/login")) { + setToken(result.data.token); + const search = new URLSearchParams(window.location.search); + // eslint-disable-next-line no-case-declarations + const redirect = search.get("redirect"); + if (redirect) { + window.location.href = redirect; + } else { + window.location.href = "/"; + } + } + return response; + case 401: + setToken(""); + if (window.location.pathname === "/login") { + return Promise.reject(new Error("to login")); + } + + // 401: 未登录 + message.error("登录失败,跳转重新登录"); + // eslint-disable-next-line no-case-declarations + const search = new URLSearchParams(window.location.search); + // eslint-disable-next-line no-case-declarations + let redirect = window.location.pathname; + if (search.toString() !== "") { + redirect = window.location.pathname + "?=" + search.toString(); + } + window.location.href = `/login?redirect=${encodeURIComponent(redirect)}`; + return Promise.reject(new Error("to login")); + default: + message.error(result.message ?? "请求失败"); + return Promise.reject(response); + } + }, + (error) => { + // console.log('error', error) + message.destroy(); + const { response } = error; + if (response) { + switch (response.status) { + case 401: + if (window.location.pathname === "/login") { + return; } - }, (error) => { - // console.log('error', error) - message.destroy() - const { response } = error - if (response) { - switch (response.status) { - case 401: - if (window.location.pathname === '/login') { - return - } - - setToken('') - // 401: 未登录 - message.error('登录失败,跳转重新登录') - // eslint-disable-next-line no-case-declarations - const search = new URLSearchParams(window.location.search) - // eslint-disable-next-line no-case-declarations - let redirect = window.location.pathname - if (search.toString() !== '') { - redirect = window.location.pathname + '?=' + search.toString() - } - window.location.href = `/login?redirect=${encodeURIComponent(redirect)}` - return - case 403: - message.error('没有权限') - break - case 404: - message.error('请求的资源不存在') - break - default: - message.error(response.data.message ?? response.data ?? error.message ?? '请求失败') - return Promise.reject(response) - } + setToken(""); + // 401: 未登录 + message.error("登录失败,跳转重新登录"); + // eslint-disable-next-line no-case-declarations + const search = new URLSearchParams(window.location.search); + // eslint-disable-next-line no-case-declarations + let redirect = window.location.pathname; + if (search.toString() !== "") { + redirect = window.location.pathname + "?=" + search.toString(); } + window.location.href = `/login?redirect=${encodeURIComponent(redirect)}`; + return; + case 403: + message.error("没有权限"); + break; + case 404: + message.error("请求的资源不存在"); + break; + default: + message.error(response.data.message ?? response.data ?? error.message ?? "请求失败"); + return Promise.reject(response); + } + } - return Promise.reject(error) - }) + return Promise.reject(error); + }, +); //扩展download方法 // @ts-ignore fix download axiosInstance.download = (url: string, data?: any) => { - const formData = new FormData() + const formData = new FormData(); for (const key in data) { - formData.append(key, data[key]) + formData.append(key, data[key]); } const config = { - method: 'post', + method: "post", url, data: formData, - responseType: 'blob', + responseType: "blob", timeout: 40 * 1000, - } as AxiosRequestConfig - return axiosInstance.request(config) -} - + } as AxiosRequestConfig; + return axiosInstance.request(config); +}; //创建返回IApiResult类型的request export const createFetchMethods = () => { - const methods = {} + const methods = {}; for (const method of Object.keys(axiosInstance)) { methods[method] = async (url: string, data?: D, config?: AxiosRequestConfig) => { - config = config ?? {} - config.url = url - config.method = method - const isGet = method === 'get' + config = config ?? {}; + config.url = url; + config.method = method; + const isGet = method === "get"; if (isGet) { - config.params = data + config.params = data; } else { - config.data = data + config.data = data; } return axiosInstance(config) - .then((response: AxiosResponse>) => { - if (response.data.code !== 200 && response.data.code !== 0) { - throw new Error(response.data.message) - } - return response.data as IApiResult - }) - .catch((err) => { - throw err - }) - } + .then((response: AxiosResponse>) => { + if (response.data.code !== 200 && response.data.code !== 0) { + throw new Error(response.data.message); + } + return response.data as IApiResult; + }) + .catch((err) => { + throw err; + }); + }; } - return methods as Record -} + return methods as Record; +}; -export const request = createFetchMethods() -export default request \ No newline at end of file +export const request = createFetchMethods(); +export default request; diff --git a/src/routes.tsx b/src/routes.tsx index d6aa0a2..ca6d7d2 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -109,6 +109,11 @@ const loginRoute = LoginRouteImport.update({ getParentRoute: () => emptyRoute, } as any) +// const login2Route = LoginRouteImport.update({ +// path: '/login2', +// getParentRoute: () => emptyRoute, +// } as any) + // // const menusRoute = createRoute({ @@ -303,7 +308,7 @@ const routeTree = rootRoute.addChildren( //非Layout loginRoute, emptyRoute, - + //login2Route, // 添加新的自定义路由 createRoute({ getParentRoute: () => rootRoute, diff --git a/src/service/system.ts b/src/service/system.ts index 9b7fc02..7c4948e 100644 --- a/src/service/system.ts +++ b/src/service/system.ts @@ -1,52 +1,55 @@ -import { IPageResult } from '@/global' -import request from '../request.ts' -import { createCURD } from '@/service/base.ts' -import { System } from '@/types' +import { IPageResult } from "@/global"; +import request from "../request.ts"; +import { createCURD } from "@/service/base.ts"; +import { System } from "@/types"; const systemServ = { - dept: { - ...createCURD('/sys/dept'), - tree: () => { - return request.get<{ tree: System.IDepartment }>('/sys/dept/tree') - } + dept: { + ...createCURD("/sys/dept"), + tree: () => { + return request.get<{ tree: System.IDepartment }>("/sys/dept/tree"); }, - menus: { - ...createCURD('/sys/menu') - }, - login: (data: System.LoginRequest) => { - return request.post('/sys/login', data) + }, + menus: { + ...createCURD("/sys/menu"), + }, + uplogin: (data: any) => { + return request.post("/sys/login", data); + }, + emailCode: (data: any) => { + return request.post("/sys/email", data); + }, + + emailLogin: (data: any) => { + return request.post("/sys/login/email", data); + }, + + logout: () => { + // + }, + user: { + ...createCURD("/sys/user"), + current: () => { + return request.get("/sys/user/info"); }, - logout:()=>{ - // + menus: () => { + return request.get>("/sys/user/menus"); }, - user: { - ...createCURD('/sys/user'), - current: () => { - return request.get('/sys/user/info') - }, - menus: () => { - return request.get>('/sys/user/menus') - }, - resetPassword: (id: number) => { - return request.post(`/sys/user/reset/password`, { id }) - } + resetPassword: (id: number) => { + return request.post(`/sys/user/reset/password`, { id }); }, - role: { - ...createCURD('/sys/role') + }, + role: { + ...createCURD("/sys/role"), + }, + logs: { + login: { + ...createCURD("/sys/log/login"), + clear: (params: { start: string; end: string }) => { + return request.post("/sys/log/login/clear", params); + }, }, - logs: { - login: { - ...createCURD('/sys/log/login'), - clear: (params: { - start: string, - end: string - }) => { - return request.post('/sys/log/login/clear', params) - } - } - } - - -} + }, +}; -export default systemServ \ No newline at end of file +export default systemServ; diff --git a/src/store/system/user.ts b/src/store/system/user.ts index 02e4836..578b86e 100644 --- a/src/store/system/user.ts +++ b/src/store/system/user.ts @@ -1,171 +1,203 @@ -import { appAtom, setToken } from '@/store/system.ts' -import { atom } from 'jotai' -import { IApiResult, IAuth, IPage, IPageResult, MenuItem } from '@/global' -import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' -import systemServ from '@/service/system.ts' -import { formatMenuData, isDev } from '@/utils' -import { message } from 'antd' -import { t } from 'i18next' -import { System } from '@/types' -import { atomWithStorage } from 'jotai/utils' -import { IUserInfo } from '@/types/system/user' +import { appAtom, setToken } from "@/store/system.ts"; +import { atom } from "jotai"; +import { IApiResult, IAuth, IPage, IPageResult, MenuItem } from "@/global"; +import { atomWithMutation, atomWithQuery, queryClientAtom } from "jotai-tanstack-query"; +import systemServ from "@/service/system.ts"; +import { formatMenuData, isDev } from "@/utils"; +import { message } from "antd"; +import { t } from "i18next"; +import { System } from "@/types"; +import { atomWithStorage } from "jotai/utils"; +import { IUserInfo } from "@/types/system/user"; + +export interface UPLoginRequest { + mfa_status: boolean; + account: string; + username: string; + password: string; + code: string; +} export const authAtom = atom({ isLogin: false, - authKey: [] -}) + authKey: [], +}); const devLogin = { - username: 'SupperAdmin', - password: 'kk123456', - code: '123456' -} -export const loginFormAtom = atom({ - ...(isDev ? devLogin : {}) -} as System.LoginRequest) + username: "SupperAdmin", + password: "kk123456", + code: "123456", +}; +export const upLoginFormAtom = atom({ + ...(isDev ? devLogin : {}), +} as UPLoginRequest); + +export const upLoginAtom = atomWithMutation((get) => ({ + mutationKey: ["uplogin"], + mutationFn: async (params) => { + return await systemServ.uplogin(params); + }, + onSuccess: (res) => { + message.success(t("login.success")); + // console.log('login success', res) + get(userMenuDataAtom).refetch().then(); + return res.data; + }, + retry: false, +})); -export const loginAtom = atomWithMutation((get) => ({ - mutationKey: [ 'login' ], +export const emailCodeAtom = atomWithMutation((get) => ({ + mutationKey: ["emailCode"], + mutationFn: async (params) => { + return await systemServ.emailCode(params); + }, + onSuccess: (res) => { + message.success(t("login.success")); + // console.log('login success', res) + get(userMenuDataAtom).refetch().then(); + return res.data; + }, + retry: false, +})); +export const emailLoginAtom = atomWithMutation((get) => ({ + mutationKey: ["emailLogin"], mutationFn: async (params) => { - return await systemServ.login(params) + return await systemServ.emailLogin(params); }, onSuccess: (res) => { - message.success(t('login.success')) + message.success(t("login.success")); // console.log('login success', res) - get(userMenuDataAtom).refetch().then() - return res.data + get(userMenuDataAtom).refetch().then(); + return res.data; }, retry: false, -})) +})); export const logoutAtom = atomWithMutation(() => ({ - mutationKey: [ 'logout' ], + mutationKey: ["logout"], mutationFn: async () => { - setToken('') - return true + setToken(""); + return true; }, -})) - -export const currentStaticUserAtom = atomWithStorage('user', null) +})); +export const currentStaticUserAtom = atomWithStorage("user", null); export const currentUserAtom = atomWithQuery, any, System.IUserInfo>((get) => { return { - queryKey: [ 'user_info', get(appAtom).token ], + queryKey: ["user_info", get(appAtom).token], queryFn: async () => { - return await systemServ.user.current() + return await systemServ.user.current(); }, select: (data) => { // store.set(currentStaticUserAtom, data.data) - return data.data + return data.data; }, - } -}) - + }; +}); export const userMenuDataAtom = atomWithQuery>, any, MenuItem[]>((get) => ({ enabled: false, - queryKey: [ 'user_menus', get(appAtom).token ], + queryKey: ["user_menus", get(appAtom).token], queryFn: async () => { - return await systemServ.user.menus() + return await systemServ.user.menus(); }, select: (data) => { - return formatMenuData(data.data.rows as any ?? [], []) + return formatMenuData((data.data.rows as any) ?? [], []); }, retry: false, -})) +})); export type UserSearch = { - dept_id?: any, - key?: string -} + dept_id?: any; + key?: string; +}; -export const userSearchAtom = atom({} as UserSearch) +export const userSearchAtom = atom({} as UserSearch); //=======user page store====== export const userPageAtom = atom({ pageSize: 10, - page: 1 -}) + page: 1, +}); // user list export const userListAtom = atomWithQuery((get) => { return { - queryKey: [ 'user_list', get(userSearchAtom), get(userPageAtom) ], - queryFn: async ({ queryKey: [ , params, page ] }) => { + queryKey: ["user_list", get(userSearchAtom), get(userPageAtom)], + queryFn: async ({ queryKey: [, params, page] }) => { return await systemServ.user.list({ - ...params as any, - ...page as any, - }) + ...(params as any), + ...(page as any), + }); }, select: (data) => { - return data.data + return data.data; }, - - } -}) + }; +}); // user selected -export const userSelectedAtom = atom({} as System.IUser) +export const userSelectedAtom = atom({} as System.IUser); export const defaultUserData = { id: 0, dept_id: 0, role_id: 0, -} as System.IUser +} as System.IUser; //save or update user export const saveOrUpdateUserAtom = atomWithMutation((get) => ({ - mutationKey: [ 'save_user' ], + mutationKey: ["save_user"], mutationFn: async (params) => { - params.status = params.status ? '1' : '0' - const isAdd = 0 === params.id + params.status = params.status ? "1" : "0"; + const isAdd = 0 === params.id; if (isAdd) { - return await systemServ.user.add(params) + return await systemServ.user.add(params); } - return await systemServ.user.update(params) + return await systemServ.user.update(params); }, onSuccess: (res) => { - const isAdd = !!res.data?.id - message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功')) + const isAdd = !!res.data?.id; + message.success(t(isAdd ? "message.saveSuccess" : "message.editSuccess", "保存成功")); //刷新userList - get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] }) + get(queryClientAtom).invalidateQueries({ queryKey: ["user_list"] }); - return res + return res; }, -})) +})); //delete user -export const batchUserIdsAtom = atom([]) +export const batchUserIdsAtom = atom([]); export const deleteUserAtom = atomWithMutation((get) => ({ - mutationKey: [ 'delete_user' ], + mutationKey: ["delete_user"], mutationFn: async (params) => { - return await systemServ.user.batchDelete(params) + return await systemServ.user.batchDelete(params); }, onSuccess: () => { - message.success(t('message.deleteSuccess', '删除成功')) + message.success(t("message.deleteSuccess", "删除成功")); //刷新userList - get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] }) - return true + get(queryClientAtom).invalidateQueries({ queryKey: ["user_list"] }); + return true; }, -})) +})); //reset password export const resetPasswordAtom = atomWithMutation(() => ({ - mutationKey: [ 'reset_password' ], + mutationKey: ["reset_password"], mutationFn: async (id) => { - return await systemServ.user.resetPassword(id) + return await systemServ.user.resetPassword(id); }, onSuccess: () => { - message.success(t('message.resetSuccess', '重置成功')) + message.success(t("message.resetSuccess", "重置成功")); //刷新userList // get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] }) - return true + return true; }, onError: () => { - message.error(t('message.resetError', '重置失败')) + message.error(t("message.resetError", "重置失败")); }, -})) +})); diff --git a/src/types/system/login.d.ts b/src/types/system/login.d.ts index d874235..199c7d9 100644 --- a/src/types/system/login.d.ts +++ b/src/types/system/login.d.ts @@ -1,14 +1,12 @@ - - -export interface LoginRequest { - 'mfa_status': boolean; - 'username': string; - 'password': string; - 'code': string; +export interface UPLoginRequest { + mfa_status: boolean; + account: string; + username: string; + password: string; + code: string; } export interface LoginResponse { - 'token': string; - 'mfa_status': boolean; + token: string; + mfa_status: boolean; } - diff --git a/vite.config.ts b/vite.config.ts index 017c4d6..556523a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,6 +13,7 @@ const proxyMap = { //'/api/v1/certold': 'http://192.168.31.41:8000', "/api/v1/cert": "http://127.0.0.1:8000", //'/api/v1/cert': 'http://192.168.31.41:8000', + "/api/v1/sys": "http://192.168.31.41:8686/", } as Record; const proxyConfig = Object.keys(proxyMap).reduce(