UNIAPP实战项目笔记54 登录时用state存储用户信息并持久化用户登录和退出登录功能

本文档展示了使用UNIAPP进行登录功能的实现,包括使用state存储用户信息,通过本地存储实现登录状态的持久化,并在App启动时自动加载本地存储的数据。在登录成功后,用户信息被保存,且提供了退出登录的功能,该功能通过调用state中的方法实现。代码示例包括login.vue和my.vue页面,以及vuex中user模块的初始化和退出登录的处理。
摘要由CSDN通过智能技术生成

UNIAPP实战项目笔记54 登录时用state存储用户信息并持久化用户登录和退出登录功能

登录信息各个页面同步使用的是state
登录信息的持久化使用的是本地存储
打开APP自动初始化本地存储数据到state中

实际案例图片

  • 登录页面数据渲染

代码 login.vue页面

登录成功后显示的页面

<template>
    <view class="login">
        <swiper vertical="true" style="height: 100vh;">
            <swiper-item>
                <scroll-view>
                    <view class="login-tel">
                        <view class="tel-main">
                            <view class="close" @tap="goBack">
                                <image class="close-img" src="../../static/img/close-bold.png" mode=""></image>
                            </view>
                            <view class="logo">
                                <image class="logo-img" src="../../static/logo.png" mode=""></image>
                            </view>
                            <view class="tel" @tap="goLoginTel">手机号注册</view>
                            <LoginOther></LoginOther>
                            <view class="login-go">
                                <view class="">已有账号,去登录</view>
                                <image src="../../static/img/arrow-down.png" mode=""></image>
                            </view>
                        </view>
                    </view>
                </scroll-view>
            </swiper-item>
            <swiper-item>
                <scroll-view>
                    <view class="login-tel">
                        <view class="tel-main">
                            <view class="close close-center">
                                <view class="" @tap="goBack">
                                    <image class="close-img" src="../../static/img/close-bold.png" mode=""></image>
                                </view>
                                <view class="login-go">
                                    <image class="close-img" src="../../static/img/up.png" mode=""></image>
                                    <view class="">没账号,去注册</view>
                                </view>
                                <view class=""></view>
                            </view>
                            <view class="login-form">
                                <view class="login-user">
                                    <text class='user-text'>账号</text>
                                    <input type="text" v-model="userName" value="" placeholder="请输入手机号/昵称"/>
                                </view>
                                <view class="login-user">
                                    <text class='user-text'>密码</text>
                                    <input type="safe-password" v-model="userPwd" value="" placeholder="6-16位字符"/>
                                </view>
                            </view>
                            <view class="login-quick">
                                <view class="">忘记密码</view>
                                <view class="">免密登录</view>
                            </view>
                            <view class="tel" @tap="submit">登录</view>
                            <view class="reminder">温馨提示,您可以选择免密登录,更加方便</view>
                            <LoginOther></LoginOther>
                        </view>
                    </view>
                </scroll-view>
            </swiper-item>
        </swiper>
        
        
        
    </view>
</template>

<script>
    import $http from '@/common/api/request.js'
    import LoginOther from '@/components/login/login-other.vue'
    import {mapMutations} from 'vuex'
    export default {
        data() {
            return {
                userName:"",
                userPwd:"",
                rules:{
                    userName:{
                        rule:/\S/,
                        msg:"账号不能为空 "
                    },
                    userPwd:{
                        rule:/^[0-9a-zA-Z]{6,16}$/,
                        msg:"密码应该为6-16位字符"
                    }
                }
            };
        },
        components:{
            LoginOther
        },
        methods:{
            ...mapMutations(['login']),
             goBack(){
                 uni.navigateBack();
             },
             submit(){
                 if( !this.validate('userName') ) return ;
                 if( !this.validate('userPwd') ) return ;
                 
                 uni.showLoading({
                     title:"登录中..."
                 });
                 // setTimeout(()=>{
                 //     uni.hideLoading();
                 //     uni.navigateBack();
                 // },2000)
                 
                 $http.request({
                     url:'/login',
                     method:"POST",
                     data:{
                         userName: this.userName,
                         userPwd : this.userPwd
                     }
                 }).then((res)=>{
                     // 保存用户信息
                     this.login(res.data);
                     console.log(res.data);
                     
                     uni.showToast({
                         title:res.msg,
                         icon:"none"
                     })
                     // console.log(res);
                     uni.hideLoading();
                     if(res.success){
                         uni.navigateBack();
                     }
                 }).catch(()=>{
                     uni.showToast({
                         title:'请求失败',
                         icon:'none'
                     })
                 })
             },
             // 判断验证是否符合要求
             validate(key){
                 let bool = true;
                 if( !this.rules[key].rule.test(this[key]) ){
                     uni.showToast({
                         title:this.rules[key].msg,
                         icon:'none'
                     });
                     bool = false;
                     return false;
                 }
                 return bool;
             },
             // 进入手机号注册页面
             goLoginTel(){
                 uni.navigateTo({
                     url:"/pages/login-tel/login-tel"
                 })
             }
        }
    }
</script>

<style lang="scss">
.login-tel{
    width: 100vw;
    height: 100vh;
}
.tel-main{
    padding: 0 20rpx;
}
.close{
    padding: 120rpx 0;
}
.close-img{
    width: 60rpx;
    height: 60rpx;
}
.logo{
    padding: 0 100rpx;
    padding-bottom: 100rpx;
    display: flex;
    justify-content: center;
    
}
.logo-img{
    width: 200rpx;
    height: 200rpx;
}
.tel{
    width: 100%;
    height: 80rpx;
    line-height: 80rpx;
    text-align: center;
    color: #fff;
    background-color: #40bde8;
    border-radius: 40rpx;
}


.login-go{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
.login-go image{
    width: 60rpx;
    height: 60rpx;
}

// 第二屏
.close-center{
    display: flex;
}
.close-center >view{
    flex: 1;
}
.login-form{
    padding-top: 100rpx;
}
.login-user{
    font-size: 40rpx;
    padding: 10rpx 0 ;
    display: flex;
    align-items: center;
    border-bottom: 2rpx solid #f7f7f7;
}
.user-text{
    padding-right: 10rpx;
}
.login-quick{
    display: flex;
    padding: 20rpx 0;
    justify-content: space-between;
}
.reminder{
    color: #ccc;
    font-size: 32rpx;
    padding: 20rpx 0;
    text-align: center;
}
</style>

代码 my.vue页面

设置功能里面有退出按钮,通过调用state中的outLogin方法来实现退出功能

<template>
    <view class="my">
        <!-- 头部 -->
        <view class="my-header">
            <view class="header-main">
                <view class="header-config" @tap="goConfig">
                    <image class="config-img" src="../../static/tabbar/list.png" mode=""></image>
                </view>
                <view class="header-logo">
                    <image class="logo-img" :src="loginStatus ? userInfo.imgUrl :'../../static/tabbar/mySelected.png'" mode=""></image>
                    <view class="logo-name" @tap="goLogin">{{loginStatus ? userInfo.nickName : '用户昵称'}}</view>
                </view>
            </view>
        </view>
        <!-- 我的订单 -->
        <view class="order">
            <view class="order-title" @tap="goOrder">
                <view class="">我的订单</view>
                <view class="">全部订单 > </view>
            </view>
            <view class="order-list">
                <view class="order-item">
                    <image class="order-img" src="../../static/logo.png" mode=""></image>
                    <view class="">待付款</view>
                </view>
                <view class="order-item">
                    <image class="order-img" src="../../static/logo.png" mode=""></image>
                    <view class="">待付款</view>
                </view>
                <view class="order-item">
                    <image class="order-img" src="../../static/logo.png" mode=""></image>
                    <view class="">待付款</view>
                </view>
                <view class="order-item">
                    <image class="order-img" src="../../static/logo.png" mode=""></image>
                    <view class="">待付款</view>
                </view>
                <view class="order-item">
                    <image class="order-img" src="../../static/logo.png" mode=""></image>
                    <view class="" @tap="qianzi">待付款1</view>
                </view>
            </view>
            <!-- 内容列表 -->
            <view class="my-content">
                <view class="my-content-item" v-for="(item,index) in 6">
                    <view class="">
                        我的收藏
                    </view>
                    <view class="">
                        >
                    </view>
                </view>
            </view>
        </view>
    </view>
</template>

<script>
    import {mapState} from 'vuex'
    export default {
        data() {
            return {
                
            }
        },
        computed:{
            ...mapState({
                loginStatus:state=>state.user.loginStatus,
                userInfo:state=>state.user.userInfo
            })
        },
        methods: {
            goConfig(){
                uni.navigateTo({
                    url:'../my-config/my-config'
                })
            },
            goOrder(){
                uni.navigateTo({
                    url:'../my-order/my-order'
                })
            },
            qianzi(){
                uni.navigateTo({
                    url:'../qianzi/qianzi'
                })
            },
            goLogin(){
                uni.navigateTo({
                    url:'/pages/login/login'
                })
            }
        }
    }
</script>

<style lang="scss">
.my-header{
    background-color: #eee;
    width: 100%;
    height: 400rpx;
}
.header-main{
    position:relative;
    top: 120rpx;
}
.header-config{
    position: absolute;
    left: 20rpx;
}
.header-logo{
    position: absolute;
    left:50%;
    margin-left:-60rpx;
    width: 120rpx;
}
.config-img{
    width: 40rpx;
    height: 40rpx;
}
.logo-img{
    width: 120rpx;
    height: 120rpx;
    border:2rpx solid #ccc;
    border-radius: 50%;
    background-color: #FFFFFF;
}
.logo-name{
    color: #FFFFFF;
    font-size: 30rpx;
    font-weight: bold;
    text-align: center;
}
.order-title{
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 20rpx;
}
.order-list{
    padding:20rpx;
    display: flex;
}
.order-item{
    flex:1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
.order-img{
    width: 80rpx;
    height: 80rpx;
}
.my-content{
    margin:20rpx 0;
    padding: 0 20rpx;
}
.my-content-item{
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 20rpx 0;
    border-bottom: 2px solid #ccc;
}
</style>

代码 my-config.vue页面

退出功能具体实现

<template>
    <view class="my-config">
        
        <view class="config-item" @tap="goPathList">
            <view class="">地址管理</view>
            <view class=""> > </view>
        </view>        
        
        <view class="config-item" v-for="(item,index) in 5">
            <view class="">地址管理</view>
            <view class=""> > </view>
        </view>
        
        <view class="my-exit" @tap="outLogin">
            退出
        </view>
        
    </view>
</template>

<script>
    import {mapMutations} from 'vuex'
    export default {
        data() {
            return {
                
            };
        },
        methods:{
            ...mapMutations(['loginOut']),
            goPathList(){
                uni.navigateTo({
                    url:'../my-path-list/my-path-list'
                })
            },
            outLogin(){
                uni.showToast({
                    title:"退出成功!",
                    icon:'none'
                });
                this.loginOut();
                uni.switchTab({
                    url:"/pages/index/index"
                })
            }
        }
    }
</script>

<style lang="scss">
.config-item{
    display: flex;
    justify-content: space-between;
    padding: 20rpx;
    border-bottom: 2rpx solid #ccc;
}
.my-exit{
    background-color: #49bdfb;
    width: 100%;
    line-height: 80rpx;
    color: #FFFFFF;
    text-align: center;
}
</style>

代码 /store/modules/user.js

user用到的state

export default{
    state:{
        // 登录状态
        loginStatus:false,
        // token
        token:null,
        // 用户信息 昵称头像等
        userInfo:{}
    },
    getters:{

    },
    mutations:{
        // 一旦进入app,就需要执行这个方法把用户信息读出来,放到App.vue的onLaunch中触发
        initUser(state){
            let userInfo = uni.getStorageSync('userInfo');
            if( userInfo ){
                userInfo = JSON.parse(userInfo);
                state.userInfo = userInfo;
                state.loginStatus = true;
                state.token = userInfo.token;
            }
        },
        
        // 登录后保存用户信息
        login(state,userInfo){
            state.userInfo = userInfo;
            state.loginStatus = true;
            state.token = userInfo.token;
            
            // 持久化存储 -- 把对象转换成字符串
            uni.setStorageSync('userInfo',JSON.stringify(userInfo));
        },
        
        // 退出登录
        loginOut(state){
            state.userInfo = {};
            state.loginStatus = false;
            state.token = null;
            // 删除本地持久化存储
            uni.removeStorageSync('userInfo');
        }
    },
    actions:{
        
    }
}

代码 /store/index.js

将 user.js引入到 state

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

Vue.use(Vuex);

// 购物车
import cart from './modules/cart.js'
// 地址管理
import path from './modules/path.js'
// 用户
import user from './modules/user.js'

export default new Vuex.Store({
    modules:{
        cart,
        path,
        user
    }
})

代码 /App.js

进入APP持久化数据获取

<script>
	export default {
		onLaunch: function() {
            // 打开APP自动触发的事件
            this.$store.commit('initUser');
			console.log('App Launch')
		},
		onShow: function() {
			console.log('App Show')
		},
		onHide: function() {
			console.log('App Hide')
		}
	}
</script>

<style>
	/*每个页面公共css */
    @import '@/common/uni.css';
    @import '@/common/common.css';
    @import '@/static/iconfont/iconfont.css';
</style>

目录结构

前端目录结构
  • manifest.json 配置文件: appid、logo…

  • pages.json 配置文件: 导航、 tabbar、 路由

  • main.js vue初始化入口文件

  • App.vue 全局配置:样式、全局监视

  • static 静态资源:图片、字体图标

  • page 页面

    • index
      • index.vue
    • list
      • list.vue
    • my
      • my.vue
    • my-config
      • my-config.vue
    • my-config
      • my-config.vue
    • my-add-path
      • my-add-path.vue
    • my-path-list
      • my-path-list.vue
    • search
      • search.vue
    • search-list
      • search-list.vue
    • shopcart
      • shopcart.vue
    • details
      • details.vue
    • my-order
      • my-order.vue
    • confirm-order
      • confirm-order.vue
    • payment
      • payment.vue
    • payment-success
      • payment-success.vue
    • login
      • login.vue
    • login-tel
      • login-tel.vue
    • login-code
      • login-code.vue
  • components 组件

    • index
      • Banner.vue
      • Hot.vue
      • Icons.vue
      • indexSwiper.vue
      • Recommend.vue
      • Shop.vue
    • common
      • Card.vue
      • Commondity.vue
      • CommondityList.vue
      • Line.vue
      • ShopList.vue
    • order
      • order-list.vue
    • uni
      • uni-number-box
        • uni-number-box.vue
      • uni-icons
        • uni-icons.vue
      • uni-nav-bar
        • uni-nav-bar.vue
      • mpvue-citypicker
        • mpvueCityPicker.vue
  • common 公共文件:全局css文件 || 全局js文件

    • api
      • request.js
    • common.css
    • uni.css
  • store vuex状态机文件

    • modules
      • cart.js
      • path.js
      • user.js
    • index.js
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值