From 5d5e48e9df2573cb0735513f53ca3b6bba85800f Mon Sep 17 00:00:00 2001 From: "X.Mo" Date: Thu, 24 Oct 2024 16:56:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=99=BB=E5=BD=95=E4=B8=8E=E6=AC=A2?= =?UTF-8?q?=E8=BF=8E=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vitepress/config.mts | 4 +- docs/public/{images => }/logo.svg | 0 docs/zh/front/advanced/login-welcome.md | 79 +++++++++++++- docs/zh/front/advanced/request.md | 130 +++++++++++++++++++++++- docs/zh/front/component/ma-form.md | 2 +- 5 files changed, 206 insertions(+), 9 deletions(-) rename docs/public/{images => }/logo.svg (100%) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index feea99f..435ce0e 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -35,7 +35,7 @@ export default defineConfigWithTheme ({ link:"/en/index", ...enGetConfig, themeConfig:{ - logo: '/images/logo.svg', + logo: '/logo.svg', nav: enGetNavs, sidebar:enGetSidebar, outline:{ @@ -45,7 +45,7 @@ export default defineConfigWithTheme ({ } }, themeConfig: { - logo: '/images/logo.svg', + logo: '/logo.svg', outline:{ label: '页面导航', level: [2, 4], diff --git a/docs/public/images/logo.svg b/docs/public/logo.svg similarity index 100% rename from docs/public/images/logo.svg rename to docs/public/logo.svg diff --git a/docs/zh/front/advanced/login-welcome.md b/docs/zh/front/advanced/login-welcome.md index e125ee4..3983f4e 100644 --- a/docs/zh/front/advanced/login-welcome.md +++ b/docs/zh/front/advanced/login-welcome.md @@ -1 +1,78 @@ -# 登录与欢迎页 \ No newline at end of file +# 登录与欢迎页 + +:::tip +本章节讲解登录页面、登录代码调用、以及登录后默认跳转的页面配置 +::: + +## 登录页面 + +页面在 `src/modules/base/views/login/index.vue`,这个页面实际是个整合页面,登录分为很多小组件,由 `index.vue` 引入整合在一起。 +其中,`` 为表单登录功能,在 `IDE` 内 `ctrl(苹果:COMMAND) + 单击` 即可追踪进去查看代码。 + +::: info 修改建议: +首先说明一点,登录页的表单并非 `Element plus` 组件库,而是使用了 `MineAdmin` 自身的 `基础组件库`,这些组件非常简陋、仅仅支持最普通的功能。 +如果需要修改此页面,不建议直接修改源码,以免影响后续 `升级` 的问题。 + +`3.0`的前端支持了[插件](/zh/front/high/plugin.md)功能,建议通过插件的形式,来改变 `login` 路由的 `component` 属性,达到`替换登录页面`的效果 + +::: + +```vue + +``` + +## 登录代码调用 + +::: info +如果实际开发中只是修改`UI`,不需要修改 `登录流程` 可跳过此段落。 +::: + +登录调用过程: + +- `src/store/modules/useUserStore.ts` 下的 `login()` 方法,内部执行了保存 `token`,`refresh_token`,`expire_at` 等认证数据。 +- 页面执行跳转欢迎页后,被 `路由守卫` 拦截,去请求用户基础数据。 +- `src/store/modules/useUserStore.ts` 下的 `requestUserInfo()` 方法,请求了 `用户数据、菜单权限、角色` 等基础数据。 +- 在 `requestUserInfo()` 还执行了重要的一步:初始化 `路由数据`,调用代码:`routeStore.initRoutes()`。 + +以上就是整个登录调用流程,如果还不懂,可以到交流群里咨询或者联系我们。 + +## 默认欢迎页配置 + +登录后,如果有指定跳转的路由:`/#/login?redirect=[登录成功跳转的路由地址]`,则会跳转到参数所携带的页面,但这个一般在身份认证过期后 +跳转登录页才会携带之前页面地址,默认情况下,我们登录成功后都是跳转到系统内置的默认页面,比如:`index`, `dashboard` 等等。 + +```ts +welcomePage: { + name: 'welcome', + path: '/welcome', + title: '欢迎页', + icon: 'icon-park-outline:jewelry', +} +``` + +::: info 配置建议 +`3.0` 可配置默认欢迎页面以及名称、图标、描述等,打开 `src/provider/settings/index.ts` 文件,找到 `welcomePage` 配置项,可配置此项。 + +但不建议直接修改这个文件,可将部分参数 `copy` 到同级目录的 `settings.config.ts` 文件中,系统会自动合并配置参数,而影响默认配置。 +::: + diff --git a/docs/zh/front/advanced/request.md b/docs/zh/front/advanced/request.md index 920d68e..67ec7b4 100644 --- a/docs/zh/front/advanced/request.md +++ b/docs/zh/front/advanced/request.md @@ -1,13 +1,15 @@ # 请求与拦截器 MineAdmin基于 [axios](https://github.com/axios/axios) 作为请求库,同时提供了请求拦截器,方便开发者对请求进行统一处理。 -## 说明 +## 请求 + +### 说明 系统内封装了两种请求处理: - 一种仅对 `MineAdmin` 后端的请求封装,只适合 `MineAdmin` 的**后端**。 - 一种是可请求外部网络或者其他后端接口的封装。 -## 使用 +### 使用 在前端全局任何位置,且无需显性加载,即可使用 `useHttp()`,注意,这个方式仅对 `MineAdmin` 后端有效 ```ts @@ -29,10 +31,10 @@ http.delete('/xxxx/2') 以上请求,系统会自动处理 `token`、`error` 等,开发者无需关心, 如果需要传入其他配置,例如: `header`,`timeout` 等参数,只需要在后面加参数传入即可。 -## token刷新 +### token刷新 `useHttp()` 内部已经封装了自动续期 `token` 功能,具体原理可查看 [后端文档](/zh/backend/security/passport.md) 这里不再赘述。 -## 请求外部网络 +### 请求外部网络 以上都是针对 `MineAdmin` 后端请求的说明,外部网络请求方式如下: ```ts import request from '@/utils/http' @@ -51,4 +53,122 @@ const requestHome = async () => { } console.log(requestHome()) -``` \ No newline at end of file +``` + +## 拦截器 + +`MineAdmin` 已经封装了拦截器,但需要说明只适配 `MineAdmin` 的后端接口,如果需要调整、修改,打开 `src/utils/http.ts` 文件 +找到下面这段代码: +```ts:line-numbers +http.interceptors.response.use( + async (response: AxiosResponse): Promise => { + isLoading.value = false + const userStore = useUserStore() + await usePluginStore().callHooks('networkResponse', response) + const config = response.config + if ((response.request.responseType === 'blob' + || response.request.responseType === 'arraybuffer') + && !/^application\/json/.test(response.headers['content-type']) + && response.status === ResultCode.SUCCESS + ) { + return Promise.resolve(response.data) + } + + if (response?.data?.code === ResultCode.SUCCESS) { + return Promise.resolve(response.data) + } + else { + switch (response?.data?.code) { + case ResultCode.UNAUTHORIZED: { + const logout = useDebounceFn( + async () => { + Message.error('登录状态已过期,需要重新登录', { zIndex: 9999 }) + await useUserStore().logout() + }, + 3000, + { maxWait: 5000 }, + ) + // 检查token是否需要刷新 + if (userStore.isLogin && !isRefreshToken.value) { + isRefreshToken.value = true + if (!cache.get('refresh_token')) { + await logout() + break + } + + try { + const refreshTokenResponse = await createHttp(null, { + headers: { + Authorization: `Bearer ${cache.get('refresh_token')}`, + }, + }).post('/admin/passport/refresh') + + if (refreshTokenResponse.data.code !== 200) { + await logout() + break + } + else { + const { data } = refreshTokenResponse.data + userStore.token = data.access_token + cache.set('token', data.access_token) + cache.set('expire', useDayjs().unix() + data.expire_at, { exp: data.expire_at }) + cache.set('refresh_token', data.refresh_token) + + config.headers!.Authorization = `Bearer ${userStore.token}` + requestList.value.map((cb: any) => cb()) + requestList.value = [] + return http(config) + } + } + // eslint-disable-next-line unused-imports/no-unused-vars + catch (e: any) { + requestList.value.map((cb: any) => cb()) + await logout() + break + } + finally { + requestList.value = [] + isRefreshToken.value = false + } + } + else { + return new Promise((resolve) => { + requestList.value.push(() => { + config.headers!.Authorization = `Bearer ${cache.get('token')}` + resolve(http(config)) + }) + }) + } + } + case ResultCode.NOT_FOUND: + Message.error('服务器资源不存在', { zIndex: 9999 }) + break + case ResultCode.FORBIDDEN: + Message.error('没有权限访问此接口', { zIndex: 9999 }) + break + case ResultCode.METHOD_NOT_ALLOWED: + Message.error('请求方法不被允许', { zIndex: 9999 }) + break + case ResultCode.FAIL: + Message.error('服务器内部错误', { zIndex: 9999 }) + break + default: + Message.error(response?.data?.message ?? '未知错误', { zIndex: 9999 }) + break + } + + return Promise.reject(response.data ? response.data : null) + } + }, + async (error: any) => { + isLoading.value = false + const serverError = useDebounceFn(async () => { + if (error && error.response && error.response.status === 500) { + Message.error(error.message ?? '服务器内部错误', { zIndex: 9999 }) + } + }, 3000, { maxWait: 5000 }) + await serverError() + return Promise.reject(error) + }, +) +``` diff --git a/docs/zh/front/component/ma-form.md b/docs/zh/front/component/ma-form.md index 2033976..ad5281a 100644 --- a/docs/zh/front/component/ma-form.md +++ b/docs/zh/front/component/ma-form.md @@ -6,7 +6,7 @@ ### useForm 方式 -
+