Vue管理系統前端系列三登入頁和首頁及`vuex`管理登入狀態

發表於2020-08-21


登入頁面設計

該節記錄了登入介面的設計,以及 vuex 的簡單實用,然後將首頁簡單搭建完成。

先看最終效果圖

先在 views 資料夾下 新建 login/index.vue 檔案。用於登入首頁

vuex 對應 使用者模組

vuex 相關介紹可參考另一篇博文,連結

新建 store/modules/user.js,將登入狀態及內容儲存到狀態管理器中,內容如下:

import router from '@/router'

export default {
    state: {
        userInfo: null, //使用者資訊
        token: null,
    },
    getters: {
        userInfo: (state) => {
            if (state.userInfo == null) {
                let sessionUser = sessionStorage.getItem('user')
                if (sessionUser != null) {
                    state.userInfo = JSON.parse(sessionUser)
                    return sessionUser
                } else {
                    let localeUser = localStorage.getItem('user')
                    if (localeUser != null) {
                        state.userInfo = JSON.parse(localeUser)
                    }
                    return localeUser
                }
            }
            return state.userInfo
        },
        token: (state) => {
            if (state.token == null) {
                let sessionToken = sessionStorage.getItem('token')
                if (sessionToken != null) {
                    state.token = sessionToken
                    return sessionToken
                } else {
                    let localeToken = localStorage.getItem('token')
                    state.token = localeToken
                    return localeToken
                }
            }
            return state.token
        },
    },
    mutations: {
        setToken(state, token, flag = true) {
            if (flag) {
                localStorage.setItem('token', token)
            } else {
                sessionStorage.setItem('token', token)
            }
        },
        setUserInfo(state, userInfo, flag = true) {
            state.userInfo = userInfo
            state.token = userInfo.token
            if (flag) {
                localStorage.setItem('user', JSON.stringify(userInfo))
            } else {
                sessionStorage.setItem('user', JSON.stringify(userInfo))
            }
            this.commit('setToken', userInfo.token, flag)
        },
        logout(state) {
            state.userInfo = null
            state.token = null
            localStorage.removeItem('token')
            localStorage.removeItem('user')
            sessionStorage.removeItem('token')
            sessionStorage.removeItem('user')
            router.push('/login')
        },
    },
    actions: {},
}

然後再在 store/index.js 中將 user 模組引入進來,程式碼如下:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

import user from './modules/user'

const store = new Vuex.Store({
    modules: {
        user: user,
    },
})
export default store

豐富介面

使用 element 的 from 元件,進行設計登入介面。介面程式碼如下:

<template>
    <div class="loginContain">
        <div class="loginBox">
            <h2 class="loginH2"><strong>Vue</strong> 後臺管理系統</h2>
            <el-form :model="loginForm" :rules="loginFormRules" ref="loginForm" label-position="left" label-width="0px" class="login-form">
                <el-form-item prop="username">
                    <el-input type="text" v-model="loginForm.username" auto-complete="off" placeholder="請輸入賬號">
                        <i slot="prefix" class="el-input__icon el-icon-user-solid"></i>
                    </el-input>
                </el-form-item>
                <el-form-item prop="password">
                    <el-input type="password" v-model="loginForm.password" auto-complete="off" placeholder="密碼">
                        <i slot="prefix" class="el-input__icon el-icon-lock"></i>
                    </el-input>
                </el-form-item>
                <el-form-item style="width:100%;">
                    <el-button type="primary" style="  width: 100%; background: #19b9e7;" @click.native.prevent="login" :loading="logining">登 錄</el-button>
                </el-form-item>
            </el-form>
        </div>
    </div>
</template>

登入相關 指令碼 程式碼如下:

<script>
export default {
    data() {
        return {
            logining: false,
            loginForm: {
                username: 'admin',
                password: '123456',
            },
            notifyObj: null,
            loginFormRules: {
                username: [{ required: true, message: '請輸入賬號', trigger: 'blur' }],
                password: [{ required: true, message: '請輸入密碼', trigger: 'blur' }],
            },
        }
    },
    //引入元件
    components: {},
    // 方法
    methods: {
        login() {
            this.logining = true
            let userInfo = { username: this.loginForm.username, password: this.loginForm.password }
            this.$api.login(userInfo).then((res) => {
                if (res.success) {
                    this.$store.commit('setUserInfo', res.data)
                    this.$message({
                        type: 'success',
                        message: '登入成功',
                        duration: 800,
                    })

                    let redirect = '/'
                    if (this.$route.query.redirect !== undefined) {
                        redirect = this.$route.query.redirect
                    }
                    setTimeout(() => {
                        this.logining = false
                        this.$router.push(redirect)
                        if (this.notifyObj) {
                            this.notifyObj.close()
                        }
                        this.notifyObj = null
                    }, 800)
                } else {
                    this.logining = false
                    this.$message({
                        type: 'error',
                        message: '賬號或密碼錯誤',
                    })
                }
            })
        },
    },
    // 計算屬性
    computed: {},
    //未掛載DOM,不能訪問ref為空陣列
    //可在這結束loading,還做一些初始化,實現函式自執行,
    //可以對data資料進行操作,可進行一些請求,請求不易過多,避免白屏時間太長。
    created() {},
    //可在這發起後端請求,拿回資料,配合路由鉤子做一些事情;可對DOM 進行操作
    mounted() {
        this.notifyObj = this.$notify({
            title: '提示',
            message: '管理員,賬號分別為:admin,密碼都為:123456',
            duration: 4000,
            iconClass: 'el-icon-s-opportunity',
        })
    },
}
</script>

對介面進行 簡單的樣式調整,設定背景等,程式碼如下:

<style lang="scss" scoped>
$inputHeight: 48px;

.loginContain {
    height: 100%;
    width: 100%;
    background: url(../../assets/img/loginbg.jpg) no-repeat center center;
    background-size: 100% 100%;
    overflow: hidden;
}
.loginBox {
    height: 455px;
    width: 550px;
    margin: 0 auto;
    position: relative;
    top: 20%;
}
.loginH2 {
    font-size: 28px;
    color: #fff;
    text-align: center;
}

.el-input {
    background-color: transparent;
    border-radius: 20px;
    height: $inputHeight;
    color: #ffffff !important;
    border: rgba(255, 255, 255, 0.2) 2px solid !important;
}
::v-deep .el-input__inner {
    padding-left: 30px;
    background-color: transparent !important;
    border: none !important;
    height: $inputHeight !important;
    color: #ffffff !important;
}
.login-form {
    -webkit-border-radius: 5px;
    border-radius: 5px;
    -moz-border-radius: 5px;
    background-clip: padding-box;
    margin: 10px auto;
    width: 380px;
    padding: 35px 35px 15px 35px;
}
</style>

相關介面上一篇文章中有寫,這裡就不再贅述了。

mock 介面截圖如下,

既然涉及到登入,那肯定需要進行許可權判斷,所以我們使用 路由前置守衛進行控制,

const WhiteListRouter = ['/login', '/notfound'] // 路由白名單
//導航守衛  路由開始前
router.beforeEach((to, from, next) => {
    let user = store.getters.userInfo
    let token = store.getters.token
    var hasAuth = user !== null && token !== null && user !== undefined && token !== undefined
    if (to.path == '/login') {
        if (hasAuth) {
            next({ path: '/' })
        } else {
            next()
        }
    } else {
        if (!hasAuth) {
            //沒登入的情況下  訪問的是否是白名單
            if (WhiteListRouter.indexOf(to.path) !== -1) {
                next()
            } else {
                next({
                    path: '/login',
                })
            }
        } else {
            next()
        }
    }
})

路由相關程式碼

const routes = [
    {
        path: '/',
        name: 'home',
        component: () => import('@/views/home'),
        children: [
            {
                path: '',
                name: '首頁',
                component: () => import('@/views/home/defaultPage'),
            },
        ],
    },
    {
        path: '/login',
        name: 'login',
        component: login,
    },
    {
        path: '/notfound',
        name: 'notfound',
        component: () => import('@/views/notfound'),
    },
    {
        path: '*',
        redirect: '/notfound',
        name: 'notfound',
        component: () => import('@/views/notfound'),
    },
]

其中涉及到的相關頁面,就只有自己建了。

首頁相關程式碼

完整程式碼:

<!-- 首頁 -->
<template>
    <el-container>
        <el-header class="header">
            <!-- logo -->
            <div class="logo" :class="isCollapse ? 'logo-collapse-width' : 'logo-width'">
                <img v-if="isCollapse" src="@assets/logo6065.png" />
                <img v-else src="@assets/logo.png" />
            </div>
            <!-- 摺疊按鈕 -->
            <div class="tools" @click.prevent="collapse">
                <i class="el-icon-menu"></i>
            </div>
            <!-- 頭部導航欄 -->
            <div class="heardNavBar">
                <el-menu default-active="1" class="el-menu-demo" background-color="#4b5f6e" text-color="#fff" active-text-color="#ffd04b" mode="horizontal">
                    <el-menu-item index="1" @click="$router.push('/')">首頁</el-menu-item>
                    <el-menu-item index="2" @click="openUrl('#')">使用文件</el-menu-item>
                    <el-menu-item index="3" @click="openUrl('https://github.com/levy-w-wang/lion-ui')">GitHub</el-menu-item>
                </el-menu>
            </div>
            <!-- 右側資訊 -->
            <div style="float:right">
                <!-- 全屏 -->
                <div style="float:left;line-height: 60px; padding: 0 10px;">
                    <i class="el-icon-full-screen" @click="toggleFull"></i>
                </div>
                <!-- 個人資訊 -->
                <div class="userinfo">
                    <el-dropdown trigger="hover">
                        <span class="el-dropdown-link userinfo-inner">
                            <img src="@assets/img/user.jpg" />
                            {{ userName }}<i class="el-icon-caret-bottom"></i>
                        </span>
                        <el-dropdown-menu slot="dropdown">
                            <el-dropdown-item>
                                <router-link to="/"><i class="el-icon-s-home"></i>首頁</router-link>
                            </el-dropdown-item>
                            <el-dropdown-item>
                                <router-link to="/"><i class="el-icon-s-custom"></i>我的主頁</router-link>
                            </el-dropdown-item>
                            <el-dropdown-item divided>
                                <a @click="loginOut()"><i class="el-icon-switch-button"></i>登出</a>
                            </el-dropdown-item>
                        </el-dropdown-menu>
                    </el-dropdown>
                </div>
            </div>
        </el-header>
        <el-container>
            <el-aside class="aside">
                <!--導航選單  default-active="1-1"-->
                <el-menu class="el-menu-vertical-demo" :collapse="isCollapse">
                    <el-submenu index="1">
                        <template slot="title">
                            <i class="el-icon-setting"></i>
                            <span slot="title">系統管理</span>
                        </template>
                        <el-menu-item index="1-1" @click="$router.push('usermanage')">使用者管理</el-menu-item>
                        <el-menu-item index="1-2" @click="$router.push('menumanage')">選單管理</el-menu-item>
                    </el-submenu>
                    <el-menu-item index="2" disabled>
                        <i class="el-icon-magic-stick"></i>
                        <span slot="title">導航一</span>
                    </el-menu-item>
                    <el-menu-item index="3" disabled>
                        <i class="el-icon-reading"></i>
                        <span slot="title">導航二</span>
                    </el-menu-item>
                </el-menu>
            </el-aside>
            <el-main>
                <router-view></router-view>
            </el-main>
        </el-container>
    </el-container>
</template>

<script>
import screenfull from 'screenfull'
export default {
    data() {
        return {
            isCollapse: false,
            userName: 'Levy',
        }
    },
    //引入元件
    components: {},
    // 方法
    methods: {
        openUrl(url) {
            window.open(url)
        },
        //摺疊導航欄
        collapse: function() {
            this.isCollapse = !this.isCollapse
        },
        loginOut() {
            this.$confirm('確認退出嗎?', '提示', {
                type: 'warning',
            })
                .then(() => {
                    this.$store.commit('logout')
                })
                .catch(() => {})
        },
        toggleFull() {
            if (!screenfull.isEnabled) {
                this.$message({
                    type: 'warning',
                    message: 'you browser can not work',
                })
                return false
            }
            screenfull.toggle()
        },
    },
    // 計算屬性
    computed: {},
    //未掛載DOM,不能訪問ref為空陣列
    //可在這結束loading,還做一些初始化,實現函式自執行,
    //可以對data資料進行操作,可進行一些請求,請求不易過多,避免白屏時間太長。
    created() {},
    //可在這發起後端請求,拿回資料,配合路由鉤子做一些事情;可對DOM 進行操作
    mounted() {},
}
</script>

<style lang="scss" scoped>
.aside {
    flex: 0 0 230px;
    height: 100%;
    .el-menu {
        height: 100%;
        text-align: left;
    }
}
.header {
    padding-left: 0px !important;
    height: 60px;
    line-height: 60px;
    width: 100%;
    background: #4b5f6e;
    color: #fff;
    .logo {
        float: left;
        height: 60px;
        padding: 0;
        margin: 0;
    }
    .logo-width {
        width: 230px;
    }
    .logo-collapse-width {
        width: 65px;
    }

    .tools {
        padding-left: 10px;
        padding-right: 10px;
        text-align: center;
        width: 40px;
        height: 60px;
        line-height: 60px;
        float: left;
        cursor: pointer;
    }
    .heardNavBar {
        float: left;
        background: #4b5f6e;
        padding: 0px 0px;
        height: 60px;
        line-height: 60px;
        font-size: 28px;
        cursor: pointer;
    }

    .userinfo {
        text-align: right;
        padding-right: 24px;
        float: right;
        padding: 0 10px;
        .userinfo-inner {
            font-size: 20px;
            cursor: pointer;
            color: #fff;
            img {
                width: 40px;
                height: 40px;
                border-radius: 10px;
                margin: 10px 0px 10px 10px;
                float: right;
            }
        }
    }
}
</style>

其中設計到了 全屏按鈕 的使用,

需要執行以下安裝命令:

npm install sreenfull --save

相關文章