八、Vue3+Ts+Vite+AntdUI构建后台基础模板——退出登录功能

371 阅读1分钟

一、新建layout模块全局状态

  1. @/store/modules目录下新建layout.ts
import { defineStore } from 'pinia';

const useLayoutStore = defineStore('layout', {
    state: () => {
        return {
            menuCollapsed: false,
        };
    },
    actions: {
        menuTrigger() {
            this.menuCollapsed = !this.menuCollapsed;
        },
    },
});

export default useLayoutStore;
  1. 修改@/store/index.ts
import useUserStore from './modules/user';
import useLoadingStore from './modules/loading';
import useLayoutStore from './modules/layout';

export { useUserStore, useLoadingStore, useLayoutStore };

二、解决layout图标按需加载的错误提示

  1. 执行命令
npm i -S @ant-design/icons-vue

三、将layout中页面头部模块化

  1. 在@/layout目录下新建modules目录
  2. 在@/layout/modules目录下新建Header.vue文件
<template>
    <a-layout-header style="background: #fff; padding: 0 20px">
        <menu-unfold-outlined v-if="layoutStore.menuCollapsed" class="trigger"  @click="menuTrigger" />
        <menu-fold-outlined v-else class="trigger" @click="menuTrigger" />
    </a-layout-header>
</template>

<script lang="ts" setup>
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue';
import { useLayoutStore } from '@/store';

const layoutStore = useLayoutStore();
const menuTrigger = () => {
    layoutStore.menuTrigger();
};
</script>

<style lang="less" scoped>
.trigger {
    font-size: 18px;
    line-height: 64px;
    padding: 0 24px;
    cursor: pointer;
    transition: color 0.3s;
}

.trigger:hover {
    color: #1890ff;
}
</style>
  1. 引入头部组件Header到layout中
  2. 修改@/layout/index.vue
<template>
    <a-layout class="layout">
        <a-layout-sider v-model:collapsed="layoutStore.menuCollapsed" :trigger="null" collapsible>
            <div class="logo" />
            <a-menu theme="dark" mode="inline" v-model:selectedKeys="selectedKeys">
                <a-menu-item key="/">
                    <dashboard-outlined />
                    <span>首页</span>
                </a-menu-item>
            </a-menu>
        </a-layout-sider>
        <a-layout>
            <Header />
            <a-layout-content style="margin: 24px 16px; padding: 24px; background: #fff; min-height: 280px">
                <a-spin :spinning="loadingStore.loadingState" :delay="300" size="large">
                    <router-view />
                </a-spin>
            </a-layout-content>
        </a-layout>
    </a-layout>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import { DashboardOutlined } from '@ant-design/icons-vue';
import { useLoadingStore, useLayoutStore } from '@/store';
import Header from './modules/Header.vue';

const loadingStore = useLoadingStore();
const layoutStore = useLayoutStore();
const selectedKeys = ref<string[]>(['/']);
</script>

<style lang="less" scoped>
.layout {
    box-sizing: border-box;
    width: 100%;
    height: 100%;
}
.logo {
    margin: 20px auto;
    width: 80%;
    height: 40px;
    background: #334454;
}

#components-layout-demo-custom-trigger .logo {
    height: 32px;
    background: rgba(255, 255, 255, 0.3);
    margin: 16px;
}

.site-layout .site-layout-background {
    background: #fff;
}
</style>

四、layout中页面头部加入用户显示

  1. 修改@/layout/modules/Header.vue
<template>
    <a-layout-header class="header">
        <div class="left">
            <div class="item" @click="menuTrigger">
                <menu-unfold-outlined v-if="layoutStore.menuCollapsed" class="trigger" />
                <menu-fold-outlined v-else class="trigger" />
            </div>
        </div>
        <div class="right">
            <div class="item">
                <a-dropdown placement="bottom">
                    <div class="user">
                        <a-avatar class="avatar" :src="userStore.userInfo.avatar">登录</a-avatar>
                        <span class="name">{{ userStore.userInfo.name }}</span>
                    </div>
                    <template #overlay>
                        <a-menu>
                            <a-menu-item>
                                <poweroff-outlined style="margin-right: 5px; color: #f5222d" />
                                <span>退出系统</span>
                            </a-menu-item>
                        </a-menu>
                    </template>
                </a-dropdown>
            </div>
        </div>
    </a-layout-header>
</template>

<script lang="ts" setup>
import { MenuUnfoldOutlined, MenuFoldOutlined, PoweroffOutlined } from '@ant-design/icons-vue';
import { useLayoutStore, useUserStore } from '@/store';

const layoutStore = useLayoutStore();
const userStore = useUserStore();
const menuTrigger = () => {
    layoutStore.menuTrigger();
};
</script>

<style lang="less" scoped>
.header {
    height: 48px;
    padding: 0 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: #fff;
    color: #666;
    font-size: 18px;
    .item {
        cursor: pointer;
        height: 48px;
        display: flex;
        align-items: center;
        margin: 0 10px;
        padding: 0 10px;
        transition: color 0.3s;
        &:hover {
            background: rgba(0, 0, 0, 0.05);
            color: #1890ff;
        }
    }
    .user {
        display: flex;
        align-items: center;
        .avatar {
            margin-right: 10px;
        }
        .name {
            font-size: 14px;
        }
    }
}
</style>

五、layout中页面头部加入退出功能

  1. Mock login的userInfo加入用户头像
  2. 修改Mock/login.ts
import Mock from 'mockjs';

export default [
    {
        // http://mockjs.com/examples.html
        url: '/mock/api/login',
        method: 'post',
        timeout: 500,
        // statusCode: 500,
        response: ({ body }) => {
            return {
                code: 200,
                success: true,
                message: 'ok',
                data: {
                    // query: body,
                    token: Mock.Random.string('lower', 200),
                    userInfo: {
                        id: Mock.Random.id(),
                        name: Mock.Random.cname(),
                        email: Mock.Random.email(),
                        gender: Mock.Random.natural(1, 2),
                        age: Mock.Random.natural(18, 30),
                        avatar: Mock.Random.image('800x800'),
                    },
                },
            };
        },
    },
];

  1. 修改@/store/modules/user.ts
import { defineStore } from 'pinia';

const useUserStore = defineStore('user', {
    state: () => {
        return {
            token: '',
            userInfo: {},
        };
    },
    actions: {
        setToken(token: string) {
            this.token = token;
        },
        setUserInfo(userInfo) {
            this.userInfo = userInfo;
        },
        clearToken() {
            this.token = '';
        },
        clearUser() {
            this.userInfo = {};
        },
    },
    persist: {
        enabled: true,
        strategies: [
            { key: 'token', storage: localStorage, paths: ['token'] },
            { key: 'userInfo', storage: localStorage, paths: ['userInfo'] },
        ],
    },
});

export default useUserStore;
  1. 修改@/router/index.ts
// @/router/index.ts

//......

router.beforeEach((to, from, next) => {
    const userStore = useUserStore();
    if (to.name !== 'Login' && !userStore.token) {
        next({ name: 'Login' });
    } else {
        if (to.name === 'Login') {
            userStore.clearToken();
            userStore.clearUser();
        }
        next();
    }
});

export default router;
  1. 增加退出登录的逻辑
  2. 修改@/layout/modules/Header.vue
<template>
    <a-layout-header class="header">
        <div class="left">
            <div class="item" @click="menuTrigger">
                <menu-unfold-outlined v-if="layoutStore.menuCollapsed" class="trigger" />
                <menu-fold-outlined v-else class="trigger" />
            </div>
        </div>
        <div class="right">
            <div class="item">
                <a-dropdown placement="bottom">
                    <div class="user">
                        <a-avatar class="avatar" :src="userStore.userInfo.avatar">登录</a-avatar>
                        <span class="name">{{ userStore.userInfo.name }}</span>
                    </div>
                    <template #overlay>
                        <a-menu>
                            <a-menu-item @click="outLogin">
                                <poweroff-outlined style="margin-right: 5px; color: #f5222d" />
                                <span>退出系统</span>
                            </a-menu-item>
                        </a-menu>
                    </template>
                </a-dropdown>
            </div>
        </div>
    </a-layout-header>
</template>

<script lang="ts" setup>
//......
const outLogin = () => {
    router.push('/login');
};
</script>

<style lang="less" scoped>
......
</style>

六、源代码地址

https://github.com/jiangzetian/vue3-admin-template

七、视频演示及源码

本文演示视频:点击浏览

更多前端内容欢迎关注公众号:天小天个人网