提交 5465f058 编写于 作者: V vben

feat(user): add user login expiration example

上级 d5b76892
......@@ -7,6 +7,7 @@
- 新增 `JsonPreview`Json 数据查看组件
- 表格的数据列(column)和操作列(actionColumn)的字段可以根据权限和业务来控制是否显示
- 新增权限控制表格示例(AuthColumn.vue)
- 新增用户登录过期示例
### ⚡ Performance Improvements
......
import { MockMethod } from 'vite-plugin-mock';
import { resultSuccess } from '../_util';
import { resultSuccess, resultError } from '../_util';
const userInfo = {
name: 'Vben',
......@@ -51,4 +51,12 @@ export default [
return resultSuccess(userInfo);
},
},
{
url: '/basic-api/user/sessionTimeout',
method: 'post',
statusCode: 401,
response: () => {
return resultError();
},
},
] as MockMethod[];
......@@ -3,8 +3,11 @@ import { GetAccountInfoModel } from './model/accountModel';
enum Api {
ACCOUNT_INFO = '/account/getAccountInfo',
SESSION_TIMEOUT = '/user/sessionTimeout',
}
// Get personal center-basic settings
export const accountInfoApi = () => defHttp.get<GetAccountInfoModel>({ url: Api.ACCOUNT_INFO });
export const sessionTimeoutApi = () => defHttp.post<void>({ url: Api.SESSION_TIMEOUT });
......@@ -5,28 +5,29 @@
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useDesign } from '/@/hooks/web/useDesign';
import { useUserStoreWidthOut } from '/@/store/modules/user';
import { SettingButtonPositionEnum } from '/@/enums/appEnum';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
import SessionTimeoutLogin from '/@/views/sys/login/SessionTimeoutLogin.vue';
export default defineComponent({
name: 'LayoutFeatures',
components: {
BackTop,
LayoutLockPage: createAsyncComponent(() => import('/@/views/sys/lock/index.vue')),
SettingDrawer: createAsyncComponent(() => import('/@/layouts/default/setting/index.vue')),
SessionTimeoutLogin,
},
setup() {
const {
getUseOpenBackTop,
getShowSettingButton,
getSettingButtonPosition,
getFullContent,
} = useRootSetting();
const { getUseOpenBackTop, getShowSettingButton, getSettingButtonPosition, getFullContent } =
useRootSetting();
const userStore = useUserStoreWidthOut();
const { prefixCls } = useDesign('setting-drawer-fearure');
const { getShowHeader } = useHeaderSetting();
const getIsSessionTimeout = computed(() => userStore.getSessionTimeout);
const getIsFixedSettingDrawer = computed(() => {
if (!unref(getShowSettingButton)) {
return false;
......@@ -44,6 +45,7 @@
getUseOpenBackTop,
getIsFixedSettingDrawer,
prefixCls,
getIsSessionTimeout,
};
},
});
......@@ -53,6 +55,7 @@
<LayoutLockPage />
<BackTop v-if="getUseOpenBackTop" :target="getTarget" />
<SettingDrawer v-if="getIsFixedSettingDrawer" :class="prefixCls" />
<SessionTimeoutLogin v-if="getIsSessionTimeout" />
</template>
<style lang="less">
......
......@@ -2,6 +2,7 @@ export default {
feat: 'Page Function',
icon: 'Icon',
tabs: 'Tabs',
sessionTimeout: 'Session Timeout',
print: 'Print',
contextMenu: 'Context Menu',
download: 'Download',
......
export default {
feat: '功能',
icon: '图标',
sessionTimeout: '登录过期',
tabs: '标签页操作',
print: '打印',
contextMenu: '右键菜单',
......
......@@ -6,9 +6,6 @@ const menu: MenuModule = {
menu: {
name: t('routes.demo.comp.comp'),
path: '/comp',
tag: {
dot: true,
},
children: [
{
path: 'basic',
......@@ -191,9 +188,6 @@ const menu: MenuModule = {
{
name: t('routes.demo.editor.editor'),
path: 'editor',
tag: {
dot: true,
},
children: [
{
path: 'json',
......
......@@ -6,7 +6,9 @@ const menu: MenuModule = {
menu: {
name: t('routes.demo.feat.feat'),
path: '/feat',
tag: {
dot: true,
},
children: [
{
path: 'icon',
......@@ -16,6 +18,13 @@ const menu: MenuModule = {
path: 'ws',
name: t('routes.demo.feat.ws'),
},
{
name: t('routes.demo.feat.sessionTimeout'),
path: 'session-timeout',
tag: {
content: 'new',
},
},
{
path: 'tabs',
name: t('routes.demo.feat.tabs'),
......
......@@ -6,17 +6,10 @@ const menu: MenuModule = {
menu: {
name: t('routes.demo.flow.name'),
path: '/flow',
tag: {
dot: true,
},
children: [
{
path: 'flowChart',
name: t('routes.demo.flow.flowChart'),
tag: {
content: 'new',
},
},
],
},
......
......@@ -12,6 +12,7 @@ const feat: AppRouteModule = {
icon: 'ion:git-compare-outline',
title: t('routes.demo.feat.feat'),
},
children: [
{
path: 'icon',
......@@ -29,6 +30,14 @@ const feat: AppRouteModule = {
title: t('routes.demo.feat.ws'),
},
},
{
path: 'session-timeout',
name: 'SessionTimeout',
component: () => import('/@/views/demo/feat/session-timeout/index.vue'),
meta: {
title: t('routes.demo.feat.sessionTimeout'),
},
},
{
path: 'print',
name: 'Print',
......
......@@ -25,6 +25,7 @@ interface UserState {
userInfo: Nullable<UserInfo>;
token?: string;
roleList: RoleEnum[];
sessionTimeout?: boolean;
}
export const useUserStore = defineStore({
......@@ -36,6 +37,8 @@ export const useUserStore = defineStore({
token: undefined,
// roleList
roleList: [],
// Whether the login expired
sessionTimeout: false,
}),
getters: {
getUserInfo(): UserInfo {
......@@ -47,9 +50,12 @@ export const useUserStore = defineStore({
getRoleList(): RoleEnum[] {
return this.roleList.length > 0 ? this.roleList : getAuthCache<RoleEnum[]>(ROLES_KEY);
},
getSessionTimeout(): boolean {
return !!this.sessionTimeout;
},
},
actions: {
setToken(info: string) {
setToken(info: string | undefined) {
this.token = info;
setAuthCache(TOKEN_KEY, info);
},
......@@ -61,10 +67,14 @@ export const useUserStore = defineStore({
this.userInfo = info;
setAuthCache(USER_INFO_KEY, info);
},
setSessionTimeout(flag: boolean) {
this.sessionTimeout = flag;
},
resetState() {
this.userInfo = null;
this.token = '';
this.roleList = [];
this.sessionTimeout = false;
},
/**
* @description: login
......@@ -85,7 +95,9 @@ export const useUserStore = defineStore({
// get user info
const userInfo = await this.getUserInfoAction({ userId });
goHome && (await router.replace(PageEnum.BASE_HOME));
const sessionTimeout = this.sessionTimeout;
sessionTimeout && this.setSessionTimeout(false);
!sessionTimeout && goHome && (await router.replace(PageEnum.BASE_HOME));
return userInfo;
} catch (error) {
return null;
......
import { useMessage } from '/@/hooks/web/useMessage';
import { useI18n } from '/@/hooks/web/useI18n';
import router from '/@/router';
import { PageEnum } from '/@/enums/pageEnum';
// import router from '/@/router';
// import { PageEnum } from '/@/enums/pageEnum';
import { useUserStoreWidthOut } from '/@/store/modules/user';
const { createMessage } = useMessage();
const error = createMessage.error!;
export function checkStatus(status: number, msg: string): void {
const { t } = useI18n();
const userStore = useUserStoreWidthOut();
switch (status) {
case 400:
error(`${msg}`);
......@@ -17,7 +19,8 @@ export function checkStatus(status: number, msg: string): void {
// Return to the current page after successful login. This step needs to be operated on the login page.
case 401:
error(t('sys.api.errMsg401'));
router.push(PageEnum.BASE_LOGIN);
userStore.setToken(undefined);
userStore.setSessionTimeout(true);
break;
case 403:
error(t('sys.api.errMsg403'));
......
import { isObject, isString } from '/@/utils/is';
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm';
export function createNow<T extends boolean>(
join: boolean,
restful: T
......@@ -16,7 +18,6 @@ export function createNow(join: boolean, restful = false): string | object {
return { _t: now };
}
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm';
/**
* @description: Format request parameter time
*/
......
<template>
<PageWrapper
title="登录过期示例"
content="用户登录过期示例,不再跳转登录页,直接生成页面覆盖当前页面,方便保持过期前的用户状态!"
>
<a-button type="primary" @click="test">点击触发用户登录过期</a-button>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { sessionTimeoutApi } from '/@/api/demo/account';
export default defineComponent({
name: 'TestSessionTimeout',
components: { PageWrapper },
setup() {
async function test() {
await sessionTimeoutApi();
}
return { test };
},
});
</script>
......@@ -3,8 +3,9 @@
<AppLocalePicker
class="absolute top-4 right-4 enter-x text-white xl:text-gray-600"
:showText="false"
v-if="!sessionTimeout"
/>
<AppDarkModeToggle class="absolute top-3 right-7 enter-x" />
<AppDarkModeToggle class="absolute top-3 right-7 enter-x" v-if="!sessionTimeout" />
<span class="-enter-x xl:hidden">
<AppLogo :alwaysShowTitle="true" />
......@@ -31,7 +32,25 @@
<div class="h-full xl:h-auto flex py-5 xl:py-0 xl:my-0 w-full xl:w-6/12">
<div
:class="`${prefixCls}-form`"
class="my-auto mx-auto xl:ml-20 xl:bg-transparent px-5 py-8 sm:px-8 xl:p-4 rounded-md shadow-md xl:shadow-none w-full sm:w-3/4 lg:w-2/4 xl:w-auto enter-x relative"
class="
my-auto
mx-auto
xl:ml-20
xl:bg-transparent
px-5
py-8
sm:px-8
xl:p-4
rounded-md
shadow-md
xl:shadow-none
w-full
sm:w-3/4
lg:w-2/4
xl:w-auto
enter-x
relative
"
>
<LoginForm />
<ForgetPasswordForm />
......@@ -72,6 +91,11 @@
AppLocalePicker,
AppDarkModeToggle,
},
props: {
sessionTimeout: {
type: Boolean,
},
},
setup() {
const globSetting = useGlobSetting();
const { prefixCls } = useDesign('login');
......
<template>
<transition>
<div :class="prefixCls">
<Login sessionTimeout />
</div>
</transition>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import Login from './Login.vue';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({
name: 'SessionTimeoutLogin',
components: { Login },
setup() {
const { prefixCls } = useDesign('st-login');
return { prefixCls };
},
});
</script>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-st-login';
.@{prefix-cls} {
position: fixed;
z-index: 9999999;
width: 100%;
height: 100%;
background: @component-background;
}
</style>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册