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
- index
-
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
- uni-number-box
- index
-
common 公共文件:全局css文件 || 全局js文件
- api
- request.js
- common.css
- uni.css
- api
-
store vuex状态机文件
- modules
- cart.js
- path.js
- user.js
- index.js
- modules