浮光商城11-前端登陆状态判断

文章介绍了如何在Vue前端通过Header.vue组件实现登录前后状态的差异化显示,使用Vuex保存和更新用户登录状态,并利用路由守卫确保用户认证。还涵盖了退出登录功能,包括清除Vuex数据和浏览器存储。

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

01. 登录状态显示

前端的显示中,需要对登录前后两种状态进行区分。

在这里插入图片描述

在这里插入图片描述

components/Header.vue,先实现根据登录与否差异化显示。

子组件代码:

<template>
    <div class="header-box">
      <div class="header">
        <div class="content">
          <div class="logo">
            <router-link to="/"><img src="../assets/logo.svg" alt=""></router-link>
          </div>
          <ul class="nav">
              <li v-for="nav in nav.header_nav_list">
                <a :href="nav.link" v-if="nav.is_http">{{nav.title}}</a>
                <router-link :to="nav.link" v-else>{{nav.title}}</router-link>
              </li>
          </ul>
          <div class="search-warp">
            <div class="search-area">
              <input class="search-input" placeholder="请输入关键字..." type="text" autocomplete="off">
              <div class="hotTags">
                <router-link to="/search/?words=Vue" target="_blank" class="">Vue</router-link>
                <router-link to="/search/?words=Python" target="_blank" class="last">Python</router-link>
              </div>
            </div>
            <div class="showhide-search" data-show="no"><img class="imv2-search2" src="../assets/search.svg" /></div>
          </div>
          <div class="login-bar logined-bar" v-show="state.is_login">
            <div class="shop-cart ">
              <img src="../assets/cart.svg" alt="" />
              <span><router-link to="/cart">购物车</router-link></span>
            </div>
            <div class="login-box ">
              <router-link to="">我的课堂</router-link>
              <el-dropdown>
                <span class="el-dropdown-link">
                  <el-avatar class="avatar" size="50" src="https://fuguangapi.oss-cn-beijing.aliyuncs.com/avatar.jpg"></el-avatar>
                </span>
                <template #dropdown>
                  <el-dropdown-menu>
                    <el-dropdown-item :icon="UserFilled">学习中心</el-dropdown-item>
                    <el-dropdown-item :icon="List">订单列表</el-dropdown-item>
                    <el-dropdown-item :icon="Setting">个人设置</el-dropdown-item>
                    <el-dropdown-item :icon="Position">注销登录</el-dropdown-item>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </div>
          </div>
          <div class="login-bar" v-show="!state.is_login">
            <div class="shop-cart full-left">
              <img src="../assets/cart.svg" alt="" />
              <span><router-link to="/cart">购物车</router-link></span>
            </div>
            <div class="login-box full-left">
              <span @click="state.show_login=true">登录</span>
              &nbsp;/&nbsp;
              <span>注册</span>
            </div>
          </div>
        </div>
      </div>
    </div>
    <el-dialog :width="600" v-model="state.show_login">
      <Login @successHandle="login_success"></Login>
    </el-dialog>
</template>
<script setup>
    import {UserFilled, List, Setting, Position} from '@element-plus/icons-vue'
    import Login from "./Login.vue"
    import nav from "../api/nav";
    import {reactive} from "vue";

    const state = reactive({
        is_login: true,  // 登录状态
        show_login: false,
    })

    // 请求头部导航列表
    nav.get_header_nav().then(response=>{
        nav.header_nav_list = response.data
    })

    // 用户登录成功以后的处理
    const login_success = ()=>{
        state.show_login = false
    }

</script>

新增样式:

<style scoped>
    /* 登陆后状态栏 */
    .logined-bar{
        margin-top: 0;
        height: 72px;
        line-height: 72px;
    }
    .header .logined-bar .shop-cart{
        height: 32px;
        line-height: 32px;
    }
    .logined-bar .login-box{
        height: 72px;
        line-height: 72px;
        position: relative;
    }
    .logined-bar .el-avatar{
        float: right;
        width: 50px;
        height: 50px;
        position: absolute;
        top: -10px;
        left: 10px;
        transition: transform .5s ease-in .1s;
    }
    .logined-bar .el-avatar:hover{
        transform: scale(1.3);
    }
</style>

如果图标没有显示,可以采用安装以下组件:

yarn add @element-plus/icons-vue

02. 使用Vuex保存状态

Vuex官方地址:https://next.vuex.vuejs.org/zh/index.html

cd ~/Desktop/fuguang/fuguang_web
# 在客户端项目根目录下执行安装命令

yarn add vuex@next

Vuex初始化,是在src目录下创建store目录,store目录下创建index.js文件对vuex进行初始化:

import {createStore} from "vuex"

// 实例化一个vuex存储库
export default createStore({
    state () {  // 数据存储位置,相当于组件中的data
        return {
          user: {}
        }
    },
    mutations: { // 操作数据的方法,相当于methods
        login (state, user) {  // state 就是上面的state   state.user 就是上面的数据
          state.user = user
        }
    }
})

main.js中注册vuex,代码:

import { createApp } from 'vue'
import App from './App.vue'
import router from "./router";
import store from "./store"
import 'element-plus/theme-chalk/index.css'


createApp(App).use(router).use(store).mount('#app')

在components/Login.vue子组件中,登录成功以后,

从JWT中获取载荷信息,记录用户信息到vuex中。

<script setup>
    import user from "../api/user"
    import { ElMessage } from 'element-plus'
    const emit = defineEmits(["login_success",])

    import {useStore} from "vuex"
    const store = useStore()

    const loginHandler = ()=>{
        // 登录处理
        if(user.username.length<1 || user.password.length<1){
            // 错误提示
            ElMessage.error('错了哦,用户名或密码不能为空!');
            return false // 在函数/方法中,可以阻止代码继续往下执行
        }

        // 发送请求
        user.user_login({
            username: user.username,
            password: user.password
        }).then(response=>{
            // 保存token,并根据用户的选择,是否记住密码
            localStorage.removeItem("token")
            sessionStorage.removeItem("token")
            if(user.remember){ // 判断是否记住登录状态
                // 记住登录
                localStorage.token = response.data.token
            }else{
                // 不记住登录,关闭浏览器以后就删除状态
                sessionStorage.token = response.data.token
            }
            // vuex存储用户登录信息,保存token,并根据用户的选择,是否记住密码
            let payload = response.data.token.split(".")[1]  // 载荷
            let payload_data = JSON.parse(atob(payload)) // 用户信息
            console.log(payload_data)
            store.commit("login", payload_data)

            // 成功提示
            ElMessage.success("登录成功!")
            // 关闭登录弹窗,对外发送一个登录成功的信息
            user.account = ""
            user.password = ""
            user.mobile = ""
            user.code = ""
            user.remember = false
            emit("login_success")

        }).catch(error=>{
            console.log(error);
            ElMessage.error("登录异常!")
        })
    }

</script>

记录下来了以后,我们就可以直接components/Header.vue中读取Vuex中的用户信息。

<template>
    <div class="header-box">
      <div class="header">
        <div class="content">
          <div class="logo">
            <router-link to="/"><img src="../assets/logo.png" alt=""></router-link>
          </div>
          <ul class="nav">
              <li v-for="item in nav.header_nav_list">
                <a v-if="item.is_http" :href="item.link">{{item.title}}</a>
                <router-link v-else :to="item.link">{{item.title}}</router-link>
              </li>
          </ul>
          <div class="search-warp">
            <div class="search-area">
              <input class="search-input" placeholder="请输入关键字..." type="text" autocomplete="off">
              <div class="hotTags">
                <router-link to="/search/?words=Vue" target="_blank" class="">Vue</router-link>
                <router-link to="/search/?words=Python" target="_blank" class="last">Python</router-link>
              </div>
            </div>
            <div class="showhide-search" data-show="no"><img class="imv2-search2" src="../assets/search.svg" /></div>
          </div>
          <div class="login-bar logined-bar" v-if="store.state.user.user_id">
            <div class="shop-cart ">
              <img src="../assets/cart.svg" alt="" />
              <span><router-link to="/cart">购物车</router-link></span>
            </div>
            <div class="login-box ">
              <router-link to="">我的课堂</router-link>
              <el-dropdown>
                <span class="el-dropdown-link">
                  <el-avatar class="avatar" size="50" src="https://luffycityapi.oss-cn-beijing.aliyuncs.com/avatar.jpg"></el-avatar>
                </span>
                <template #dropdown>
                  <el-dropdown-menu>
                      <el-dropdown-item :icon="UserFilled" >学习中心</el-dropdown-item>
                      <el-dropdown-item :icon="List">订单列表</el-dropdown-item>
                      <el-dropdown-item :icon="Setting">个人设置</el-dropdown-item>
                      <el-dropdown-item :icon="Position">注销登录</el-dropdown-item>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </div>
          </div>
          <div class="login-bar" v-else>
            <div class="shop-cart full-left">
              <img src="../assets/cart.svg" alt="" />
              <span><router-link to="/cart">购物车</router-link></span>
            </div>
            <div class="login-box full-left">
              <span @click="state.show_login=true">登录</span>
              &nbsp;/&nbsp;
              <span>注册</span>
            </div>
          </div>
        </div>
      </div>
    </div>
    <el-dialog :width="600" v-model="state.show_login">
    <Login @successHandle="login_success"></Login>
    </el-dialog>
</template>
<script setup>
    import Login from "./Login.vue"

    import nav from "../api/nav"
    import {reactive} from "vue"
    import {useStore} from "vuex"
    const store = useStore()

    const state = reactive({
        show_login: false,
    })

    nav.get_header_nav().then(response=>{
        nav.header_nav_list = response.data;
    })

    // 用户登录成功以后的处理
    const login_success = (token)=>{
        state.show_login = false
    }

</script>

因为vuex默认是保存数据在内存中的,

基于浏览器开发的网页,如果刷新网页会存在数据丢失的情况。

我们可以把store数据永久存储到localStorage中。

这里就需要使用插件vuex-persistedstate来实现。

在前端项目的根目录下执行安装命令

cd ~/Desktop/fuguang/fuguang_web

yarn add vuex-persistedstate

在vuex的store/index.js文件中导入此插件。

import {createStore} from "vuex"
import createPersistedState from "vuex-persistedstate"

// 实例化一个vuex存储库
export default createStore({
    // 调用永久存储vuex数据的插件,localstorage里会多一个名叫vuex的Key,里面就是vuex的数据
    plugins: [createPersistedState()],
    state(){  // 相当于组件中的data,用于保存全局状态数据
        return {
            user: {}
        }
    },
    getters: {
        getUserInfo(state){
            // 从jwt的载荷中提取用户信息
            let now = parseInt( (new Date() - 0) / 1000 );
            if(state.user.exp === undefined) {
                // 没登录
                state.user = {}
                localStorage.token = null;
                sessionStorage.token = null;
                return null
            }

            if(parseInt(state.user.exp) < now) {
                // 过期处理
                state.user = {}
                localStorage.token = null;
                sessionStorage.token = null;
                return null
            }
            return state.user;
        }
    },
    mutations: { // 相当于组件中的methods,用于操作state全局数据
        login(state, payload){
            state.user = payload; // state.user 就是上面声明的user
        }
    }
})

完成了登录功能以后,

我们要防止用户翻墙访问需要认证身份的页面时,

可以基于vue-router的导航守卫来完成。

src/router/index.js,代码:

import {createRouter, createWebHistory} from 'vue-router'
import store from "../store";
// 路由列表
const routes = [
  {
    meta:{
        title: "首页-浮光在线",
        keepAlive: true
    },
    path: '/',         // uri访问地址
    name: "Home",
    component: ()=> import("../views/Home.vue")
  },
  {
    meta:{
        title: "用户登录-浮光在线",
        keepAlive: true
    },
    path:'/login',      // uri访问地址
    name: "Login",
    component: ()=> import("../views/Login.vue")
  },{
    meta:{
        title: "luffy2.0-个人中心",
        keepAlive: true,
        authorization: true,
    },
    path: '/user',
    name: "User",
    component: ()=> import("../views/User.vue"),
  },
]

// 路由对象实例化
const router = createRouter({
  // history, 指定路由的模式
  history: createWebHistory(),
  // 路由列表
  routes,
});



// 导航守卫
router.beforeEach((to, from, next)=>{
  document.title=to.meta.title
  // 登录状态验证
  if (to.meta.authorization && !store.getters.getUserInfo) {
    next({"name": "Login"})
  }else{
    next()
  }
})

// 暴露路由对象
export default router

src/views/User.vue,代码:

<template>
	<div>用户中心</div>
</template>

<script>
    export default {
        name: "User"
    }
</script>

<style scoped>

</style>

03. 退出登录功能

退出登录时,需要将状态管理中的数据清除,同时清除浏览器的本地存储。

在vuex的store/index.js中编写一个【登录注销】的方法logout,

代码:

import {createStore} from "vuex"
import createPersistedState from "vuex-persistedstate"

// 实例化一个vuex存储库
export default createStore({
    // 调用永久存储vuex数据的插件,localstorage里会多一个名叫vuex的Key,里面就是vuex的数据
    plugins: [createPersistedState()],
    state(){  // 相当于组件中的data,用于保存全局状态数据
        return {
            user: {}
        }
    },
    getters: {
        getUserInfo(state){
            let now = parseInt( (new Date() - 0) / 1000 );
            if(state.user.exp === undefined) {
                // 没登录
                state.user = {}
                localStorage.token = null;
                sessionStorage.token = null;
                return null
            }

            if(parseInt(state.user.exp) < now) {
                // 过期处理
                state.user = {}
                localStorage.token = null;
                sessionStorage.token = null;
                return null
            }
            return state.user;
        }
    },
    mutations: { // 相当于组件中的methods,用于操作state全局数据
        login(state, payload){
            state.user = payload; // state.user 就是上面声明的user
        },
        logout(state){ // 退出登录
            state.user = {}
            localStorage.token = null;
            sessionStorage.token = null;
        }
    }
})

在用户点击头部登录栏的注销登录时绑定登录注销操作。components/Header.vue

代码:

<el-dropdown-item :icon="Position" @click="logout">注销登录</el-dropdown-item>
<script setup>
    import {UserFilled, List, Setting, Position} from '@element-plus/icons-vue'
    import Login from "./Login.vue"
    import nav from "../api/nav";
    import {reactive} from "vue";

    import {useStore} from "vuex"
    const store = useStore()

    const state = reactive({
        show_login: false,
    })

    // 请求头部导航列表
    nav.get_header_nav().then(response=>{
        nav.header_nav_list = response.data
    })

    // 用户登录成功以后的处理
    const login_success = ()=>{
        state.show_login = false
    }

    // 登录注销的处理
    const logout = ()=>{
        store.commit("logout");
    }


</script>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值