用户登录配置

该博客介绍了在一个Vue.js项目中,如何设置用户登录、退出登录的API接口,以及在路由层面实现权限控制。包括登录页的实现,路由配置,用户状态管理,以及在不同页面间进行权限判断的beforeEach钩子函数。此外,还展示了登录页的Vue组件代码,实现了登录验证和登录状态的持久化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设置路由

E:\dongli\power-supervision-system\src\api\moudules\login.js

import request from '@/utils/request'

/**
 * 登录模块
 */

/**
 * 登录
 * @param {*} params 
 */
export const login = (params) => {
    return request({
        url: '/user/login',
        method: 'get',
        params
    })
}

/**
 * 退出登录
 * @param {*} params 
 */
export const logout = (params) => {
    return request({
        url: '/user/logout',
        method: 'get',
        params
    })
}

用户登录 

E:\dongli\power-supervision-system\src\api\moudules\user.js

import request from '@/utils/request'

/**
 * 用户列表
 * @param {*} params 
 */
export const getUserList = params => {
    return request({
        url: '/user/getUserList',
        method: 'get',
        params
    })
}
/**
 * 用户登录
 * @param {userName:'',password:''} params 
 */
export const login = params=>{
    return request({
        url: '/login/checkLogin',
        method: 'post',
        params
    })
}
/**
 * 用户注册步骤1校验企业名称
 * @param {companyName:''} params 
 */
export const isRegister = params=>{
    return request({
        url: '/login/isRegister',
        method: 'post',
        params
    })
}
/**
 * 用户注册
 * @param {companyId:'',……} params 
 */
export const register = params=>{
    return request({
        url: '/login/register',
        method: 'post',
        params
    })
}
/**
 * 用户退出
 * @param {userId:'',……} params 
 */
export const logout = params=>{
    return request({
        url: '/login/logout',
        method: 'post',
        params
    })
}

 路由页

E:\dongli\power-supervision-system\src\router\index.js

import {createRouter, createWebHashHistory} from 'vue-router'
import Auth from '@/utils/auth'
import Layout from '../views/layout'

const whiteList = ['/login','/dashboard']

const routes = [
    {
        path: '/',
        redirect: '/dashboard'
    }, {
        path: "/dashboard",
        name: "dashboard",
        meta: {
            title: '系统首页'
        },
        component: () => import (
        /* webpackChunkName: "dashboard" */
        "../views/Dashboard.vue")
    }, 
    {
        path: "/",
        name: "Layout",
        component: Layout,
        children: [
     
        ]
    }, {
        path: "/login",
        name: "Login",
        meta: {
            title: '登录'
        },
        component: () => import (
        /* webpackChunkName: "login" */
        "../views/login.vue")
    }
];

const router = createRouter({
    history: createWebHashHistory(),
    routes
});

router.beforeEach((to, from, next) => {
    document.title = `${to.meta.title} | ${window.g.title}`;
    if(Auth.getToken()) {
        if (to.path === '/login') {
            next({ path: '/' })
        } else if(to.path.indexOf("/error") >= 0){
            // 防止因重定向到error页面造成beforeEach死循环
            next()
        } else {
            //store.dispatch('GenerateRoutes').then((res) => {
                next()
            //})
        }
    } else {
        // 没有token
        if (whiteList.indexOf(to.path) !== -1) {
            // 在免登录白名单,直接进入
            next()
        } else {
            next(`/login`) // 否则全部重定向到登录页
        }
    }
});

export default router;

 用户页js

E:\dongli\power-supervision-system\src\store\modules\user.js

import { login, logout } from '@/api/moudules/login'
import authToken from '@/utils/auth'
import { ElMessage } from 'element-plus'

const user = {
    namespaced: true,
    state: {
        token: authToken.getToken(),
        // 用户名
        username: localStorage.getItem('username'),
        userId: ''
    },
    mutations: {
        SET_TOKEN: (state, token) => {
            state.token = token
        },
        SET_USERNAME: (state, username) => {
            state.username = username;
            localStorage.setItem("username",username);
        },
        SET_USERID: (state, userId) => {
            localStorage.setItem("userId",userId);
            state.userId = userId;
        }
    },
    actions: {
        // 登录
        Login({ commit }, userInfo) {
            return new Promise((resolve, reject) => {
                login(userInfo).then(res => {
                    if(res.code == 200) {
                        let data = res.data
                        commit('SET_USERNAME', data.loginName)
                        commit('SET_USERID', data.userId)
                        commit('SET_TOKEN', data.token)
                        authToken.setToken(data.token)
                        resolve(data)
                    }else{
                        ElMessage.error(res.msg)
                    }
                }).catch(error => {
                    reject(error)
                })
            })
        },
        // 退出登录
        LogOut({ commit }) {
            return new Promise((resolve, reject) => {
                logout().then(() => {
                    commit('SET_TOKEN', '')
                    authToken.removeToken()
                    resolve()
                }).catch(error => {
                    reject(error)
                })
            })
        }
    }
}

export default user

 设置token

E:\dongli\power-supervision-system\src\utils\auth.js

import Cookies from 'js-cookie'

const authToken = {
    // 在Cookie中记录登录状态的key
    tokenKey: 'token',

    // 获取Token
    getToken: function() {
        return Cookies.get(this.tokenKey)
    },
    
    // 设置Token
    setToken: function(token) {
        // TODO: 设置token,并填写有效期
        var maxAge = new Date(new Date().getTime() + 1800 * 1000)
        Cookies.set(this.tokenKey, token, {
            expires: maxAge
        })
    },

    // 移除Token
    removeToken: function(){
        Cookies.remove(this.tokenKey)
    }
}

export default authToken

request 请求

E:\dongli\power-supervision-system\src\utils\request.js

import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from "@/router/index";
import Auth from '@/utils/auth'

let base_url;
if (process.env.NODE_ENV === 'production') {
    base_url = window.g.baseURL
} else if (process.env.NODE_ENV === 'development') {
    base_url = '/api/'
}

// create an axios instance
const service = axios.create({
    baseURL: base_url, // api 的 base_url
    withCredentials: true,
    timeout: 300000 // request timeout
})

// request interceptor
service.interceptors.request.use(
    config => {
        if(!Auth.getToken())
        {
            //router.push("/login"); 
            return config
        }else{
            config.headers['token'] = Auth.getToken()
            return config
        }
        
    },
    error => {
        // Do something with request error
        Promise.reject(error)
    }
)

// response interceptor
service.interceptors.response.use(
    response => {
        let data;
        // IE9时response.data是undefined,因此需要使用response.request.responseText(Stringify后的字符串)
        if (response.data == undefined) {
            data = JSON.parse(response.request.responseText)
        } else {
            if(response.config.responseType)
                 data = response
            else{
                data = response.data
                if (data.code === 600) {
                    console.log('跳到登录页')
                    //清空token
                    Auth.removeToken()
                    router.push("/login"); 
                }
            }
            
        }
        return data
    },
    error => {
        if (error && error.response) {
            switch (error.response.status) {
                case 400:
                error.message = '数据请求错误'
                break
                case 401:
                error.message = '未授权,请登录'
                break
                case 403:
                error.message = '拒绝访问'
                break
                case 404:
                error.message = '请求地址出错'
                break
                case 408:
                error.message = '请求超时'
                break
                case 500:
                error.message = '服务器内部错误'
                break
                case 501:
                error.message = '服务未实现'
                break
                case 502:
                error.message = '网关错误'
                break
                case 503:
                error.message = '服务不可用'
                break
                case 504:
                error.message = '网关超时'
                break
                case 505:
                error.message = 'HTTP版本不受支持'
                break
                default:
            }
        }
        ElMessage({
            message: error.message,
            type: 'error',
            duration: 5 * 1000
        })
        return Promise.reject(error)
    }
)

export default service

初始登录页?

E:\dongli\power-supervision-system\src\views\Login-bak.vue

<template>
    <div class="login-wrap">
        <div class="ms-login">
            <div class="ms-title">后台管理系统</div>
            <el-form :model="param" :rules="rules" ref="login" label-width="0px" class="ms-content">
                <el-form-item prop="username">
                    <el-input v-model="param.username" placeholder="username">
                        <template #prepend>
                            <el-button icon="el-icon-user"></el-button>
                        </template>
                    </el-input>
                </el-form-item>
                <el-form-item prop="password">
                    <el-input
                        type="password"
                        placeholder="password"
                        v-model="param.password"
                        @keyup.enter="submitForm()"
                    >
                        <template #prepend>
                            <el-button icon="el-icon-lock"></el-button>
                        </template>
                    </el-input>
                </el-form-item>
                <div class="login-btn">
                    <el-button type="primary" @click="submitForm()">登录</el-button>
                </div>
                <p class="login-tips">Tips : 用户名和密码随便填。</p>
            </el-form>
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            param: {
                username: "admin",
                password: "123123"
            },
            rules: {
                username: [
                    { required: true, message: "请输入用户名", trigger: "blur" }
                ],
                password: [
                    { required: true, message: "请输入密码", trigger: "blur" }
                ]
            }
        };
    },
    created() {
        this.$store.commit("clearTags");
    },
    methods: {
        submitForm() {
            this.$refs.login.validate(valid => {
                if (valid) {
                    this.$message.success("登录成功");
                    localStorage.setItem("ms_username", this.param.username);
                    this.$router.push("/");
                } else {
                    this.$message.error("请输入账号和密码");
                    return false;
                }
            });
        }
    }
};
</script>

<style scoped>
.login-wrap {
    position: relative;
    width: 100%;
    height: 100%;
    background-image: url(../assets/img/login-bg.jpg);
    background-size: 100%;
}
.ms-title {
    width: 100%;
    line-height: 50px;
    text-align: center;
    font-size: 20px;
    color: #fff;
    border-bottom: 1px solid #ddd;
}
.ms-login {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 350px;
    margin: -190px 0 0 -175px;
    border-radius: 5px;
    background: rgba(255, 255, 255, 0.3);
    overflow: hidden;
}
.ms-content {
    padding: 30px 30px;
}
.login-btn {
    text-align: center;
}
.login-btn button {
    width: 100%;
    height: 36px;
    margin-bottom: 10px;
}
.login-tips {
    font-size: 12px;
    line-height: 30px;
    color: #fff;
}
</style>

登录页 vue 

E:\dongli\power-supervision-system\src\views\login.vue

<template>
    <div class="login-container">

        <!-- 登录 -->
        <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">
            <div class="title-container">
                <h3 class="title">{{title}}</h3>
            </div>

            <el-form-item prop="loginName">
                <span class="iconfont icon-user"></span>
                <el-input v-model="loginForm.loginName" placeholder="请输入账号" name="loginName" type="text" auto-complete="on" />
            </el-form-item>

            <el-form-item prop="password">
                <span class="iconfont icon-password"></span>
                <el-input v-model="loginForm.password" :type="passwordType" placeholder="请输入密码" name="password" auto-complete="on" @keyup.enter="handleLogin" />
                <span class="show-pwd" @click="showPwd">
                    <i class="iconfont" :class="passwordType === 'password' ? 'icon-eye' : 'icon-eye-open'"></i>
                </span>
            </el-form-item>

            <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.prevent="handleLogin">
                登录
            </el-button>

        </el-form>

    </div>
</template>

<script>
import axios from 'axios';
export default {
    name: 'Login',
    data() {
        return {
            title: '',
            loginForm: {
                loginName: 'admin',
                password: '123456'
            },
            loginRules: {
                loginName: [{
                    required: true,
                    trigger: 'blur',
                    'message': '请输入用户名'
                }],
                password: [{
                    required: true,
                    trigger: 'blur',
                    'message': '请输入密码'
                }]
            },
            passwordType: 'password',
            loading: false,
            redirect: undefined
        }
    },
    watch: {
        $route: {
            handler: function(route) {
                this.redirect = route.query && route.query.redirect
            },
            immediate: true
        }
    },
    computed: {
        title() {
            return window.g.title
        }
    },
    methods: {
        showPwd() {
            if (this.passwordType === 'password') {
                this.passwordType = ''
            } else {
                this.passwordType = 'password'
            }
        },
        handleLogin() {
            this.$refs.loginForm.validate(valid => {
                if (valid) {
                    this.$store.dispatch("user/Login", this.loginForm).then((res) => {
                        this.$router.push('/');
                    })
                    .catch(() => {
                        this.loading = false;
                    });
                }
                else {
                    return false
                }
            })
        }
    }
}
</script>

<style lang="scss">

$bg: #283443;
$light_gray: #eee;
$cursor: #fff;

@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
    .login-container .el-input input{
        color: $cursor;
        &::first-line {
            color: $light_gray;
        }
    }
}

/* reset element-ui css */
.login-container {
    .el-input {
        display: inline-block;
        height: 47px;
        width: 85%;
        input {
            height: 47px;
            padding: 12px 5px;
            color: $light_gray;
            border: 0px;
            border-radius: 0px;
            background: transparent;
            caret-color: $cursor;
            -webkit-appearance: none;
            &:-webkit-autofill {
                -webkit-box-shadow: 0 0 0px 1000px $bg inset !important;
                -webkit-text-fill-color: $cursor !important;
            }
        }
    }
    .el-form-item {
        color: #454545;
        border: 1px solid rgba(255, 255, 255, 0.1);
        border-radius: 5px;
        background: rgba(0, 0, 0, 0.1);
    }
}
</style>

<style lang="scss" scoped>
// $bg: #2d3a4b;
$bg: transparent;
$dark_gray: #889aa4;
$light_gray: #eee;
.login-container {
    width: 100%;
    min-height: 100%;
    background-color: #2d3a4b;
    overflow: hidden;
    .login-form {
        position: relative;
        width: 520px;
        max-width: 100%;
        padding: 160px 35px 0;
        margin: 0 auto;
        overflow: hidden;
    }
    .tips {
        margin-bottom: 10px;
        font-size: 14px;
        color: #fff;
        span {
            &:first-of-type {
                margin-right: 16px;
            }
        }
    }
    .iconfont {
        display: inline-block;
        width: 30px;
        padding: 6px 5px;
        color: $dark_gray;
        font-size: 16px;
        vertical-align: middle;
        text-align: center;
    }
    .title-container {
        position: relative;
        .title {
            margin: 0px auto 40px auto;
            font-size: 26px;
            font-weight: bold;
            color: $light_gray;
            text-align: center;
        }
        .set-language {
            position: absolute;
            top: 3px;
            right: 0;
            font-size:18px;
            color: #fff;
            cursor: pointer;
        }
    }
    .show-pwd {
        position: absolute;
        top: 0;
        right: 0;
        font-size: 16px;
        color: $dark_gray;
        cursor: pointer;
        user-select: none;
    }
}
</style>

 头部信息退出登录 

E:\dongli\power-supervision-system\src\views\layout\components\Header.vue

<template>
    <div class="header">
        
        <div class="logo">{{title}}</div>
        <!-- 折叠按钮 -->
        <!-- <div class="collapse-btn" @click="collapseChage">
            <i v-if="!collapse" class="iconfont icon-fold"></i>
            <i v-else class="iconfont icon-unfold"></i>
        </div> -->
        <div class="header-right">
            <div class="header-user-con">
                <!-- 消息中心 -->
                <!-- <div class="btn-bell">
                    <el-tooltip
                        effect="dark"
                        :content="message?`有${message}条未读消息`:`消息中心`"
                        placement="bottom"
                    >
                        <router-link to="/tabs">
                            <i class="el-icon-bell"></i>
                        </router-link>
                    </el-tooltip>
                    <span class="btn-bell-badge" v-if="message"></span>
                </div> -->
                <!-- 用户头像 -->
                <div class="user-avator">
                    <img class="userImg" src="@/assets/img/user.png" />
                </div>
                <!-- 用户名下拉菜单 -->
                <el-dropdown class="user-name" trigger="click" @command="handleCommand">
                    <span class="el-dropdown-link">
                        {{username}}
                        <i class="el-icon-caret-bottom"></i>
                    </span>
                    <template #dropdown>
                        <el-dropdown-menu>
                            <el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
                        </el-dropdown-menu>
                    </template>
                </el-dropdown>
            </div>
        </div>
    </div>
</template>
<script>
import Auth from '@/utils/auth'
export default {
    data() {
        return {
            fullscreen: false,
            name: "",
            message: 2
        };
    },
    computed: {
        username() {
            let username = localStorage.getItem("username");
            return username ? username : this.name;
        },
        collapse() {
            return this.$store.state.collapse;
        },
        title() {
            return window.g.title;
        }
    },
    methods: {
        // 用户名下拉菜单选择事件
        handleCommand(command) {
            if (command == "loginout") {
                localStorage.removeItem("username");
                localStorage.removeItem("userId");
                Auth.removeToken()
                this.$router.push("/login");
            }
        },
        // 侧边栏折叠
        collapseChage() {
            this.$store.commit("hadndleCollapse", !this.collapse);
        }
    },
    mounted() {
        if (document.body.clientWidth < 1500) {
            this.collapseChage();
        }
        let username = localStorage.getItem("username");
            return username ? username : this.name;
    }
};
</script>
<style lang="scss" scoped>
.header {
    position: relative;
    box-sizing: border-box;
    width: 100%;
    height: 70px;
    font-size: 22px;
    color: #fff;
    box-shadow: 10px 10px 5px #888888;
}
.collapse-btn {
    float: left;
    padding: 0 20px;
    cursor: pointer;
    line-height: 70px;
    i.iconfont {
        font-size: 20px;
    }
}
.header .logo {
    float: left;
    width: 250px;
    line-height: 70px;
    text-align: center;
    background-color: #18223d;
}
.header-right {
    float: right;
    padding-right: 50px;
}
.header-user-con {
    display: flex;
    height: 70px;
    align-items: center;
}
.btn-fullscreen {
    transform: rotate(45deg);
    margin-right: 5px;
    font-size: 24px;
}
.btn-bell,
.btn-fullscreen {
    position: relative;
    width: 30px;
    height: 30px;
    text-align: center;
    border-radius: 15px;
    cursor: pointer;
}
.btn-bell-badge {
    position: absolute;
    right: 0;
    top: -2px;
    width: 8px;
    height: 8px;
    border-radius: 4px;
    background: #f56c6c;
    color: #fff;
}
.btn-bell .el-icon-bell {
    color: #fff;
}
.user-name {
    margin-left: 10px;
}
.user-avator {
    margin-left: 20px;
}
.user-avator img {
    display: block;
    width: 28px;
    height: 28px;
    border-radius: 50%;
}
.el-dropdown-link {
    // color: #fff;
    cursor: pointer;
}
.el-dropdown-menu__item {
    text-align: center;
}
</style>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值