Vue + Element UI 实现权限管理系统 前端篇(四):优化登录流程

继续笔记:在这里基本是大框架搭出来了。

完善登入流程

1.丰富登入界面

1.1 Element 参考 搭建

<template>
    <el-form 
        :model="loginForm" 
        ref="loginForm"
        :rules="fieldRules"
        label-position="left"
        label-width="0px"
        class="demo-ruleForm login-container"
        > 
        <h3 class="title">登入系统</h3>
        <el-form-item prop="account">
            <el-input type="text" v-model="loginForm.account" auto-complete="off" placeholder="账号"></el-input>
        </el-form-item>
        
        <el-form-item prop="password">
            <el-input type="password" v-model="loginForm.password" auto-complete="off" placeholder="密码"></el-input>
        </el-form-item>
        <!-- <el-checkbox v-model="checked" checked class="remember">记住密码</el-checkbox> -->
        <el-form-item style="width: 100%;">
            <el-button type="primary" style="width: 48%;" @click.native.prevent="reset">重 置</el-button>
            <el-button type="primary" style="width: 48%;" @click.native.prevent="login" :loading="logining">登 入</el-button>
        </el-form-item>
    </el-form>
</template>

1.2添加页面组件显示规则和操作响应,登入成功后将登入信息存储到本地会话,用于配置路由跳转

<script>
    import mock from '@/mock/index.js';
    import Cookies from 'js-cookie';
    
    export default {
        name: 'Login',
        data() {
            return {
                logining: false,
                loginForm: {            // 账号,密码
                    account: 'admin',
                    password: 'admin',
                },
                fieldRules: {           // 输入框规则 rules:表单验证规则,即 async-validator 所使用的校验规则,类型为 Object。
                    account: [
                        { required: true, message: '请输入账号', trigger: 'blur' },   // 'blur'是鼠标失去焦点的时候会触发验证  required 是否必填,如果不设置,则会根据校验规则自动生成
                    ],
                    password: [
                        { required: true, message: '请输入密码', trigger: 'blur' },
                    ],
                },
                // checked: true
            }
        },
        methods: {
            login() {
                let userInfo = { account: this.loginForm.account, password: this.loginForm.password };  // 提取本地账号密码
                this.$api.login(JSON.stringify(userInfo)).then((res) => {       // 传递给后台本地账号密码返回 token ,现在是没有验证账号密码过程的
                    alert(res.data.token)
                    Cookies.set('token', res.data.token);    // 放置token到Cookie
                    sessionStorage.setItem('user', userInfo.account);   // 保存用户到本地会话
                    this.$router.push('/');     // 登入成功,转跳到主页
                }).catch((res) => {
                    alert(res);
                })
            },
            reset() {
                this.$refs.loginForm.resetFields();
            }
        }
    }
</script>   

1.3优化样式

<style lang="scss" scoped>
    .login-container {
        width: 400px;
        background: #fff;
        background-clip: padding-box;
        margin: 180px auto;
        padding: 35px 35px 15px;
        box-sizing: border-box;
        border: 1px solid #eaeaea;
        border-radius: 5px;
        box-shadow: 0 0 30px rgba(0, 0, 0 , 0.1);
        .title {
            text-align: center;
            color: #505458;
            margin: 0 auto 40px;
        }
        // .remember {
        //     margin: 0 0 25px;
        // }
    }

1.4最后效果

 

2.修改接口 post 请求

修改 http/interface.js,把请求类型改为 post,并传入 data 参数。

3.修改对应的 mock 接口

修改 mock/modules/logins.js,把请求类型改为 post

// 登入接口
export function login () {
    return {
        // isOpen: false,
        url: 'http://localhost:8080/login',
        type: 'post',
        data: {
            'msg': 'success',
            'code': 0,
            'data': {
                'token': '4344323121398'
                // 其它数据
            }
        }
    }
}

4.添加导航守卫(只有登入后才能访问相应权限界面)

// 导航守卫
router.beforeEach((to, from, next) => {
    // 登入界面成功之后,会把用户信息保存在会话
    // 存在时间为会话生命周期,页面关闭既失效
    let user = sessionStorage.getItem('user');
    if(to.path == '/login') {
        // 访问登入界面,如果用户会话信息存在,代表已登入过,转跳到主页
        if(user) {
            next({ path: '/' })
        }else {
            next()
        }
    }else {
        // 访问非登入界面,且用户信息不存在,代表未登入,则转跳到登入界面
        if(!user) {
            next({ path: '/login' })
        }else {
            next()
        }
    }
})

5.完善主页界面

5.1 构建主界面

<template>
    <div class="container">
        <!-- 
            row: 行概念
            col: 列概念, col组件的 :span属性的布局调整 栅格占据的列数,默认值 24 
         -->
        <el-row class="header">           
                <!-- 大标题 class 的切换 看 col 是否失效 lapse:失效 -->
                <el-col :span="5" class="logo" :class="isCollapse ? 'logo-collapse-width' : 'logo-width'"> 
                    <img :src="this.logo" />
                    {{ isCollapse ? sysName : sysName }}
                </el-col>
                <!-- icon @click.prevent: 阻止默认行为 -->
                <el-col :span="1">
                    <div class="tools" @click.prevent="collapse">
                        <i class="el-icon-menu"></i>
                    </div>
                </el-col>
                <!-- 顶部导航栏,
                    默认是垂直模式,通过 mode="horizontal" 设置成水平模式 horizontal: 水平的
                    default-openeds    当前打开的 sub-menu 的 index 的数组
                -->
                <el-col :span="13">
                    <div class="hearNavBar">
                        <!-- default-active 它的说明内容为:当前激活菜单的 index。(即当前哪一项被设置为高亮) 是为了浏览器刷新后,仍然可以定位到之前选中的路由。
                            mode 属性可以使导航菜单变更为水平模式
                        -->
                        <el-menu
                            :default-active="activeIndex"
                            class="el-menu-demo"
                            background-color="#4b5f6e"
                            text-color="#fff"
                            active-text-color="#ffd04b"
                            mode="horizontal"
                            @select="handleSelectHearNavBar"
                        >
                        <!-- index 唯一标志 默认值 null -->
                            <el-menu-item index="1">首页</el-menu-item>
                            <el-menu-item index="2">消息中心</el-menu-item>
                            <el-menu-item index="3">订单管理</el-menu-item>
                        </el-menu>
                    </div>
                </el-col>
                <el-col :span="5" class="userinfo">
                    <el-dropdown trigger="hover">
                        <span class="el-dropdown-link userinfo-inner">
                            <img :src="this.userAvatar" />
                            {{ username }}
                        </span>
                        <el-dropdown-menu slot="dropdown">
                            <el-dropdown-item>我的消息</el-dropdown-item>
                            <el-dropdown-item>设置</el-dropdown-item>
                            <!-- 自定义组件 @click + .native 后才能触发 -->
                            <el-dropdown-item divided @click.native="logout">退出登入</el-dropdown-item>
                        </el-dropdown-menu>
                    </el-dropdown>
                </el-col>
        </el-row>
        <el-row class="main">
            <aside class="aside">
                <!-- 导航菜单 -->
                <el-menu 
                    default-active="1-2"
                    class="el-menu-vertical-demo"
                    :collapse="isCollapse"
                    @open="handleopen"
                    @close="handleclose"
                    @select="handleselect"
                >
                    <!-- 这里的两个 slot 不懂 -->
                    <el-submenu index="1">
                        <template slot="title">
                            <i class="el-icon-location"></i>
                            <span slot="title">系统管理</span>
                        </template>
                        <el-menu-item index="1-1" @click="$router.push('user')">用户管理</el-menu-item>
                        <el-menu-item index="1-2" @click="$router.push('menu')">菜单管理</el-menu-item>
                    </el-submenu>
                    <el-submenu index="2">
                        <template slot="title">
                            <i class="el-icon-location"></i>
                            <span slot="title">系统监控</span>
                        </template>
                        <el-menu-item index="2-1" @click="$router.push('user')">服务监控</el-menu-item>
                        <el-menu-item index="2-2" @click="$router.push('menu')">任务监控</el-menu-item>
                    </el-submenu>
                    <el-menu-item index="3" disabled>
                        <i class="el-icon-document"></i>
                        <span slot="title">导航三</span>
                    </el-menu-item>
                    <el-menu-item index="4">
                        <i class="el-icon-setting"></i>
                        <span slot="title">导航四</span>
                    </el-menu-item>
                </el-menu>
            </aside>
            <!--  section元素表示一个包含在HTML文档中的独立部分   content:内容    container:容器 -->
            <section class="content-container">
                <div class="grid-content bg-purple-light">
                    <!-- 面包屑 由当前路由动态显示-->
                    <el-col :span="24" class="breadcrumb-container">
                        <el-breadcrumb separator="/" class="breadcrumb-inner">
                            <el-breadcrumb-item v-for="item in $route.matched" :key="item.path">
                                {{ item.name }}
                            </el-breadcrumb-item>
                        </el-breadcrumb>
                    </el-col>
                    <!-- 子路由展示地方,transition过渡效果-->
                    <el-col :span="24" class="content-wrapper">
                        <transition name="fade" mode="out-in">
                            <router-view></router-view>
                        </transition>
                    </el-col>
                </div>
            </section>
        </el-row>

    </div>
</template>

5.2 处理页面事件和页面数据显示,主要是两个事件和在 mounted 函数内获取页面数据。

<script>
    import mock from '@/mock/index.js';
    export default {
        name: 'Home',
        data() {
            return {
                isCollapse: false,
                sysName: "kitty",
                username: "louis",
                userAvater: "",
                logo: "",
                activeIndex: '1'
            }
        },
        methods: {
            handleopen() {
                console.log('handleopen');
            },
            handleclose() {
                console.log('handleclose'); 
            },
            handleselect(a, b) {
                console.log('handleselect');
            },
            handleSelectHearNavBar(key, keyPath) {
                console.log(key, keyPath)
            },
            // 折叠导航栏
            collapse: function() {
                this.isCollapse = !this.isCollapse;
            },
            // 退出登入
            logout: function() {
                // 绑定 this
                let _this = this;
                this.$confirm("确认退出吗?", "提示", {
                    type: "warning"
                })
                .then(() => {
                    sessionStorage.removeItem("user");
                    this.$router.push("/login");
                })
                .catch(() => {});
            }
        },
        // 页面属性初始化
        mounted() {
            this.sysName = "I like Kitty";
            this.logo = require("@/assets/user/logo.png");
            let user = sessionStorage.getItem("user");
            if (user) {
                this.userName = user;
                this.userAvatar = require("@/assets/user/user.png")
            }
        }
    }
</script>

5.3 修饰调整 css 样式 调整完效果如下图所示。

<style lang="scss" scoped>
    // 后续用 flex 布局重构下
    .container {
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        width: 100%;
        .header {
            height: 60px;
            line-height: 60px;
            background: #4b5f6e;
            color:#fff;
            .userinfo {
                text-align: right;
                padding-right: 30px;
                float: right;
                .userinfo-inner {
                    font-size: 20px;
                    cursor: pointer;
                    color: #fff;
                    img {
                        width: 40px;
                        height: 40px;
                        border-radius: 10px;
                        margin: 10px 0px 10px 10px;
                        float: right;
                    }
                }
            }
            .logo {
                height: 60px;
                font-size: 22px;
                border-right-width: 1px;
                border-right-style: solid;
                border-color: rgba(238, 241, 146, 0.5);
                text-align: left;
                img {
                    width: 40px;
                    height: 40px;
                    margin: 10px;
                    float: left;
                }
                .txt {
                    color: #fff;
                }
            }
            // 切换左侧标签栏状态时变化
            .logo-width {
                width: 230px;
            }
            .logo-collapse-width {
                width: 65px;
            }
            //  切换图标
            .tools {
                width: 40px;
                padding: 0 10px;
                // 水平方向上居中
                text-align: center;            
                cursor: pointer;
            }
            .hearNavBar {
                width: 100%;
                height: 60px;
                background: #4b5f6e;
                 
                line-height: 60px;
                cursor: pointer;
            }
        }
        .main {
            width: 100%;
            display: flex;
            position: absolute;
            top: 60px;
            bottom: 0px;
            overflow: hidden;
            aside{
                flex: 0 0 230px;
                width: 230px;
                .el-menu {
                    height: 100%;
                    text-align: left;
                }
            }
            .content-container {
                // 占据剩下的空间
                flex:1;
                padding: 0px;
                .breadcrumb-container {
                    height: 28px;
                    background: rgba(99, 138, 161, 0.2);
                    border: 1px solid rgba(38, 80, 114, 0.2);
                    .breadcrumb-inner {
                        padding: 5px 0px 5px 5px ;
                        text-align: left;
                        font-size: 18px;
                        width: 100%;
                        height: 100%;
                        float: left;
                    }
                }
                .content-wrapper {
                    background: #fff;
                    box-sizing: border-box;
                }
            }
        }
    }
</style>
style

 

6.嵌套路由(主界面的 main)

6.1 在 views 目录下新建 Main、User、Menu 页面,用于菜单路由,内容随便显示点什么就可以

6.2 在 router/index.js 文件中添加子路由,分别指向子页面

6.3 在 views/Home.vue 页面对应的导航菜单中添加点击事件,路由到对应的子页面

6.4 登录之后,点击用户管理,路由到用户管理界面

 


 

学习出处:https://www.cnblogs.com/xifengxiaoma/ 

posted @ 2020-07-31 11:29  Mock777  阅读(475)  评论(0编辑  收藏  举报